Class: Kward::SessionStore::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/session_store.rb

Overview

Live handle that attaches persistence callbacks to a conversation.

Once attached, every append/compact/tool execution writes a JSONL record and advances leaf_id for session tree navigation. Avoid mutating the attached conversation directly without these callbacks unless deliberately importing or reconstructing history.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(store:, id:, path:, cwd:, created_at:, name: nil, parent_id: nil, parent_path: nil, leaf_id: nil) ⇒ Session

Creates an object for JSONL session persistence.



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/kward/session_store.rb', line 58

def initialize(store:, id:, path:, cwd:, created_at:, name: nil, parent_id: nil, parent_path: nil, leaf_id: nil)
  @store = store
  @id = id
  @path = path
  @cwd = cwd
  @created_at = created_at
  @name = name
  @parent_id = parent_id
  @parent_path = parent_path
  @leaf_id = leaf_id
end

Instance Attribute Details

#created_atTime (readonly)

Returns creation timestamp used for sorting and filenames.

Returns:

  • (Time)

    creation timestamp used for sorting and filenames



47
48
49
# File 'lib/kward/session_store.rb', line 47

def created_at
  @created_at
end

#cwdString (readonly)

Returns workspace directory recorded when the session was created.

Returns:

  • (String)

    workspace directory recorded when the session was created



45
46
47
# File 'lib/kward/session_store.rb', line 45

def cwd
  @cwd
end

#idString (readonly)

Returns stable persisted session id from the JSONL header.

Returns:

  • (String)

    stable persisted session id from the JSONL header



41
42
43
# File 'lib/kward/session_store.rb', line 41

def id
  @id
end

#leaf_idString?

Returns active tree leaf id used to restore the selected branch.

Returns:

  • (String, nil)

    active tree leaf id used to restore the selected branch



55
56
57
# File 'lib/kward/session_store.rb', line 55

def leaf_id
  @leaf_id
end

#nameString?

Returns user-visible session name persisted as metadata records.

Returns:

  • (String, nil)

    user-visible session name persisted as metadata records



53
54
55
# File 'lib/kward/session_store.rb', line 53

def name
  @name
end

#parent_idString? (readonly)

Returns source session id when this session was cloned or forked.

Returns:

  • (String, nil)

    source session id when this session was cloned or forked



49
50
51
# File 'lib/kward/session_store.rb', line 49

def parent_id
  @parent_id
end

#parent_pathString? (readonly)

Returns source session path when this session was cloned or forked.

Returns:

  • (String, nil)

    source session path when this session was cloned or forked



51
52
53
# File 'lib/kward/session_store.rb', line 51

def parent_path
  @parent_path
end

#pathString (readonly)

Returns absolute JSONL session file path.

Returns:

  • (String)

    absolute JSONL session file path



43
44
45
# File 'lib/kward/session_store.rb', line 43

def path
  @path
end

Instance Method Details

#append_branch_summary(parent_id, from_id:, summary:, details: {}) ⇒ Object

Adds a branch-summary node under parent_id and selects it as the leaf.



139
140
141
142
143
144
# File 'lib/kward/session_store.rb', line 139

def append_branch_summary(parent_id, from_id:, summary:, details: {})
  record = @store.build_tree_record(@path, "branch_summary", parent_id, fromId: from_id, summary: summary, details: details || {})
  @leaf_id = record[:id]
  @store.append_record(@path, record)
  record[:id]
end

#append_label_change(entry_id, label) ⇒ Object

Persists a display label override for one tree entry.



134
135
136
# File 'lib/kward/session_store.rb', line 134

def append_label_change(entry_id, label)
  @store.append_label_change(@path, entry_id, label)
end

#append_message(message) ⇒ Object

Persists a message as a tree entry and advances the session leaf.



84
85
86
87
88
# File 'lib/kward/session_store.rb', line 84

def append_message(message)
  record = @store.build_tree_record(@path, "message", @leaf_id, message: message)
  @leaf_id = record[:id]
  @store.append_record(@path, record)
end

#append_tool_execution(tool_call, content) ⇒ Object

Persists normalized tool execution metadata alongside transcript messages.



98
99
100
# File 'lib/kward/session_store.rb', line 98

def append_tool_execution(tool_call, content)
  @store.append_record(@path, RPC::ToolEventNormalizer.new(tool_call, content: content).execution_record)
end

#attach(conversation) ⇒ Object

Installs persistence callbacks on conversation.

The callbacks are intentionally simple lambdas so Conversation remains storage-agnostic while SessionStore remains the only owner of JSONL record shape.



75
76
77
78
79
80
81
# File 'lib/kward/session_store.rb', line 75

def attach(conversation)
  conversation.on_append = lambda { |message| append_message(message) }
  conversation.on_compact = lambda { |message| compact(message) }
  conversation.on_tool_execution = lambda { |tool_call, content| append_tool_execution(tool_call, content) }
  conversation.on_runtime_update = lambda { |provider:, model:, reasoning_effort:| update_runtime(provider: provider, model: model, reasoning_effort: reasoning_effort) }
  self
end

#branch(entry_id) ⇒ Object

Moves the active leaf to an existing entry so future messages fork there.



123
124
125
126
# File 'lib/kward/session_store.rb', line 123

def branch(entry_id)
  @leaf_id = entry_id.to_s.empty? ? nil : entry_id.to_s
  @store.append_leaf_change(@path, @leaf_id)
end

#compact(message) ⇒ Object

Persists a compaction summary entry and makes it the active leaf.



91
92
93
94
95
# File 'lib/kward/session_store.rb', line 91

def compact(message)
  record = @store.build_tree_record(@path, "compaction", @leaf_id, message: message)
  @leaf_id = record[:id]
  @store.append_record(@path, record)
end

#delete_if_unusedObject

Removes this session file when it is still empty and unnamed.



159
160
161
# File 'lib/kward/session_store.rb', line 159

def delete_if_unused
  @store.delete_unused_session(self)
end

#rename(name) ⇒ Object

Persists a user-visible session name without rewriting earlier records.



113
114
115
116
117
118
119
120
# File 'lib/kward/session_store.rb', line 113

def rename(name)
  @name = name.to_s.strip.empty? ? nil : name.to_s.strip
  @store.append_record(@path, {
    type: "session_info",
    timestamp: Time.now.utc.iso8601(3),
    name: @name
  })
end

#reset_leafObject

Clears the active leaf so the next append starts a fresh root branch.



129
130
131
# File 'lib/kward/session_store.rb', line 129

def reset_leaf
  branch(nil)
end

#update_memory_state(session_memories:, last_retrieval: nil) ⇒ Object

Persists the session memory snapshot used when the session is restored.



103
104
105
106
107
108
109
110
# File 'lib/kward/session_store.rb', line 103

def update_memory_state(session_memories:, last_retrieval: nil)
  @store.append_record(@path, {
    type: "memory_state",
    timestamp: Time.now.utc.iso8601(3),
    sessionMemories: Array(session_memories),
    lastRetrieval: last_retrieval
  })
end

#update_runtime(provider: nil, model:, reasoning_effort:) ⇒ Object

Persists model/runtime metadata so restored sessions keep their context.



147
148
149
150
151
152
153
154
155
156
# File 'lib/kward/session_store.rb', line 147

def update_runtime(provider: nil, model:, reasoning_effort:)
  @store.append_record(@path, {
    type: "session_info",
    timestamp: Time.now.utc.iso8601(3),
    name: @name,
    provider: provider.to_s,
    model: model.to_s,
    reasoningEffort: reasoning_effort.to_s
  }.delete_if { |_key, value| value.to_s.empty? })
end