Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ new version heading in the same commit.

## [Unreleased]

## [0.222.0] — 2026-07-16
## [0.222.1] — 2026-07-16
### Fixed
- **Session trail: `task_dispatch` no longer mis-reads as "deleted".** The `task.dispatched` audit event
keys the task id under `data.task` (not `data.id`), so the activity classifier resolved an empty id and
the trail showed the dispatch as `deleted` with a blank summary. It now reads `data.task` (falling back
to `data.id`), so a dispatch shows the task title + its live status; the endpoint also guards
empty-id targets so a missing id yields no status rather than a misleading "deleted". Follow-up to the
v0.221.0 session activity trail.
### Added
- **Chat renders KB pages and hosted apps inline too.** Extends the inline-deliverable cards (v0.220.0)
beyond Library artifacts: when an agent writes a Knowledge Base page (`kb_write`) or builds/changes a
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agent-os",
"version": "0.222.0",
"version": "0.222.1",
"description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.",
"license": "MIT",
"type": "commonjs",
Expand Down
6 changes: 6 additions & 0 deletions scripts/session-trail-test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const { createHttpServer } = require(path.join(ROOT, 'dist/server.js'));
.run(t++, SID, T, type, 'website-bot', JSON.stringify(data));
audit('task.created', { id: 'tk1', title: 'migrate the DB' });
audit('task.updated', { id: 'tk1', status: 'doing' });
audit('task.dispatched', { task: 'tk1', title: 'migrate the DB', agent: 'website-bot' }); // id lives in data.task
audit('task.dispatched', { title: 'orphan (no id)' }); // no task id → must NOT resolve to "deleted"
audit('secret.put', { key: 'STRIPE_KEY', principal: '*' });
audit('secret.requested', { key: 'OPENAI_KEY', mode: 'provide' });
audit('skill.proposed', { name: 'triage-flow', description: 'triage playbook' });
Expand Down Expand Up @@ -82,6 +84,10 @@ const { createHttpServer } = require(path.join(ROOT, 'dist/server.js'));
// (2) live status resolution
assert(by('task_create').status === 'doing' && by('task_create').statusTone === 'open', 'task → live status "doing" (open)', JSON.stringify(by('task_create')));
assert(by('task_update').status === 'doing', 'task_update entry also resolves to current "doing"');
// task.dispatched keys the id under data.task; an id-bearing dispatch resolves, an orphan stays blank
const disp = ev.filter((e) => e.primitive === 'task_dispatch');
assert(disp.some((e) => e.target && e.target.id === 'tk1' && e.status === 'doing'), 'task_dispatch (data.task=tk1) → resolves to "doing"', JSON.stringify(disp));
assert(disp.some((e) => !e.status && !(e.target && e.target.id)), 'task_dispatch with no id → no target, no misleading "deleted" status');
assert(by('secret_put').status === 'stored', 'secret → "stored" (exists in vault)', JSON.stringify(by('secret_put')));
assert(by('kb_write').status === 'rev 3' && by('kb_write').statusTone === 'muted', 'kb_write → current "rev 3"', JSON.stringify(by('kb_write')));
assert(by('secret_request').status === 'approved' && by('secret_request').statusTone === 'done', 'secret_request → card status "approved" (done)', JSON.stringify(by('secret_request')));
Expand Down
2 changes: 1 addition & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2392,7 +2392,7 @@ async function handle(os: AgentOS, tm: TerminalManager, autos: Automations, req:
return out;
};
for (const e of events) {
if (!e.target) continue;
if (!e.target || !e.target.id) continue; // no id to locate the object → nothing to resolve (not "deleted")
const r = resolveStatus(e.target);
if (r) { e.status = r.status; e.statusTone = r.tone; }
}
Expand Down
2 changes: 1 addition & 1 deletion src/state/session-activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export function classifyActivity(type: string, data: Record<string, unknown>): A
case 'task.claimed': return { category: 'tasks', primitive: 'task_claim', summary: id(), target: { kind: 'task', id: id() } };
case 'task.updated': return { category: 'tasks', primitive: 'task_update', summary: `${id()} → ${str(data.status)}`, target: { kind: 'task', id: id() } };
case 'task.completed': return { category: 'tasks', primitive: 'task_update', summary: `${id()} → ${str(data.status) || 'done'}`, effect: 'allow', target: { kind: 'task', id: id() } };
case 'task.dispatched': return { category: 'tasks', primitive: 'task_dispatch', summary: id(), target: { kind: 'task', id: id() } };
case 'task.dispatched': { const tid = str(data.task) || id(); return { category: 'tasks', primitive: 'task_dispatch', summary: clipText(data.title) || tid, target: tid ? { kind: 'task', id: tid } : undefined }; }
case 'task.deleted': return { category: 'tasks', primitive: 'task_delete', summary: id(), target: { kind: 'task', id: id() } };

// ── secrets vault (shared credential handoff) ──
Expand Down
Loading