@yeaft/webchat-agent 1.0.599 → 1.0.601

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.
@@ -93,6 +93,8 @@ export class SqlitePersonRepository {
93
93
  async append(episode, kind, data) { return this.request('append', [episode, kind, data]); }
94
94
  async startCall(episode, data) { return this.request('startCall', [episode, data]); }
95
95
  async finalizeCall(episode, data) { return this.request('finalizeCall', [episode, data]); }
96
+ async startCapability(episode, data) { return this.request('startCapability', [episode, data]); }
97
+ async finalizeCapability(episode, data) { return this.request('finalizeCapability', [episode, data]); }
96
98
  async createdCapabilities(episode) { return this.request('createdCapabilities', [episode]); }
97
99
  async saveCreatedCapability(episode, input) { return this.request('saveCreatedCapability', [episode, validateCreatedCapability(input)]); }
98
100
  async episodeAttachments(episode) { return this.request('episodeAttachments', [episode]); }
@@ -104,7 +106,7 @@ export class SqlitePersonRepository {
104
106
  async settings(ownerId, settings) { return this.request('settings', [ownerId, settings]); }
105
107
  async list(ownerId, collection, options, filter) { return this.request('list', [ownerId, collection, options, filter]); }
106
108
  async snapshot(ownerId) { return this.request('snapshot', [ownerId]); }
107
- async inspect(ownerId, options) { return this.request('inspect', [ownerId, options]); }
109
+ async inspect(ownerId, options, nativeToolIds) { return this.request('inspect', [ownerId, options, nativeToolIds]); }
108
110
  async search(ownerId, options) { return this.request('search', [ownerId, options]); }
109
111
  /** Ascending durable journal, scoped before paging. Empty pages retain after. */
110
112
  async searchChanges(ownerId, options) { return this.request('searchChanges', [ownerId, options]); }
@@ -49,7 +49,7 @@ const publicOutput = (output, failed) => {
49
49
  : { text: output.text, bytes: retainedBytes, complete: true, usage, stopReason };
50
50
  };
51
51
  const READS = new Set(['receipt', 'inspect', 'search', 'getPerson', 'context', 'recall', 'list', 'searchChanges', 'resolveMemories', 'createdCapabilities', 'episodeAttachments']);
52
- const WRITES = new Set(['open', 'recover', 'admit', 'heartbeat', 'append', 'startCall', 'finalizeCall', 'commit', 'finish', 'cancel', 'settings', 'snapshot', 'saveCreatedCapability']);
52
+ const WRITES = new Set(['open', 'recover', 'admit', 'heartbeat', 'append', 'startCall', 'finalizeCall', 'startCapability', 'finalizeCapability', 'commit', 'finish', 'cancel', 'settings', 'snapshot', 'saveCreatedCapability']);
53
53
  const memoryKind = kind => { if (!['messages', 'concepts'].includes(kind)) fail('INVALID_REQUEST'); return kind; };
54
54
  const boundedLimit = (limit, max = 100) => { if (!Number.isSafeInteger(limit) || limit < 1 || limit > max) fail('INVALID_REQUEST'); return limit; };
55
55
  const sequence = value => {
@@ -256,7 +256,7 @@ export class SqlitePersonStore {
256
256
  }
257
257
  append(episode, kind, data) {
258
258
  // Call proof/publication events are emitted only by their transactional methods.
259
- if (['call_started', 'call_output', 'call_failed', 'capability_created'].includes(kind)) fail('INVALID_REQUEST');
259
+ if (['call_started', 'call_output', 'call_failed', 'capability_created', 'capability_finalized'].includes(kind)) fail('INVALID_REQUEST');
260
260
  const p = this.own(episode);
261
261
  const record = this.one('episodes', this.episodeScope(episode), ' AND id = ?', [episode.id]);
262
262
  if (!record || record.status !== 'running') fail('STALE');
@@ -268,7 +268,7 @@ export class SqlitePersonStore {
268
268
  startCall(episode, data) {
269
269
  const p = this.own(episode), scope = this.episodeScope(episode);
270
270
  const record = this.one('episodes', scope, ' AND id = ?', [episode.id]);
271
- if (!record || record.status !== 'running' || record.openCall) fail('STALE');
271
+ if (!record || record.status !== 'running' || record.openCall || record.openCapability) fail('STALE');
272
272
  p.writeSerial++; this.put('persons', p);
273
273
  record.openCall = { callId: data.callId, requested: data.requested, effective: data.effective, ...(data.manifest ? { manifest: data.manifest } : {}) };
274
274
  this.put('episodes', record);
@@ -292,6 +292,43 @@ export class SqlitePersonStore {
292
292
  });
293
293
  return live;
294
294
  }
295
+ startCapability(episode, data) {
296
+ const p = this.own(episode), scope = this.episodeScope(episode);
297
+ const record = this.one('episodes', scope, ' AND id = ?', [episode.id]);
298
+ const proof = this.one('traces', scope, " AND json_extract(record, '$.episodeId') = ? AND json_extract(record, '$.kind') = 'call_output' AND json_extract(record, '$.callId') = ? AND json_extract(record, '$.output.complete') = 1", [episode.id, data.callId]);
299
+ if (!record || record.status !== 'running' || record.openCall || record.openCapability || !proof) fail('STALE');
300
+ const prior = this.one('traces', scope, " AND json_extract(record, '$.episodeId') = ? AND json_extract(record, '$.kind') = 'capability_started' AND json_extract(record, '$.callId') = ?", [episode.id, data.callId]);
301
+ if (prior) fail('STALE');
302
+ record.openCapability = { ...data, invocationHash: digest(data.capability) };
303
+ this.put('episodes', record);
304
+ p.writeSerial++; this.put('persons', p);
305
+ return this.trace(episode.ownerId, episode.id, 'capability_started', data);
306
+ }
307
+ /** Original started invocation only; consuming this slot never restores cognitive authority. */
308
+ finalizeCapability(episode, { callId, capability, result, code = null, terminalCode = null }) {
309
+ const scope = this.episodeScope(episode), record = this.one('episodes', scope, ' AND id = ?', [episode.id]);
310
+ if (!record || record.workerId !== episode.workerId || record.epoch !== episode.epoch ||
311
+ record.baseStateVersion !== episode.baseStateVersion || record.controlVersion !== episode.controlVersion || record.inputWatermark !== episode.inputWatermark ||
312
+ record.openCapability?.callId !== callId || record.openCapability.invocationHash !== digest(capability) ||
313
+ !['running', 'cancelled', 'interrupted', 'failed'].includes(record.status)) return false;
314
+ const p = record.status === 'running' && this.fenced(episode);
315
+ const live = Boolean(p && !terminalCode);
316
+ const started = record.openCapability;
317
+ const failed = Boolean(code || result?.ok === false);
318
+ const outcome = failed ? 'capability_failed' : 'capability_result';
319
+ const data = { callId, ...(started.capabilityManifest ? { capabilityManifest: started.capabilityManifest } : {}),
320
+ ...(failed ? { capabilityId: started.capability.id, code: code || result.code } : { capability: started.capability }),
321
+ ...(result !== undefined ? { result } : {}) };
322
+ delete record.openCapability; this.put('episodes', record);
323
+ if (live) {
324
+ const experience = recordCapabilityExperience(p.capabilityExperience, record, outcome, data, this.now.toISOString());
325
+ if (experience) { p.capabilityExperience = experience; this.put('persons', p); }
326
+ }
327
+ this.trace(episode.ownerId, episode.id, live ? outcome : 'capability_finalized', { ...data,
328
+ ...(!live ? { capability: started.capability, workerId: record.workerId, outcome, afterTerminal: true, accepted: false,
329
+ terminalCode: terminalCode || record.terminalCode || 'STALE' } : {}) });
330
+ return live;
331
+ }
295
332
  createdCapabilities(episode) {
296
333
  this.own(episode);
297
334
  return this.rows('created_capabilities', this.episodeScope(episode), ' ORDER BY id ASC LIMIT 32').map(publicDoc);
@@ -392,11 +429,11 @@ export class SqlitePersonStore {
392
429
  if (Object.keys(settings).length) this.trace(ownerId, null, 'settings', { settings: settingsView(p.settings), ...(name !== undefined ? { name: p.name } : {}) });
393
430
  return { settings: settingsView(p.settings), person: this.personView(p) };
394
431
  }
395
- inspect(ownerId, options) {
432
+ inspect(ownerId, options, nativeToolIds) {
396
433
  const { section, cursor, limit } = inspectRequest(options);
397
434
  this.getPerson(ownerId);
398
435
  const scope = this.scope(ownerId);
399
- if (section === 'skills') return inspectCapabilities(this.rows('created_capabilities', scope, ' ORDER BY id ASC LIMIT 32'), { cursor, limit });
436
+ if (section === 'skills') return inspectCapabilities(this.rows('created_capabilities', scope, ' ORDER BY id ASC LIMIT 32'), { cursor, limit }, nativeToolIds);
400
437
  const records = this.rows('concepts', scope, ' AND (? IS NULL OR id > ?) ORDER BY id ASC LIMIT ?', [cursor, cursor, limit + 1]);
401
438
  return inspectionPage(records, limit, 'id', conceptView);
402
439
  }