summaryrefslogtreecommitdiff
path: root/spec/fake_ext.lua
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-08-18 14:18:14 -0600
committert <t@tjp.lol>2026-08-18 14:20:28 -0600
commitcc69a2e431779528578211a5cb3385bb23e076bf (patch)
tree8833171e25bb3dfb71fc4a2e0b8fe583782da764 /spec/fake_ext.lua
parent372ef8ff40991644ec2654c61328f31779f4ad21 (diff)
Persist and replay subagent progress across sessions
Record durable child-turn metadata, restore bounded progress cards during startup replay, and keep settled cards attached to transcript entries. Add workflow-local Lua profiles and expose discovered agents and workflows in the session header.
Diffstat (limited to 'spec/fake_ext.lua')
-rw-r--r--spec/fake_ext.lua82
1 files changed, 81 insertions, 1 deletions
diff --git a/spec/fake_ext.lua b/spec/fake_ext.lua
index dfdfdb3..5207e03 100644
--- a/spec/fake_ext.lua
+++ b/spec/fake_ext.lua
@@ -17,7 +17,8 @@
-- panto.agent{ config =, store =, session_id =, conversation = } -> agent
-- panto.file_system_jsonl_store{ dir = } / panto.null_store()
-- agent:conversation() / :session_id() / :tools() / :set_tools(decls) / :run_async(opts)
--- store:resolve(id) / :load(id)
+-- store:list() / :list_bounded(opts) / :resolve(id) / :load(id) /
+-- :load_messages(id, opts)
-- conv:messages() / :message_metadata(i) / :add_system_message(text, { metadata = })
-- job:next_event() / :result() / :request_cancel() / :close()
--
@@ -189,6 +190,10 @@ function conv_mt:messages()
return out
end
+function conv_mt:len()
+ return #self._messages
+end
+
function conv_mt:message_metadata(index)
local message = self._messages[tonumber(index) or 0]
if message == nil then
@@ -239,7 +244,75 @@ function store_mt:resolve(id)
return { id = id, message_count = #session, api_style = "messages" }
end
+function store_mt:list()
+ if self._harness.reject_unbounded_replay then error("unbounded list was used", 2) end
+ local ids = {}
+ for id in pairs(self._harness.sessions) do
+ ids[#ids + 1] = id
+ end
+ table.sort(ids)
+ local out = {}
+ for _, id in ipairs(ids) do
+ out[#out + 1] = {
+ id = id,
+ created = "",
+ modified = "",
+ message_count = #self._harness.sessions[id],
+ model = "",
+ }
+ end
+ return out
+end
+
+function store_mt:list_bounded(opts)
+ local limit = type(opts) == "table" and math.max(0, math.floor(tonumber(opts.limit) or 0)) or 0
+ self._harness.bounded_list_calls = self._harness.bounded_list_calls + 1
+ local ids = {}
+ for id in pairs(self._harness.sessions) do ids[#ids + 1] = id end
+ table.sort(ids)
+ local out = {}
+ for index = 1, math.min(limit, #ids) do
+ local id = ids[index]
+ out[#out + 1] = {
+ id = id,
+ created = "",
+ modified = "",
+ message_count = #self._harness.sessions[id],
+ model = "",
+ }
+ end
+ return out
+end
+
+local function metadata_has_key(metadata, key)
+ return type(metadata) == "table" and metadata[key] ~= nil
+end
+
+function store_mt:load_messages(id, opts)
+ self._harness.bounded_load_calls = self._harness.bounded_load_calls + 1
+ local source = self._harness.sessions[id]
+ if type(source) ~= "table" then return nil end
+ opts = type(opts) == "table" and opts or {}
+ local selected = {}
+ for index, message in ipairs(source) do
+ local role_ok = opts.role == nil or message.role == opts.role
+ local metadata_ok = opts.metadata_key == nil or metadata_has_key(message.metadata, opts.metadata_key)
+ if role_ok and metadata_ok then selected[#selected + 1] = index end
+ end
+ local indices = {}
+ if opts.from_end then
+ local first = math.max(1, #selected - (tonumber(opts.limit) or 0) + 1)
+ for index = first, #selected do indices[#indices + 1] = selected[index] end
+ else
+ for index = 1, math.min(#selected, tonumber(opts.limit) or 0) do indices[#indices + 1] = selected[index] end
+ end
+ local messages = {}
+ for _, index in ipairs(indices) do messages[#messages + 1] = source[index] end
+ return new_conversation(messages)
+end
+
function store_mt:load(id)
+ if self._harness.reject_unbounded_replay then error("unbounded load was used", 2) end
local session = self._harness.sessions[id]
if session == nil then
return nil
@@ -415,6 +488,11 @@ function agent_mt:conversation()
return self._conv
end
+function agent_mt:set_message_metadata(index, metadata)
+ self._record.final_metadata = metadata
+ return true
+end
+
function agent_mt:session_id()
return child_id(self._harness, self._record)
end
@@ -538,6 +616,8 @@ function M.install(opts)
polls = 0,
live = 0,
max_live = 0,
+ bounded_list_calls = 0,
+ bounded_load_calls = 0,
}
function handle.queue(outcome)