@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
|
@@ -7,11 +7,21 @@ const FIELDS = Object.freeze({
|
|
|
7
7
|
status: [], open: [], snapshot: [], receipt: ['clientMessageId', 'requestHash'],
|
|
8
8
|
send: ['text', 'clientMessageId', 'attachments'], think: ['text', 'clientMessageId', 'attachments'],
|
|
9
9
|
dream: ['clientMessageId'], cancel: ['episodeId'],
|
|
10
|
-
tasks: [], task_log: ['taskId', 'offset', 'maxBytes'], task_cancel: ['taskId'], agent_close: ['agentId'],
|
|
10
|
+
tasks: ['cursor', 'limit'], task_log: ['taskId', 'offset', 'maxBytes'], task_cancel: ['taskId'], agent_close: ['agentId'],
|
|
11
|
+
outputs: ['cursor', 'limit'], output_read: ['outputId', 'offset', 'maxBytes'],
|
|
11
12
|
messages: ['cursor', 'limit'], traces: ['cursor', 'limit'], turns: ['cursor', 'limit'],
|
|
12
13
|
inspect: ['section', 'cursor', 'limit'], search: ['query', 'cursor', 'limit'],
|
|
13
14
|
settings: ['name', 'autonomyEnabled', 'modelCandidates', 'defaultModel'],
|
|
14
15
|
});
|
|
16
|
+
const OUTPUT_ERRORS = Object.freeze({
|
|
17
|
+
not_found: 'Digital person output not found',
|
|
18
|
+
output_not_file: 'Only published file snapshots can be read; links are not fetched',
|
|
19
|
+
output_quota: 'Maximum 10 MiB per file, 100 MiB or 200 outputs per person',
|
|
20
|
+
output_path: 'Output must be a regular file strictly within workDir; symlinks are not permitted',
|
|
21
|
+
output_platform: 'File publication requires Linux with accessible procfs and no-follow directory descriptors; links and existing snapshots remain available',
|
|
22
|
+
invalid_request: 'Invalid digital person output request',
|
|
23
|
+
not_open: 'Open the digital person first',
|
|
24
|
+
});
|
|
15
25
|
|
|
16
26
|
/** Request-only relay: identity is supplied by the authenticated Server, never the browser.
|
|
17
27
|
* Person data stays on the Agent. No Person response is broadcast to other clients.
|
|
@@ -60,7 +70,7 @@ export function createPersonRelay({
|
|
|
60
70
|
}
|
|
61
71
|
const source = msg.payload;
|
|
62
72
|
if (source != null && (typeof source !== 'object' || Array.isArray(source))) {
|
|
63
|
-
await reply(client, envelope, { ok: false, error: 'Invalid digital person payload' });
|
|
73
|
+
await reply(client, envelope, { ok: false, errorCode: 'invalid_request', error: 'Invalid digital person payload' });
|
|
64
74
|
return true;
|
|
65
75
|
}
|
|
66
76
|
if (op === 'turns' && ((source?.cursor != null && (!Number.isSafeInteger(source.cursor) || source.cursor < 1))
|
|
@@ -68,13 +78,28 @@ export function createPersonRelay({
|
|
|
68
78
|
await reply(client, envelope, { ok: false, errorCode: 'invalid_request', error: 'Invalid digital person turn page' });
|
|
69
79
|
return true;
|
|
70
80
|
}
|
|
81
|
+
if (op === 'outputs' || op === 'output_read') {
|
|
82
|
+
const value = source ?? {};
|
|
83
|
+
const invalid = Object.keys(value).some(key => !FIELDS[op].includes(key))
|
|
84
|
+
|| (op === 'outputs' && ((value.cursor != null && (typeof value.cursor !== 'string' || !/^[1-9][0-9]{0,14}$/.test(value.cursor)))
|
|
85
|
+
|| (Object.hasOwn(value, 'limit') && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > 50))))
|
|
86
|
+
|| (op === 'output_read' && (typeof value.outputId !== 'string' || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value.outputId)
|
|
87
|
+
|| (Object.hasOwn(value, 'offset') && (!Number.isSafeInteger(value.offset) || value.offset < 0))
|
|
88
|
+
|| (Object.hasOwn(value, 'maxBytes') && (!Number.isInteger(value.maxBytes) || value.maxBytes < 1 || value.maxBytes > 65536))));
|
|
89
|
+
if (invalid) {
|
|
90
|
+
await reply(client, envelope, { ok: false, errorCode: 'invalid_request', error: 'Invalid digital person output request' });
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
71
94
|
if (['tasks', 'task_log', 'task_cancel', 'agent_close'].includes(op)) {
|
|
72
95
|
const value = source ?? {};
|
|
73
96
|
const idKey = op === 'agent_close' ? 'agentId' : 'taskId';
|
|
74
97
|
const invalid = Object.keys(value).some(key => !FIELDS[op].includes(key))
|
|
75
98
|
|| (op !== 'tasks' && (typeof value[idKey] !== 'string' || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(value[idKey])))
|
|
76
99
|
|| (Object.hasOwn(value, 'offset') && (!Number.isSafeInteger(value.offset) || value.offset < 0))
|
|
77
|
-
|| (Object.hasOwn(value, 'maxBytes') && (!Number.isInteger(value.maxBytes) || value.maxBytes < 1 || value.maxBytes > 65536))
|
|
100
|
+
|| (Object.hasOwn(value, 'maxBytes') && (!Number.isInteger(value.maxBytes) || value.maxBytes < 1 || value.maxBytes > 65536))
|
|
101
|
+
|| (Object.hasOwn(value, 'limit') && (!Number.isInteger(value.limit) || value.limit < 1 || value.limit > 100))
|
|
102
|
+
|| (value.cursor != null && (typeof value.cursor !== 'string' || !/^t1:[A-Za-z0-9_-]{1,2048}$/.test(value.cursor)));
|
|
78
103
|
if (invalid) {
|
|
79
104
|
await reply(client, envelope, { ok: false, errorCode: 'invalid_request', error: 'Invalid digital person task payload' });
|
|
80
105
|
return true;
|
|
@@ -145,8 +170,10 @@ export function createPersonRelay({
|
|
|
145
170
|
await reply(row.client, row.envelope, msg.ok === true
|
|
146
171
|
? { ok: true, data: msg.data }
|
|
147
172
|
: { ok: false,
|
|
148
|
-
errorCode: ['outcome_unknown', 'invalid_request', 'busy', 'unsupported', 'not_configured', 'not_open', 'stale', 'idempotency_conflict', 'attachment_expired', 'invalid_attachment', 'unsupported_attachment', 'attachment_limit', 'image_model', 'model_selection', 'not_found', 'task_scope_denied', 'task_control_unavailable'].includes(msg.errorCode) ? msg.errorCode : 'requestFailed',
|
|
149
|
-
error:
|
|
173
|
+
errorCode: ['outcome_unknown', 'invalid_request', 'busy', 'unsupported', 'not_configured', 'not_open', 'stale', 'idempotency_conflict', 'attachment_expired', 'invalid_attachment', 'unsupported_attachment', 'attachment_limit', 'image_model', 'model_selection', 'not_found', 'task_scope_denied', 'task_control_unavailable', 'output_path', 'output_platform', 'output_quota', 'output_not_file'].includes(msg.errorCode) ? msg.errorCode : 'requestFailed',
|
|
174
|
+
error: ['outputs', 'output_read'].includes(row.envelope.op)
|
|
175
|
+
? (Object.hasOwn(OUTPUT_ERRORS, msg.errorCode) ? OUTPUT_ERRORS[msg.errorCode] : 'Digital person output request failed; refresh before retrying')
|
|
176
|
+
: typeof msg.error === 'string' ? msg.error.slice(0, 500) : 'Digital person request failed' });
|
|
150
177
|
return true;
|
|
151
178
|
},
|
|
152
179
|
clearClient(client) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.616"}
|