@yeaft/webchat-agent 1.0.615 → 1.0.617

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.
@@ -8,10 +8,20 @@ const FIELDS = Object.freeze({
8
8
  send: ['text', 'clientMessageId', 'attachments'], think: ['text', 'clientMessageId', 'attachments'],
9
9
  dream: ['clientMessageId'], cancel: ['episodeId'],
10
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,6 +78,19 @@ 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';
@@ -147,8 +170,10 @@ export function createPersonRelay({
147
170
  await reply(row.client, row.envelope, msg.ok === true
148
171
  ? { ok: true, data: msg.data }
149
172
  : { ok: false,
150
- 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',
151
- error: typeof msg.error === 'string' ? msg.error.slice(0, 500) : 'Digital person request failed' });
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' });
152
177
  return true;
153
178
  },
154
179
  clearClient(client) {
@@ -1 +1 @@
1
- {"version":"1.0.615"}
1
+ {"version":"1.0.617"}