@yeaft/webchat-agent 1.0.622 → 1.0.623
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 +10 -0
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +180 -123
- 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/contracts.js +2 -2
- package/yeaft/person/provider.js +1 -0
- package/yeaft/person/runtime.js +160 -50
- package/yeaft/person/service.js +22 -6
- package/yeaft/person/sqlite-repository.js +1 -0
- package/yeaft/person/sqlite-store.js +36 -13
- package/yeaft/person/task-host.js +20 -5
- package/yeaft/person/turn-diagnostics.js +7 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, realpathSync, readFileSync, writeFileSync, renameSync, readdirSync, existsSync, lstatSync } from 'node:fs';
|
|
1
|
+
import { mkdirSync, realpathSync, readFileSync, writeFileSync, renameSync, readdirSync, opendirSync, existsSync, lstatSync } from 'node:fs';
|
|
2
2
|
import { resolve, join, relative, isAbsolute, sep } from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { TaskManager } from '../tasks/manager.js';
|
|
@@ -96,8 +96,23 @@ function atomicJSON(path, value) {
|
|
|
96
96
|
renameSync(temporary, path);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
function jsonFiles(directory) {
|
|
99
|
+
function jsonFiles(directory, { limit } = {}) {
|
|
100
100
|
if (!existsSync(directory)) return [];
|
|
101
|
+
if (limit != null) {
|
|
102
|
+
// Main cognition reads a bounded latest window, not every durable result at once.
|
|
103
|
+
// Iterate directory entries without assembling an unbounded filename array.
|
|
104
|
+
const handle = opendirSync(directory), records = [];
|
|
105
|
+
const time = record => new Date(record.updatedAt ?? record.task?.updatedAt ?? record.createdAt ?? 0).getTime() || 0;
|
|
106
|
+
try {
|
|
107
|
+
for (let entry; (entry = handle.readSync());) {
|
|
108
|
+
if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
|
|
109
|
+
try { records.push(JSON.parse(readFileSync(join(directory, entry.name), 'utf8'))); } catch { continue; }
|
|
110
|
+
records.sort((a, b) => time(b) - time(a));
|
|
111
|
+
if (records.length > limit) records.pop();
|
|
112
|
+
}
|
|
113
|
+
} finally { handle.closeSync(); }
|
|
114
|
+
return records;
|
|
115
|
+
}
|
|
101
116
|
return readdirSync(directory).filter(name => name.endsWith('.json')).flatMap(name => {
|
|
102
117
|
try { return [JSON.parse(readFileSync(join(directory, name), 'utf8'))]; } catch { return []; }
|
|
103
118
|
});
|
|
@@ -562,9 +577,9 @@ class ScopedPersonTaskHost {
|
|
|
562
577
|
this.#persistAgents();
|
|
563
578
|
const recent = items => items.sort((a, b) => String(b.updatedAt ?? b.task?.updatedAt ?? '').localeCompare(String(a.updatedAt ?? a.task?.updatedAt ?? ''))).slice(0, limit);
|
|
564
579
|
return { namespace: this.scope.key, sessionId: this.sessionId,
|
|
565
|
-
tasks: recent(jsonFiles(this.#manager.store.sessionDir(this.sessionId))),
|
|
566
|
-
agents: clone([...this.#agents.values()].slice(-limit)), completions: recent(jsonFiles(join(this.dataRoot, 'completions'))),
|
|
567
|
-
toolResults: recent(jsonFiles(join(this.dataRoot, 'tool-results'))).map(result => ({ ...result,
|
|
580
|
+
tasks: recent(jsonFiles(this.#manager.store.sessionDir(this.sessionId), { limit })),
|
|
581
|
+
agents: clone([...this.#agents.values()].slice(-limit)), completions: recent(jsonFiles(join(this.dataRoot, 'completions'), { limit })),
|
|
582
|
+
toolResults: recent(jsonFiles(join(this.dataRoot, 'tool-results'), { limit })).map(result => ({ ...result,
|
|
568
583
|
output: result.output.slice(0, 8192), rawPath: join(this.dataRoot, 'tool-results', `${result.id}.json`) })) };
|
|
569
584
|
}
|
|
570
585
|
|
|
@@ -51,7 +51,7 @@ export function callUsage(raw, completed) {
|
|
|
51
51
|
return { ...usage, reportedCalls: Number(reported), missingCalls: Number(!reported), complete: completed && usage.totalTokens !== null };
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function sumUsage(calls, terminal) {
|
|
54
|
+
export function sumUsage(calls, terminal) {
|
|
55
55
|
const fields = [...TOKEN_FIELDS, 'inputTotalTokens', 'totalTokens'];
|
|
56
56
|
const usage = Object.fromEntries(fields.map(key => {
|
|
57
57
|
const known = calls.map(call => call.usage[key]).filter(value => value !== null);
|
|
@@ -84,7 +84,11 @@ export function turnView(episode, events) {
|
|
|
84
84
|
if (!call) continue;
|
|
85
85
|
if (event.kind === 'call_output' || event.kind === 'call_failed') {
|
|
86
86
|
call.status = event.kind === 'call_output' ? 'completed' : ['CANCELLED', 'INTERRUPTED'].includes(event.code) ? event.code.toLowerCase() : 'failed';
|
|
87
|
-
call.endedAt = event.createdAt;
|
|
87
|
+
call.endedAt = event.createdAt;
|
|
88
|
+
// Legacy call lifecycle timestamps include queue/pause time. Do not label
|
|
89
|
+
// them as provider duration when no actual dispatch measurement was stored.
|
|
90
|
+
call.elapsedMs = token(event.requestElapsedMs);
|
|
91
|
+
call.code = string(event.code);
|
|
88
92
|
call.effective.effort = event.effortObserved === true ? string(event.effectiveEffort) : null;
|
|
89
93
|
call.usage = callUsage(event.usage, event.kind === 'call_output');
|
|
90
94
|
} else if (event.kind === 'proposal_rejected') {
|
|
@@ -97,7 +101,7 @@ export function turnView(episode, events) {
|
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
const items = [...calls.values()].sort((a, b) => a.index - b.index);
|
|
100
|
-
return { id: episode.id, seq: episode.seq, kind: episode.kind, status: episode.status, createdAt: episode.createdAt,
|
|
104
|
+
return { id: episode.id, seq: episode.seq, kind: episode.kind, status: episode.status, controlStatus: episode.status === 'running' ? string(episode.controlStatus) : null, createdAt: episode.createdAt,
|
|
101
105
|
endedAt: episode.endedAt, terminalCode: episode.terminalCode, budget: { calls: token(episode.calls), timeoutMs: token(episode.timeoutMs) },
|
|
102
106
|
models: [...new Set(items.map(call => call.dispatched.model).filter(Boolean))], usage: sumUsage(items, episode.status !== 'running'), calls: items };
|
|
103
107
|
}
|