Class: Kward::SessionStore::Session
- Inherits:
-
Object
- Object
- Kward::SessionStore::Session
- 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
-
#created_at ⇒ Time
readonly
Creation timestamp used for sorting and filenames.
-
#cwd ⇒ String
readonly
Workspace directory recorded when the session was created.
-
#id ⇒ String
readonly
Stable persisted session id from the JSONL header.
-
#leaf_id ⇒ String?
Active tree leaf id used to restore the selected branch.
-
#modified_at ⇒ Time
readonly
Timestamp of the latest persisted conversation activity.
-
#name ⇒ String?
User-visible session name persisted as metadata records.
-
#parent_id ⇒ String?
readonly
Source session id when this session was cloned or forked.
-
#parent_path ⇒ String?
readonly
Source session path when this session was cloned or forked.
-
#path ⇒ String
readonly
Absolute JSONL session file path.
Instance Method Summary collapse
-
#append_branch_summary(parent_id, from_id:, summary:, details: {}) ⇒ Object
Adds a branch-summary node under
parent_idand selects it as the leaf. -
#append_label_change(entry_id, label) ⇒ Object
Persists a display label override for one tree entry.
-
#append_message(message) ⇒ Object
Persists a message as a tree entry and advances the session leaf.
-
#append_system_prompt_snapshot(system_message, reason: "changed") ⇒ Object
Persists the current system prompt as audit metadata when it changes.
-
#append_tool_execution(tool_call, content) ⇒ Object
Persists normalized tool execution metadata alongside transcript messages.
-
#attach(conversation) ⇒ Object
Installs persistence callbacks on
conversation. -
#branch(entry_id) ⇒ Object
Moves the active leaf to an existing entry so future messages fork there.
-
#compact(message) ⇒ Object
Persists a compaction summary entry and makes it the active leaf.
-
#delete_if_unused ⇒ Object
Removes this session file when it is still empty and unnamed.
-
#initialize(store:, id:, path:, cwd:, created_at:, name: nil, parent_id: nil, parent_path: nil, leaf_id: nil, modified_at: nil, system_prompt_hash: nil) ⇒ Session
constructor
Creates an object for JSONL session persistence.
-
#rename(name) ⇒ Object
Persists a user-visible session name without rewriting earlier records.
-
#reset_leaf ⇒ Object
Clears the active leaf so the next append starts a fresh root branch.
-
#update_memory_state(session_memories:, last_retrieval: nil) ⇒ Object
Persists the session memory snapshot used when the session is restored.
-
#update_runtime(provider: nil, model:, reasoning_effort:) ⇒ Object
Persists model/runtime metadata so restored sessions keep their context.
Constructor Details
#initialize(store:, id:, path:, cwd:, created_at:, name: nil, parent_id: nil, parent_path: nil, leaf_id: nil, modified_at: nil, system_prompt_hash: nil) ⇒ Session
Creates an object for JSONL session persistence.
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/kward/session_store.rb', line 65 def initialize(store:, id:, path:, cwd:, created_at:, name: nil, parent_id: nil, parent_path: nil, leaf_id: nil, modified_at: nil, system_prompt_hash: nil) @store = store @id = id @path = path @cwd = cwd @created_at = created_at @modified_at = modified_at || created_at @name = name @parent_id = parent_id @parent_path = parent_path @leaf_id = leaf_id @system_prompt_hash = system_prompt_hash end |
Instance Attribute Details
#created_at ⇒ Time (readonly)
Returns creation timestamp used for sorting and filenames.
52 53 54 |
# File 'lib/kward/session_store.rb', line 52 def created_at @created_at end |
#cwd ⇒ String (readonly)
Returns workspace directory recorded when the session was created.
50 51 52 |
# File 'lib/kward/session_store.rb', line 50 def cwd @cwd end |
#id ⇒ String (readonly)
Returns stable persisted session id from the JSONL header.
46 47 48 |
# File 'lib/kward/session_store.rb', line 46 def id @id end |
#leaf_id ⇒ String?
Returns active tree leaf id used to restore the selected branch.
62 63 64 |
# File 'lib/kward/session_store.rb', line 62 def leaf_id @leaf_id end |
#modified_at ⇒ Time (readonly)
Returns timestamp of the latest persisted conversation activity.
54 55 56 |
# File 'lib/kward/session_store.rb', line 54 def modified_at @modified_at end |
#name ⇒ String?
Returns user-visible session name persisted as metadata records.
60 61 62 |
# File 'lib/kward/session_store.rb', line 60 def name @name end |
#parent_id ⇒ String? (readonly)
Returns source session id when this session was cloned or forked.
56 57 58 |
# File 'lib/kward/session_store.rb', line 56 def parent_id @parent_id end |
#parent_path ⇒ String? (readonly)
Returns source session path when this session was cloned or forked.
58 59 60 |
# File 'lib/kward/session_store.rb', line 58 def parent_path @parent_path end |
#path ⇒ String (readonly)
Returns absolute JSONL session file path.
48 49 50 |
# File 'lib/kward/session_store.rb', line 48 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.
168 169 170 171 172 173 174 |
# File 'lib/kward/session_store.rb', line 168 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) mark_modified(record) record[:id] end |
#append_label_change(entry_id, label) ⇒ Object
Persists a display label override for one tree entry.
162 163 164 165 |
# File 'lib/kward/session_store.rb', line 162 def append_label_change(entry_id, label) record = @store.append_label_change(@path, entry_id, label) mark_modified(record) end |
#append_message(message) ⇒ Object
Persists a message as a tree entry and advances the session leaf.
95 96 97 98 99 100 |
# File 'lib/kward/session_store.rb', line 95 def () record = @store.build_tree_record(@path, "message", @leaf_id, message: ) @leaf_id = record[:id] @store.append_record(@path, record) mark_modified(record) end |
#append_system_prompt_snapshot(system_message, reason: "changed") ⇒ Object
Persists the current system prompt as audit metadata when it changes.
118 119 120 121 122 123 124 125 |
# File 'lib/kward/session_store.rb', line 118 def append_system_prompt_snapshot(, reason: "changed") @system_prompt_hash = @store.append_system_prompt_snapshot( @path, , reason: reason, latest_hash: @system_prompt_hash ) end |
#append_tool_execution(tool_call, content) ⇒ Object
Persists normalized tool execution metadata alongside transcript messages.
111 112 113 114 115 |
# File 'lib/kward/session_store.rb', line 111 def append_tool_execution(tool_call, content) record = RPC::ToolEventNormalizer.new(tool_call, content: content).execution_record @store.append_record(@path, record) mark_modified(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.
84 85 86 87 88 89 90 91 92 |
# File 'lib/kward/session_store.rb', line 84 def attach(conversation) conversation.on_append = lambda { || () } conversation.on_compact = lambda { || compact() } 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) } conversation. = lambda { || append_system_prompt_snapshot(, reason: "changed") } append_system_prompt_snapshot(conversation., reason: "attach") self end |
#branch(entry_id) ⇒ Object
Moves the active leaf to an existing entry so future messages fork there.
150 151 152 153 154 |
# File 'lib/kward/session_store.rb', line 150 def branch(entry_id) @leaf_id = entry_id.to_s.empty? ? nil : entry_id.to_s record = @store.append_leaf_change(@path, @leaf_id) mark_modified(record) end |
#compact(message) ⇒ Object
Persists a compaction summary entry and makes it the active leaf.
103 104 105 106 107 108 |
# File 'lib/kward/session_store.rb', line 103 def compact() record = @store.build_tree_record(@path, "compaction", @leaf_id, message: ) @leaf_id = record[:id] @store.append_record(@path, record) mark_modified(record) end |
#delete_if_unused ⇒ Object
Removes this session file when it is still empty and unnamed.
189 190 191 |
# File 'lib/kward/session_store.rb', line 189 def delete_if_unused @store.delete_unused_session(self) end |
#rename(name) ⇒ Object
Persists a user-visible session name without rewriting earlier records.
140 141 142 143 144 145 146 147 |
# File 'lib/kward/session_store.rb', line 140 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_leaf ⇒ Object
Clears the active leaf so the next append starts a fresh root branch.
157 158 159 |
# File 'lib/kward/session_store.rb', line 157 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.
128 129 130 131 132 133 134 135 136 137 |
# File 'lib/kward/session_store.rb', line 128 def update_memory_state(session_memories:, last_retrieval: nil) record = { type: "memory_state", timestamp: Time.now.utc.iso8601(3), sessionMemories: Array(session_memories), lastRetrieval: last_retrieval } @store.append_record(@path, record) mark_modified(record) end |
#update_runtime(provider: nil, model:, reasoning_effort:) ⇒ Object
Persists model/runtime metadata so restored sessions keep their context.
177 178 179 180 181 182 183 184 185 186 |
# File 'lib/kward/session_store.rb', line 177 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 |