@undefineds.co/linx 0.2.13 → 0.2.15

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.
@@ -1,6 +1,8 @@
1
1
  import { setTimeout as delay } from 'node:timers/promises';
2
2
  import { getDefaultPodDataSession } from '../pod-data-session.js';
3
- import { AS_ACTOR, AS_ANNOUNCE, AS_OBJECT, buildApprovalDocumentUrl, DCT_CREATED, ODRL_ACTION, ODRL_POLICY, ODRL_TARGET, RDF_TYPE, UDFS_ACTION, UDFS_ACTOR, UDFS_ACTOR_ROLE, UDFS_APPROVAL_REQUEST, UDFS_ASSIGNED_TO, UDFS_AUDIT_ENTRY, UDFS_AUTONOMY_GRANT, UDFS_CONTEXT, UDFS_DECISION_BY, UDFS_DECISION_ROLE, UDFS_EFFECT, UDFS_ON_BEHALF_OF, UDFS_POLICY_VERSION, UDFS_REASON, UDFS_RESOLVED_AT, UDFS_REVOKED_AT, UDFS_RISK, UDFS_RISK_CEILING, UDFS_SESSION, UDFS_STATUS, UDFS_TOOL_CALL_ID, UDFS_TOOL_NAME, buildApprovalResourceUrl, buildAuditDocumentUrl, buildAuditResourceUrl, buildGrantResourceUrl, buildInboxResourceUrl, firstIri, firstLiteral, iri, listTurtleResources, listTurtleResourcesRecursive, literal, parseManagedTurtleBlocks, readTurtleResource, subjectIdFromResourceUrl, upsertManagedTurtleBlock, } from '../pi-adapter/pod-native.js';
3
+ import { AS, ODRL, UDFS } from '@undefineds.co/models/namespaces';
4
+ import { ApprovalVocab, AuditVocab, GrantVocab, InboxNotificationVocab } from '@undefineds.co/models/vocab/sidecar';
5
+ import { buildApprovalDocumentUrl, RDF_TYPE, buildApprovalResourceUrl, buildAuditDocumentUrl, buildAuditResourceUrl, buildGrantResourceUrl, buildInboxResourceUrl, firstIri, firstLiteral, iri, listTurtleResources, listTurtleResourcesRecursive, literal, parseManagedTurtleBlocks, readTurtleResource, subjectIdFromResourceUrl, upsertManagedTurtleBlock, } from '../pi-adapter/pod-native.js';
4
6
  const WATCH_CHAT_ID = 'linx-watch';
5
7
  const WATCH_AGENT_ID = 'linx-watch-assistant';
6
8
  const REMOTE_APPROVAL_POLICY_VERSION = 'linx-watch-remote-approval/v1';
@@ -45,6 +47,9 @@ function buildApprovalUri(webIdOrUri, approvalId) {
45
47
  function buildApprovalUriForDate(webIdOrUri, approvalId, createdAt) {
46
48
  return buildApprovalResourceUrl(webIdOrUri, approvalId, createdAt);
47
49
  }
50
+ function documentUrlFromResourceUri(resourceUri) {
51
+ return resourceUri.split('#', 1)[0] ?? resourceUri;
52
+ }
48
53
  function buildGrantUri(webIdOrUri, grantId) {
49
54
  return buildGrantResourceUrl(webIdOrUri, grantId);
50
55
  }
@@ -108,16 +113,6 @@ function buildRequestMessage(request) {
108
113
  }
109
114
  return request.message;
110
115
  }
