@yeaft/webchat-agent 1.0.613 → 1.0.616
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/local-runtime/server/handlers/client-person.js +32 -5
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +301 -191
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/person/bridge.js +5 -1
- package/yeaft/person/capabilities.js +21 -7
- package/yeaft/person/contracts.js +34 -3
- package/yeaft/person/inspection.js +30 -4
- package/yeaft/person/outputs.js +153 -0
- package/yeaft/person/runtime.js +2 -2
- package/yeaft/person/service.js +6 -1
- package/yeaft/person/sqlite-repository.js +4 -0
- package/yeaft/person/sqlite-schema.js +16 -0
- package/yeaft/person/sqlite-store.js +72 -7
- package/yeaft/person/task-host.js +48 -6
|
@@ -13,7 +13,8 @@ import { isTerminalAgentStatus } from '../sub-agent/status.js';
|
|
|
13
13
|
import { describeAgentOutcome } from '../sub-agent/outcome.js';
|
|
14
14
|
import { NullTrace } from '../debug-trace.js';
|
|
15
15
|
import { loadConfig } from '../config.js';
|
|
16
|
-
import { digest, fail, PERSON_TASK_LIMITS, personTaskRequest } from './contracts.js';
|
|
16
|
+
import { digest, fail, PERSON_TASK_LIMITS, personTaskRequest, taskInspectionCursor } from './contracts.js';
|
|
17
|
+
import { inspectionPage, inspectionTime } from './inspection.js';
|
|
17
18
|
|
|
18
19
|
// Deliberately not the full Session registry: no transcript search, routing,
|
|
19
20
|
// interactive UI, MCP, Work Center, or nested orchestration authority.
|
|
@@ -77,13 +78,13 @@ function projectedTask(task) {
|
|
|
77
78
|
sourceEpisodeId: task.source?.episodeId ?? null, agentId: task.runtime?.subAgentId ?? null,
|
|
78
79
|
recoveryStatus: task.status === 'orphaned' ? 'orphaned' : null };
|
|
79
80
|
}
|
|
80
|
-
function projectedAgent(agent, executionPending) {
|
|
81
|
+
function projectedAgent(agent, executionPending, taskId) {
|
|
81
82
|
const usage = Object.fromEntries(['tokens', 'turns', 'startedAt'].map(key => [key,
|
|
82
83
|
Number.isFinite(agent.usage?.[key]) && agent.usage[key] >= 0 ? agent.usage[key] : 0]));
|
|
83
84
|
// Only the helper's fixed outcome vocabulary crosses this boundary, never the
|
|
84
85
|
// native result, budget reason, final report, or partial output.
|
|
85
86
|
const { status, complete, reason, truncated } = describeAgentOutcome(agent);
|
|
86
|
-
return { id: agent.id, name: summary(agent.name, 160), status: agent.status,
|
|
87
|
+
return { id: agent.id, taskId, name: summary(agent.name, 160), status: agent.status,
|
|
87
88
|
outcome: { status, complete, reason, truncated }, executionPending,
|
|
88
89
|
mission: summary(agent.mission ?? agent.task, 1024), usage, createdAt: agent.createdAt ?? null,
|
|
89
90
|
sourceEpisodeId: agent.personEpisodeId ?? null, recoveryStatus: agent.recoveryStatus === 'orphaned' ? 'orphaned' : null };
|
|
@@ -440,6 +441,17 @@ class ScopedPersonTaskHost {
|
|
|
440
441
|
return task;
|
|
441
442
|
}
|
|
442
443
|
|
|
444
|
+
#projectAgent(agent, executionPending) {
|
|
445
|
+
// The persistent link is independent of either history page. Only canonical
|
|
446
|
+
// IDs of this Person's actual child tasks may become owner log controls.
|
|
447
|
+
const id = agent.taskId;
|
|
448
|
+
const task = typeof id === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(id)
|
|
449
|
+
? this.taskManager.getTask(this.sessionId, id, this.parentVpId) : null;
|
|
450
|
+
const taskId = task && task.id === id && task.sessionId === this.sessionId && task.kind === 'sub_agent'
|
|
451
|
+
&& task.runtime?.subAgentId === agent.id ? id : null;
|
|
452
|
+
return projectedAgent(agent, executionPending, taskId);
|
|
453
|
+
}
|
|
454
|
+
|
|
443
455
|
/** Owner-facing inspection/control: never attach a provider, schedule cognition
|
|
444
456
|
* or cancel unrelated work. Native snapshots remain private to evidence(). */
|
|
445
457
|
async request({ ownerId, personId, namespace = this.scope.namespace, op, payload = {} } = {}) {
|
|
@@ -448,16 +460,46 @@ class ScopedPersonTaskHost {
|
|
|
448
460
|
const args = personTaskRequest(op, payload);
|
|
449
461
|
switch (op) {
|
|
450
462
|
case 'tasks': {
|
|
463
|
+
const continuation = args.cursor ? taskInspectionCursor(args.cursor) : null;
|
|
464
|
+
if (continuation && continuation.scope !== this.scope.key) throw denied();
|
|
451
465
|
this.#persistAgents();
|
|
452
466
|
const tasks = jsonFiles(this.#manager.store.sessionDir(this.sessionId))
|
|
453
467
|
.filter(task => task.sessionId === this.sessionId && task.ownerVpId === this.parentVpId && ['shell', 'sub_agent'].includes(task.kind));
|
|
454
468
|
const agents = [...this.#agents.values()].filter(agent => agentBelongsToScope(agent, this.agentScope));
|
|
469
|
+
const byCreation = (a, b) => inspectionTime(b.createdAt) - inspectionTime(a.createdAt) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
470
|
+
if (args.limit !== undefined) {
|
|
471
|
+
const readPage = (items, key, view) => {
|
|
472
|
+
const boundary = continuation?.[key];
|
|
473
|
+
if (continuation && boundary === null) return { items: [], nextCursor: null };
|
|
474
|
+
const records = items.filter(item => !boundary || inspectionTime(item.createdAt) < boundary.time ||
|
|
475
|
+
(inspectionTime(item.createdAt) === boundary.time && item.id > boundary.id))
|
|
476
|
+
.sort(byCreation)
|
|
477
|
+
.slice(0, args.limit + 1);
|
|
478
|
+
return inspectionPage(records, args.limit, item => ({ time: inspectionTime(item.createdAt), id: item.id }), view);
|
|
479
|
+
};
|
|
480
|
+
const taskPage = readPage(tasks, 'tasks', projectedTask);
|
|
481
|
+
const agentPage = readPage(agents, 'agents', agent => this.#projectAgent(agent, agent.executionPending));
|
|
482
|
+
const more = taskPage.nextCursor !== null || agentPage.nextCursor !== null;
|
|
483
|
+
const nextCursor = more ? `t1:${Buffer.from(JSON.stringify({ scope: this.scope.key,
|
|
484
|
+
tasks: taskPage.nextCursor, agents: agentPage.nextCursor })).toString('base64url')}` : null;
|
|
485
|
+
// History stays strictly newest-created first. A separate, bounded
|
|
486
|
+
// control snapshot keeps older live shells and terminal children with
|
|
487
|
+
// detached tools reachable without scrolling through settled history.
|
|
488
|
+
const activePage = (items, isActive, view) => inspectionPage(items.filter(isActive).sort(byCreation),
|
|
489
|
+
PERSON_TASK_LIMITS.records, 'id', view);
|
|
490
|
+
const activeTasks = activePage(tasks, task => !isTerminalTaskStatus(task.status), projectedTask);
|
|
491
|
+
const activeAgents = activePage(agents, agent => !isTerminalAgentStatus(agent.status) || agent.executionPending,
|
|
492
|
+
agent => this.#projectAgent(agent, agent.executionPending));
|
|
493
|
+
return { tasks: taskPage.items, agents: agentPage.items, nextCursor, truncated: more,
|
|
494
|
+
active: { tasks: activeTasks.items, agents: activeAgents.items,
|
|
495
|
+
truncated: activeTasks.nextCursor !== null || activeAgents.nextCursor !== null } };
|
|
496
|
+
}
|
|
455
497
|
const recent = (items, settled) => items.sort((a, b) => Number(settled(a)) - Number(settled(b))
|
|
456
|
-
||
|
|
498
|
+
|| byCreation(a, b)).slice(0, PERSON_TASK_LIMITS.records);
|
|
457
499
|
// A terminal driver with actual tools still running is active work, not
|
|
458
500
|
// disposable history: keep its cleanup control ahead of settled records.
|
|
459
501
|
return { tasks: recent(tasks, task => isTerminalTaskStatus(task.status)).map(projectedTask),
|
|
460
|
-
agents: recent(agents, agent => isTerminalAgentStatus(agent.status) && !agent.executionPending).map(agent =>
|
|
502
|
+
agents: recent(agents, agent => isTerminalAgentStatus(agent.status) && !agent.executionPending).map(agent => this.#projectAgent(agent, agent.executionPending)),
|
|
461
503
|
truncated: tasks.length > PERSON_TASK_LIMITS.records || agents.length > PERSON_TASK_LIMITS.records };
|
|
462
504
|
}
|
|
463
505
|
case 'task_log': {
|
|
@@ -483,7 +525,7 @@ class ScopedPersonTaskHost {
|
|
|
483
525
|
parentEngineDeps: this.agentScope, taskManager: this.taskManager }));
|
|
484
526
|
if (!result.success) fail('TASK_CONTROL_UNAVAILABLE');
|
|
485
527
|
await this.#joinAgent(agent);
|
|
486
|
-
return { agent:
|
|
528
|
+
return { agent: this.#projectAgent(agent, this.#executionPending(agent)), pending: false };
|
|
487
529
|
}
|
|
488
530
|
default: fail('INVALID_REQUEST');
|
|
489
531
|
}
|