111
- function buildRequestAuditContext(record, request) {
112
- return {
113
- kind: request.kind,
114
- message: buildRequestMessage(request),
115
- ...(request.kind === 'command-approval' && request.command ? { command: request.command } : {}),
116
- ...(request.kind === 'command-approval' && request.cwd ? { cwd: request.cwd } : {}),
117
- backend: record.backend,
118
- sessionId: record.id,
119
- };
120
- }
121
116
  function extractToolCallId(request) {
122
117
  if (!isRecord(request.raw)) {
123
118
  return crypto.randomUUID();
@@ -129,7 +124,7 @@ function extractToolCallId(request) {
129
124
  ?? crypto.randomUUID();
130
125
  }
131
126
  function encodeDecisionReason(decision, note) {
132
- return JSON.stringify({
127
+ return safeJsonStringify({
133
128
  decision,
134
129
  ...(note?.trim() ? { note: note.trim() } : {}),
135
130
  });
@@ -156,34 +151,25 @@ function parseDecisionReason(value) {
156
151
  return null;
157
152
  }
158
153
  }
159
- function parseRequestAuditContext(value) {
160
- if (typeof value !== 'string' || !value.trim()) {
161
- return null;
162
- }
154
+ async function warnOnly(runtime, task) {
163
155
  try {
164
- const parsed = JSON.parse(value);
165
- if (!isRecord(parsed)) {
166
- return null;
167
- }
168
- const kind = normalizeString(parsed.kind);
169
- const message = normalizeString(parsed.message);
170
- const sessionId = normalizeString(parsed.sessionId);
171
- if (!kind || !message || !sessionId) {
172
- return null;
156
+ await task();
157
+ }
158
+ catch (error) {
159
+ if (runtime.onWarning) {
160
+ runtime.onWarning(error);
161
+ return;
173
162
  }
174
- return {
175
- kind: kind,
176
- message,
177
- sessionId,
178
- ...(normalizeString(parsed.backend) ? { backend: normalizeString(parsed.backend) } : {}),
179
- ...(normalizeString(parsed.runtime) ? { runtime: normalizeString(parsed.runtime) } : {}),
180
- ...(normalizeString(parsed.toolName) ? { toolName: normalizeString(parsed.toolName) } : {}),
181
- ...(normalizeString(parsed.command) ? { command: normalizeString(parsed.command) } : {}),
182
- ...(normalizeString(parsed.cwd) ? { cwd: normalizeString(parsed.cwd) } : {}),
183
- };
163
+ const message = error instanceof Error ? error.message : String(error);
164
+ process.emitWarning(`LinX Pod sync failed: ${message}`);
165
+ }
166
+ }
167
+ function safeJsonStringify(value) {
168
+ try {
169
+ return JSON.stringify(value);
184
170
  }
185
171
  catch {
186
- return null;
172
+ return JSON.stringify({ error: 'unserializable_context' });
187
173
  }
188
174
  }
189
175
  function extractSessionId(sessionUri) {
@@ -206,35 +192,20 @@ function decisionFromApprovalRow(row) {
206
192
  }
207
193
  return 'accept';
208
194
  }
209
- function requestAuditForApproval(row, approvalUris, audits) {
210
- const uriSet = new Set(approvalUris);
211
- const matches = audits.filter((audit) => (audit.action === 'approval_requested'
212
- && ((audit.approval && uriSet.has(audit.approval))
213
- || (audit.session === row.session && audit.toolCallId === row.toolCallId))));
214
- matches.sort((left, right) => toIsoString(right.createdAt, '').localeCompare(toIsoString(left.createdAt, '')));
215
- return matches[0];
216
- }
217
- function normalizeApprovalSummary(row, audits) {
195
+ function normalizeApprovalSummary(row) {
218
196
  const createdAt = toIsoString(row.createdAt, new Date(0).toISOString());
219
- const approvalUris = [
220
- buildApprovalUriForDate(row.session, row.id, new Date(createdAt)),
221
- buildApprovalUri(row.session, row.id),
222
- ];
223
- const requestAudit = requestAuditForApproval(row, approvalUris, audits);
224
- const requestContext = parseRequestAuditContext(requestAudit?.context);
225
197
  const sessionUri = row.session;
226
198
  const decision = decisionFromApprovalRow(row);
227
199
  return {
228
200
  id: row.id,
201
+ ...(normalizeString(row.approvalUri) ? { approvalUri: normalizeString(row.approvalUri) } : {}),
229
202
  sessionId: extractSessionId(sessionUri),
230
203
  sessionUri,
231
204
  toolCallId: row.toolCallId,
232
205
  toolName: row.toolName,
233
206
  risk: normalizeString(row.risk) ?? 'medium',
234
207
  status: normalizeString(row.status) ?? 'pending',
235
- message: requestContext?.message ?? row.toolName,
236
- ...(requestContext?.command ? { command: requestContext.command } : {}),
237
- ...(requestContext?.cwd ? { cwd: requestContext.cwd } : {}),
208
+ message: formatApprovalMessage(row),
238
209
  ...(normalizeString(row.assignedTo) ? { assignedTo: normalizeString(row.assignedTo) } : {}),
239
210
  ...(normalizeString(row.decisionBy) ? { decisionBy: normalizeString(row.decisionBy) } : {}),
240
211
  ...(decision ? { decision } : {}),
@@ -242,6 +213,18 @@ function normalizeApprovalSummary(row, audits) {
242
213
  ...(row.resolvedAt ? { resolvedAt: toIsoString(row.resolvedAt, createdAt) } : {}),
243
214
  };
244
215
  }
216
+ function formatApprovalMessage(row) {
217
+ if (row.toolName === 'commandExecution') {
218
+ return 'Command execution approval';
219
+ }
220
+ if (row.toolName === 'fileChange') {
221
+ return 'File change approval';
222
+ }
223
+ if (row.toolName === 'permissionRequest') {
224
+ return 'Permission approval';
225
+ }
226
+ return row.toolName;
227
+ }
245
228
  function formatSummaryHeadline(summary) {
246
229
  return `${summary.id} | ${summary.status} | ${summary.risk} | session=${summary.sessionId}`;
247
230
  }
@@ -316,6 +299,7 @@ async function createRemoteApprovalClient(runtime) {
316
299
  function createNativeRemoteApprovalStore(webId, fetcher) {
317
300
  return {
318
301
  listApprovals: () => listApprovalRows(webId, fetcher),
302
+ findApproval: (id, options) => findApprovalRow(webId, fetcher, id, options),
319
303
  insertApproval: (row) => writeApprovalRow(webId, fetcher, row),
320
304
  async updateApproval(id, patch) {
321
305
  const existing = (await listApprovalRows(webId, fetcher)).find((row) => row.id === id);
@@ -331,6 +315,32 @@ function createNativeRemoteApprovalStore(webId, fetcher) {
331
315
  insertInboxNotification: (row) => writeInboxNotificationRow(webId, fetcher, row),
332
316
  };
333
317
  }
318
+ async function findApprovalRow(webId, fetcher, id, options = {}) {
319
+ if (options.resourceUri) {
320
+ return readApprovalRowFromResource(fetcher, options.resourceUri);
321
+ }
322
+ if (options.createdAt) {
323
+ const createdAt = new Date(toIsoString(options.createdAt, new Date().toISOString()));
324
+ return readApprovalRowFromResource(fetcher, buildApprovalResourceUrl(webId, id, createdAt));
325
+ }
326
+ return (await listApprovalRows(webId, fetcher)).find((row) => row.id === id) ?? null;
327
+ }
328
+ async function readApprovalRowFromResource(fetcher, resourceUri) {
329
+ const turtle = await readTurtleResource(fetcher, documentUrlFromResourceUri(resourceUri));
330
+ if (!turtle) {
331
+ return null;
332
+ }
333
+ for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, documentUrlFromResourceUri(resourceUri))) {
334
+ if (subject !== resourceUri) {
335
+ continue;
336
+ }
337
+ const row = approvalRowFromPredicates(subject, predicates);
338
+ if (row) {
339
+ return row;
340
+ }
341
+ }
342
+ return null;
343
+ }
334
344
  async function listApprovalRows(webId, fetcher) {
335
345
  const [currentUrls, legacyUrls] = await Promise.all([
336
346
  listTurtleResourcesRecursive(fetcher, `${getPodBaseUrl(webId)}/.data/approvals/`).catch(() => []),
@@ -357,22 +367,22 @@ async function writeApprovalRow(webId, fetcher, row) {
357
367
  await upsertManagedTurtleBlock(fetcher, documentUrl, {
358
368
  subject: subjectUrl,
359
369
  triples: [
360
- { predicate: RDF_TYPE, object: iri(UDFS_APPROVAL_REQUEST) },
361
- { predicate: UDFS_SESSION, object: iri(row.session) },
362
- { predicate: UDFS_TOOL_CALL_ID, object: literal(row.toolCallId) },
363
- { predicate: UDFS_TOOL_NAME, object: literal(row.toolName) },
364
- { predicate: ODRL_TARGET, object: iri(row.target) },
365
- { predicate: ODRL_ACTION, object: iri(row.action) },
366
- { predicate: UDFS_RISK, object: literal(row.risk) },
367
- { predicate: UDFS_STATUS, object: literal(row.status) },
368
- ...(row.assignedTo ? [{ predicate: UDFS_ASSIGNED_TO, object: iri(row.assignedTo) }] : []),
369
- ...(row.decisionBy ? [{ predicate: UDFS_DECISION_BY, object: iri(row.decisionBy) }] : []),
370
- ...(row.decisionRole ? [{ predicate: UDFS_DECISION_ROLE, object: literal(row.decisionRole) }] : []),
371
- ...(row.onBehalfOf ? [{ predicate: UDFS_ON_BEHALF_OF, object: iri(row.onBehalfOf) }] : []),
372
- ...(row.reason ? [{ predicate: UDFS_REASON, object: literal(row.reason) }] : []),
373
- ...(row.policyVersion ? [{ predicate: UDFS_POLICY_VERSION, object: literal(row.policyVersion) }] : []),
374
- { predicate: DCT_CREATED, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
375
- ...(row.resolvedAt ? [{ predicate: UDFS_RESOLVED_AT, object: literal(toIsoString(row.resolvedAt, new Date().toISOString())) }] : []),
370
+ { predicate: RDF_TYPE, object: iri(UDFS.ApprovalRequest) },
371
+ { predicate: ApprovalVocab.session, object: iri(row.session) },
372
+ { predicate: ApprovalVocab.toolCallId, object: literal(row.toolCallId) },
373
+ { predicate: ApprovalVocab.toolName, object: literal(row.toolName) },
374
+ { predicate: ApprovalVocab.target, object: iri(row.target) },
375
+ { predicate: ApprovalVocab.action, object: iri(row.action) },
376
+ { predicate: ApprovalVocab.risk, object: literal(row.risk) },
377
+ { predicate: ApprovalVocab.status, object: literal(row.status) },
378
+ ...(row.assignedTo ? [{ predicate: ApprovalVocab.assignedTo, object: iri(row.assignedTo) }] : []),
379
+ ...(row.decisionBy ? [{ predicate: ApprovalVocab.decisionBy, object: iri(row.decisionBy) }] : []),
380
+ ...(row.decisionRole ? [{ predicate: ApprovalVocab.decisionRole, object: literal(row.decisionRole) }] : []),
381
+ ...(row.onBehalfOf ? [{ predicate: ApprovalVocab.onBehalfOf, object: iri(row.onBehalfOf) }] : []),
382
+ ...(row.reason ? [{ predicate: ApprovalVocab.reason, object: literal(row.reason) }] : []),
383
+ ...(row.policyVersion ? [{ predicate: ApprovalVocab.policyVersion, object: literal(row.policyVersion) }] : []),
384
+ { predicate: ApprovalVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
385
+ ...(row.resolvedAt ? [{ predicate: ApprovalVocab.resolvedAt, object: literal(toIsoString(row.resolvedAt, new Date().toISOString())) }] : []),
376
386
  ],
377
387
  });
378
388
  }
@@ -398,17 +408,18 @@ async function writeAuditRow(webId, fetcher, row) {
398
408
  await upsertManagedTurtleBlock(fetcher, documentUrl, {
399
409
  subject: subjectUrl,
400
410
  triples: [
401
- { predicate: RDF_TYPE, object: iri(UDFS_AUDIT_ENTRY) },
402
- { predicate: UDFS_ACTION, object: literal(row.action) },
403
- { predicate: UDFS_ACTOR, object: iri(row.actor) },
404
- { predicate: UDFS_ACTOR_ROLE, object: literal(row.actorRole) },
405
- ...(row.onBehalfOf ? [{ predicate: UDFS_ON_BEHALF_OF, object: iri(row.onBehalfOf) }] : []),
406
- ...(row.session ? [{ predicate: UDFS_SESSION, object: iri(row.session) }] : []),
407
- ...(row.toolCallId ? [{ predicate: UDFS_TOOL_CALL_ID, object: literal(row.toolCallId) }] : []),
408
- ...(row.approval ? [{ predicate: 'https://undefineds.co/ns#approval', object: iri(row.approval) }] : []),
409
- ...(row.context ? [{ predicate: UDFS_CONTEXT, object: literal(row.context) }] : []),
410
- ...(row.policyVersion ? [{ predicate: UDFS_POLICY_VERSION, object: literal(row.policyVersion) }] : []),
411
- { predicate: DCT_CREATED, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
411
+ { predicate: RDF_TYPE, object: iri(UDFS.AuditEntry) },
412
+ { predicate: AuditVocab.action, object: literal(row.action) },
413
+ { predicate: AuditVocab.actor, object: iri(row.actor) },
414
+ { predicate: AuditVocab.actorRole, object: literal(row.actorRole) },
415
+ ...(row.onBehalfOf ? [{ predicate: AuditVocab.onBehalfOf, object: iri(row.onBehalfOf) }] : []),
416
+ ...(row.session ? [{ predicate: AuditVocab.session, object: iri(row.session) }] : []),
417
+ ...(row.entry ? [{ predicate: AuditVocab.entry, object: iri(row.entry) }] : []),
418
+ ...(row.toolCallId ? [{ predicate: AuditVocab.toolCallId, object: literal(row.toolCallId) }] : []),
419
+ ...(row.toolName ? [{ predicate: AuditVocab.toolName, object: literal(row.toolName) }] : []),
420
+ ...(row.approval ? [{ predicate: AuditVocab.approval, object: iri(row.approval) }] : []),
421
+ ...(row.policyVersion ? [{ predicate: AuditVocab.policyVersion, object: literal(row.policyVersion) }] : []),
422
+ { predicate: AuditVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
412
423
  ],
413
424
  });
414
425
  }
@@ -445,17 +456,17 @@ async function writeGrantRow(webId, fetcher, row) {
445
456
  await upsertManagedTurtleBlock(fetcher, documentUrl, {
446
457
  subject: subjectUrl,
447
458
  triples: [
448
- { predicate: RDF_TYPE, object: iri(ODRL_POLICY) },
449
- { predicate: RDF_TYPE, object: iri(UDFS_AUTONOMY_GRANT) },
450
- { predicate: ODRL_TARGET, object: iri(target) },
451
- { predicate: ODRL_ACTION, object: iri(action) },
452
- { predicate: UDFS_EFFECT, object: literal(effect) },
453
- ...(normalizeString(row.riskCeiling) ? [{ predicate: UDFS_RISK_CEILING, object: literal(normalizeString(row.riskCeiling)) }] : []),
454
- { predicate: UDFS_DECISION_BY, object: iri(decisionBy) },
455
- { predicate: UDFS_DECISION_ROLE, object: literal(decisionRole) },
456
- ...(normalizeString(row.onBehalfOf) ? [{ predicate: UDFS_ON_BEHALF_OF, object: iri(normalizeString(row.onBehalfOf)) }] : []),
457
- { predicate: DCT_CREATED, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
458
- ...(normalizeString(row.revokedAt) ? [{ predicate: UDFS_REVOKED_AT, object: literal(normalizeString(row.revokedAt)) }] : []),
459
+ { predicate: RDF_TYPE, object: iri(ODRL.Policy) },
460
+ { predicate: RDF_TYPE, object: iri(UDFS.AutonomyGrant) },
461
+ { predicate: GrantVocab.target, object: iri(target) },
462
+ { predicate: GrantVocab.action, object: iri(action) },
463
+ { predicate: GrantVocab.effect, object: literal(effect) },
464
+ ...(normalizeString(row.riskCeiling) ? [{ predicate: GrantVocab.riskCeiling, object: literal(normalizeString(row.riskCeiling)) }] : []),
465
+ { predicate: GrantVocab.decisionBy, object: iri(decisionBy) },
466
+ { predicate: GrantVocab.decisionRole, object: literal(decisionRole) },
467
+ ...(normalizeString(row.onBehalfOf) ? [{ predicate: GrantVocab.onBehalfOf, object: iri(normalizeString(row.onBehalfOf)) }] : []),
468
+ { predicate: GrantVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
469
+ ...(normalizeString(row.revokedAt) ? [{ predicate: GrantVocab.revokedAt, object: literal(normalizeString(row.revokedAt)) }] : []),
459
470
  ],
460
471
  });
461
472
  }
@@ -464,22 +475,22 @@ async function writeInboxNotificationRow(webId, fetcher, row) {
464
475
  await upsertManagedTurtleBlock(fetcher, url, {
465
476
  subject: url,
466
477
  triples: [
467
- { predicate: RDF_TYPE, object: iri(AS_ANNOUNCE) },
468
- ...(row.actor ? [{ predicate: AS_ACTOR, object: iri(row.actor) }] : []),
469
- { predicate: AS_OBJECT, object: iri(row.object) },
470
- { predicate: DCT_CREATED, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
478
+ { predicate: RDF_TYPE, object: iri(AS.Announce) },
479
+ ...(row.actor ? [{ predicate: InboxNotificationVocab.actor, object: iri(row.actor) }] : []),
480
+ { predicate: InboxNotificationVocab.object, object: iri(row.object) },
481
+ { predicate: InboxNotificationVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
471
482
  ],
472
483
  });
473
484
  }
474
485
  function approvalRowFromPredicates(url, predicates) {
475
- const session = firstIri(predicates, UDFS_SESSION);
476
- const toolCallId = firstLiteral(predicates, UDFS_TOOL_CALL_ID);
477
- const toolName = firstLiteral(predicates, UDFS_TOOL_NAME);
478
- const target = firstIri(predicates, ODRL_TARGET);
479
- const action = firstIri(predicates, ODRL_ACTION);
480
- const risk = firstLiteral(predicates, UDFS_RISK);
481
- const status = firstLiteral(predicates, UDFS_STATUS);
482
- const createdAt = firstLiteral(predicates, DCT_CREATED);
486
+ const session = firstIri(predicates, ApprovalVocab.session);
487
+ const toolCallId = firstLiteral(predicates, ApprovalVocab.toolCallId);
488
+ const toolName = firstLiteral(predicates, ApprovalVocab.toolName);
489
+ const target = firstIri(predicates, ApprovalVocab.target);
490
+ const action = firstIri(predicates, ApprovalVocab.action);
491
+ const risk = firstLiteral(predicates, ApprovalVocab.risk);
492
+ const status = firstLiteral(predicates, ApprovalVocab.status);
493
+ const createdAt = firstLiteral(predicates, ApprovalVocab.createdAt);
483
494
  if (!session || !toolCallId || !toolName || !target || !action || !risk || !status || !createdAt) {
484
495
  return null;
485
496
  }
@@ -492,21 +503,21 @@ function approvalRowFromPredicates(url, predicates) {
492
503
  action,
493
504
  risk,
494
505
  status,
495
- assignedTo: firstIri(predicates, UDFS_ASSIGNED_TO),
496
- decisionBy: firstIri(predicates, UDFS_DECISION_BY),
497
- decisionRole: firstLiteral(predicates, UDFS_DECISION_ROLE),
498
- onBehalfOf: firstIri(predicates, UDFS_ON_BEHALF_OF),
499
- reason: firstLiteral(predicates, UDFS_REASON),
500
- policyVersion: firstLiteral(predicates, UDFS_POLICY_VERSION),
506
+ assignedTo: firstIri(predicates, ApprovalVocab.assignedTo),
507
+ decisionBy: firstIri(predicates, ApprovalVocab.decisionBy),
508
+ decisionRole: firstLiteral(predicates, ApprovalVocab.decisionRole),
509
+ onBehalfOf: firstIri(predicates, ApprovalVocab.onBehalfOf),
510
+ reason: firstLiteral(predicates, ApprovalVocab.reason),
511
+ policyVersion: firstLiteral(predicates, ApprovalVocab.policyVersion),
501
512
  createdAt,
502
- resolvedAt: firstLiteral(predicates, UDFS_RESOLVED_AT),
513
+ resolvedAt: firstLiteral(predicates, ApprovalVocab.resolvedAt),
503
514
  };
504
515
  }
505
516
  function auditRowFromPredicates(url, predicates) {
506
- const action = firstLiteral(predicates, UDFS_ACTION);
507
- const actor = firstIri(predicates, UDFS_ACTOR);
508
- const actorRole = firstLiteral(predicates, UDFS_ACTOR_ROLE);
509
- const createdAt = firstLiteral(predicates, DCT_CREATED);
517
+ const action = firstLiteral(predicates, AuditVocab.action);
518
+ const actor = firstIri(predicates, AuditVocab.actor);
519
+ const actorRole = firstLiteral(predicates, AuditVocab.actorRole);
520
+ const createdAt = firstLiteral(predicates, AuditVocab.createdAt);
510
521
  if (!action || !actor || !actorRole || !createdAt) {
511
522
  return null;
512
523
  }
@@ -515,22 +526,23 @@ function auditRowFromPredicates(url, predicates) {
515
526
  action,
516
527
  actor,
517
528
  actorRole,
518
- onBehalfOf: firstIri(predicates, UDFS_ON_BEHALF_OF),
519
- session: firstIri(predicates, UDFS_SESSION),
520
- toolCallId: firstLiteral(predicates, UDFS_TOOL_CALL_ID),
521
- approval: firstIri(predicates, 'https://undefineds.co/ns#approval'),
522
- context: firstLiteral(predicates, UDFS_CONTEXT),
523
- policyVersion: firstLiteral(predicates, UDFS_POLICY_VERSION),
529
+ onBehalfOf: firstIri(predicates, AuditVocab.onBehalfOf),
530
+ session: firstIri(predicates, AuditVocab.session),
531
+ entry: firstIri(predicates, AuditVocab.entry),
532
+ toolCallId: firstLiteral(predicates, AuditVocab.toolCallId),
533
+ toolName: firstLiteral(predicates, AuditVocab.toolName),
534
+ approval: firstIri(predicates, AuditVocab.approval),
535
+ policyVersion: firstLiteral(predicates, AuditVocab.policyVersion),
524
536
  createdAt,
525
537
  };
526
538
  }
527
539
  function grantRowFromPredicates(url, predicates) {
528
- const target = firstIri(predicates, ODRL_TARGET);
529
- const action = firstIri(predicates, ODRL_ACTION);
530
- const effect = firstLiteral(predicates, UDFS_EFFECT);
531
- const decisionBy = firstIri(predicates, UDFS_DECISION_BY);
532
- const decisionRole = firstLiteral(predicates, UDFS_DECISION_ROLE);
533
- const createdAt = firstLiteral(predicates, DCT_CREATED);
540
+ const target = firstIri(predicates, GrantVocab.target);
541
+ const action = firstIri(predicates, GrantVocab.action);
542
+ const effect = firstLiteral(predicates, GrantVocab.effect);
543
+ const decisionBy = firstIri(predicates, GrantVocab.decisionBy);
544
+ const decisionRole = firstLiteral(predicates, GrantVocab.decisionRole);
545
+ const createdAt = firstLiteral(predicates, GrantVocab.createdAt);
534
546
  if (!target || !action || !effect || !decisionBy || !decisionRole || !createdAt) {
535
547
  return null;
536
548
  }
@@ -539,12 +551,12 @@ function grantRowFromPredicates(url, predicates) {
539
551
  target,
540
552
  action,
541
553
  effect,
542
- riskCeiling: firstLiteral(predicates, UDFS_RISK_CEILING),
554
+ riskCeiling: firstLiteral(predicates, GrantVocab.riskCeiling),
543
555
  decisionBy,
544
556
  decisionRole,
545
- onBehalfOf: firstIri(predicates, UDFS_ON_BEHALF_OF),
557
+ onBehalfOf: firstIri(predicates, GrantVocab.onBehalfOf),
546
558
  createdAt,
547
- revokedAt: firstLiteral(predicates, UDFS_REVOKED_AT),
559
+ revokedAt: firstLiteral(predicates, GrantVocab.revokedAt),
548
560
  };
549
561
  }
550
562
  export async function createRemoteWatchApproval(options) {
@@ -564,7 +576,7 @@ export async function createRemoteWatchApproval(options) {
564
576
  risk: buildRisk(options.request),
565
577
  ...(options.request.kind === 'command-approval' && options.request.command ? { command: options.request.command } : {}),
566
578
  ...(options.request.kind === 'command-approval' && options.request.cwd ? { cwd: options.request.cwd } : {}),
567
- context: buildRequestAuditContext(options.record, options.request),
579
+ entry: sessionUri,
568
580
  }),
569
581
  runtime: activeRuntime,
570
582
  });
@@ -586,14 +598,7 @@ export async function createRemoteApproval(options) {
586
598
  const assignedTo = subject.assignedTo ?? webId;
587
599
  const onBehalfOf = subject.onBehalfOf ?? webId;
588
600
  const policyVersion = subject.policyVersion ?? REMOTE_APPROVAL_POLICY_VERSION;
589
- const requestContext = request.context ?? {
590
- kind: request.kind,
591
- message: request.message,
592
- sessionId: extractSessionId(sessionUri),
593
- toolName: request.toolName,
594
- ...(request.command ? { command: request.command } : {}),
595
- ...(request.cwd ? { cwd: request.cwd } : {}),
596
- };
601
+ const requestEntry = request.entry ?? approvalUri;
597
602
  await store.insertApproval({
598
603
  id: approvalId,
599
604
  session: sessionUri,
@@ -607,27 +612,30 @@ export async function createRemoteApproval(options) {
607
612
  policyVersion,
608
613
  createdAt: now,
609
614
  });
610
- await store.insertAudit({
615
+ const requestAudit = {
611
616
  id: crypto.randomUUID(),
612
617
  action: 'approval_requested',
613
618
  actor: subject.actorUri,
614
619
  actorRole: 'secretary',
615
620
  onBehalfOf,
616
621
  session: sessionUri,
622
+ entry: requestEntry,
617
623
  toolCallId: request.toolCallId,
624
+ toolName: request.toolName,
618
625
  approval: approvalUri,
619
- context: JSON.stringify(requestContext),
620
626
  policyVersion,
621
627
  createdAt: now,
622
- });
623
- await store.insertInboxNotification({
628
+ };
629
+ await warnOnly(activeRuntime, () => store.insertAudit(requestAudit));
630
+ await warnOnly(activeRuntime, () => store.insertInboxNotification({
624
631
  id: crypto.randomUUID(),
625
632
  actor: subject.actorUri,
626
633
  object: approvalUri,
627
634
  createdAt: now,
628
- }).catch(() => undefined);
635
+ }));
629
636
  return normalizeApprovalSummary({
630
637
  id: approvalId,
638
+ approvalUri,
631
639
  session: sessionUri,
632
640
  toolCallId: request.toolCallId,
633
641
  toolName: request.toolName,
@@ -638,19 +646,7 @@ export async function createRemoteApproval(options) {
638
646
  assignedTo,
639
647
  policyVersion,
640
648
  createdAt: now,
641
- }, [{
642
- id: crypto.randomUUID(),
643
- action: 'approval_requested',
644
- actor: subject.actorUri,
645
- actorRole: 'secretary',
646
- onBehalfOf,
647
- session: sessionUri,
648
- toolCallId: request.toolCallId,
649
- approval: approvalUri,
650
- context: JSON.stringify(requestContext),
651
- policyVersion,
652
- createdAt: now,
653
- }]);
649
+ });
654
650
  });
655
651
  }
656
652
  export async function waitForRemoteWatchApproval(options) {
@@ -660,10 +656,13 @@ export async function waitForRemoteWatchApproval(options) {
660
656
  if (options.signal?.aborted) {
661
657
  throw createAbortError();
662
658
  }
663
- const approvals = await store.listApprovals();
664
- const row = approvals.find((entry) => entry.id === options.approvalId);
659
+ const row = await readRemoteApprovalRow(store, {
660
+ approvalId: options.approvalId,
661
+ approvalUri: options.approvalUri,
662
+ });
665
663
  if (!row) {
666
- throw new Error(`Remote approval disappeared before resolution: ${options.approvalId}`);
664
+ await activeRuntime.sleep(options.pollMs ?? DEFAULT_REMOTE_APPROVAL_POLL_MS);
665
+ continue;
667
666
  }
668
667
  const decision = decisionFromApprovalRow(row);
669
668
  if (decision) {
@@ -696,6 +695,7 @@ export async function requestRemoteWatchApproval(options) {
696
695
  });
697
696
  return waitForRemoteWatchApproval({
698
697
  approvalId: summary.id,
698
+ approvalUri: summary.approvalUri,
699
699
  pollMs: options.pollMs,
700
700
  signal: options.signal,
701
701
  runtime: activeRuntime,
@@ -728,6 +728,7 @@ export async function requestRemoteApproval(options) {
728
728
  });
729
729
  return waitForRemoteWatchApproval({
730
730
  approvalId: summary.id,
731
+ approvalUri: summary.approvalUri,
731
732
  pollMs: options.pollMs,
732
733
  signal: options.signal,
733
734
  runtime: activeRuntime,
@@ -737,12 +738,9 @@ export async function listRemoteWatchApprovals(options = {}) {
737
738
  const activeRuntime = options.runtime ?? await createDefaultRuntime();
738
739
  const requestedStatus = options.status ?? 'pending';
739
740
  return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
740
- const [approvals, audits] = await Promise.all([
741
- store.listApprovals(),
742
- store.listAudits(),
743
- ]);
741
+ const approvals = await store.listApprovals();
744
742
  return approvals
745
- .map((row) => normalizeApprovalSummary(row, audits))
743
+ .map((row) => normalizeApprovalSummary(row))
746
744
  .filter((summary) => !summary.assignedTo || summary.assignedTo === webId)
747
745
  .filter((summary) => requestedStatus === 'all' || summary.status === requestedStatus)
748
746
  .sort((left, right) => right.createdAt.localeCompare(left.createdAt));
@@ -751,14 +749,15 @@ export async function listRemoteWatchApprovals(options = {}) {
751
749
  export async function resolveRemoteWatchApproval(options) {
752
750
  const activeRuntime = options.runtime ?? await createDefaultRuntime();
753
751
  return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
754
- const approvals = await store.listApprovals();
755
- const row = approvals.find((entry) => entry.id === options.approvalId);
752
+ const row = await readRemoteApprovalRow(store, {
753
+ approvalId: options.approvalId,
754
+ approvalUri: options.approvalUri,
755
+ });
756
756
  if (!row) {
757
757
  throw new Error(`Remote approval not found: ${options.approvalId}`);
758
758
  }
759
759
  if (row.status !== 'pending') {
760
- const audits = await store.listAudits();
761
- return normalizeApprovalSummary(row, audits);
760
+ return normalizeApprovalSummary(row);
762
761
  }
763
762
  const now = activeRuntime.now();
764
763
  const approvalCreatedAt = new Date(toIsoString(row.createdAt, now.toISOString()));
@@ -774,22 +773,20 @@ export async function resolveRemoteWatchApproval(options) {
774
773
  reason: encodeDecisionReason(options.decision, options.note),
775
774
  resolvedAt: now,
776
775
  });
777
- await store.insertAudit({
776
+ await warnOnly(activeRuntime, () => store.insertAudit({
778
777
  id: crypto.randomUUID(),
779
778
  action: nextStatus === 'approved' ? 'approval_approved' : 'approval_rejected',
780
779
  actor: webId,
781
780
  actorRole: 'human',
782
781
  onBehalfOf: webId,
783
782
  session: row.session,
783
+ entry: approvalUri,
784
784
  toolCallId: row.toolCallId,
785
+ toolName: row.toolName,
785
786
  approval: approvalUri,
786
- context: JSON.stringify({
787
- decision: options.decision,
788
- ...(options.note?.trim() ? { note: options.note.trim() } : {}),
789
- }),
790
787
  policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
791
788
  createdAt: now,
792
- });
789
+ }));
793
790
  if (options.decision === 'accept_for_session') {
794
791
  const grantId = crypto.randomUUID();
795
792
  await store.insertGrant({
@@ -803,19 +800,19 @@ export async function resolveRemoteWatchApproval(options) {
803
800
  onBehalfOf: webId,
804
801
  createdAt: now,
805
802
  });
806
- await store.insertInboxNotification({
803
+ await warnOnly(activeRuntime, () => store.insertInboxNotification({
807
804
  id: crypto.randomUUID(),
808
805
  actor: webId,
809
806
  object: buildGrantUri(row.session, grantId),
810
807
  createdAt: now,
811
- }).catch(() => undefined);
808
+ }));
812
809
  }
813
- await store.insertInboxNotification({
810
+ await warnOnly(activeRuntime, () => store.insertInboxNotification({
814
811
  id: crypto.randomUUID(),
815
812
  actor: webId,
816
813
  object: approvalUri,
817
814
  createdAt: now,
818
- }).catch(() => undefined);
815
+ }));
819
816
  const nextRow = {
820
817
  ...row,
821
818
  status: nextStatus,
@@ -825,15 +822,25 @@ export async function resolveRemoteWatchApproval(options) {
825
822
  reason: encodeDecisionReason(options.decision, options.note),
826
823
  resolvedAt: now,
827
824
  };
828
- const audits = await store.listAudits();
829
- return normalizeApprovalSummary(nextRow, audits);
825
+ return normalizeApprovalSummary(nextRow);
830
826
  });
831
827
  }
828
+ async function readRemoteApprovalRow(store, options) {
829
+ if (store.findApproval) {
830
+ const row = await store.findApproval(options.approvalId, {
831
+ resourceUri: options.approvalUri,
832
+ });
833
+ if (row || options.approvalUri) {
834
+ return row;
835
+ }
836
+ }
837
+ const approvals = await store.listApprovals();
838
+ return approvals.find((entry) => entry.id === options.approvalId) ?? null;
839
+ }
832
840
  export const __podApprovalInternal = {
833
841
  createAbortError,
834
842
  createDefaultRuntime,
835
843
  buildActionUri,
836
- buildRequestAuditContext,
837
844
  buildRisk,
838
845
  buildToolName,
839
846
  createNativeRemoteApprovalStore,
@@ -841,9 +848,9 @@ export const __podApprovalInternal = {
841
848
  decisionFromApprovalRow,
842
849
  encodeDecisionReason,
843
850
  formatSummaryHeadline,
851
+ readRemoteApprovalRow,
844
852
  isRemoteApprovalAbortError,
845
853
  normalizeApprovalSummary,
846
854
  parseDecisionReason,
847
- parseRequestAuditContext,
848
855
  };
849
856
  //# sourceMappingURL=pod-approval.js.map