@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.
- package/dist/lib/chat-api.js +31 -4
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/pi-adapter/interactive.js +37 -0
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +1 -114
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +18 -45
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +2 -0
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/runtime.js +1 -0
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +23 -2
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/watch/pod-approval.js +190 -183
- package/dist/lib/watch/pod-approval.js.map +1 -1
- package/dist/lib/watch/runner.js +3 -0
- package/dist/lib/watch/runner.js.map +1 -1
- package/package.json +2 -2
|
@@ -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 {
|
|
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
|
|
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
|
|
160
|
-
if (typeof value !== 'string' || !value.trim()) {
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
154
|
+
async function warnOnly(runtime, task) {
|
|
163
155
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
361
|
-
{ predicate:
|
|
362
|
-
{ predicate:
|
|
363
|
-
{ predicate:
|
|
364
|
-
{ predicate:
|
|
365
|
-
{ predicate:
|
|
366
|
-
{ predicate:
|
|
367
|
-
{ predicate:
|
|
368
|
-
...(row.assignedTo ? [{ predicate:
|
|
369
|
-
...(row.decisionBy ? [{ predicate:
|
|
370
|
-
...(row.decisionRole ? [{ predicate:
|
|
371
|
-
...(row.onBehalfOf ? [{ predicate:
|
|
372
|
-
...(row.reason ? [{ predicate:
|
|
373
|
-
...(row.policyVersion ? [{ predicate:
|
|
374
|
-
{ predicate:
|
|
375
|
-
...(row.resolvedAt ? [{ predicate:
|
|
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(
|
|
402
|
-
{ predicate:
|
|
403
|
-
{ predicate:
|
|
404
|
-
{ predicate:
|
|
405
|
-
...(row.onBehalfOf ? [{ predicate:
|
|
406
|
-
...(row.session ? [{ predicate:
|
|
407
|
-
...(row.
|
|
408
|
-
...(row.
|
|
409
|
-
...(row.
|
|
410
|
-
...(row.
|
|
411
|
-
{ predicate:
|
|
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(
|
|
449
|
-
{ predicate: RDF_TYPE, object: iri(
|
|
450
|
-
{ predicate:
|
|
451
|
-
{ predicate:
|
|
452
|
-
{ predicate:
|
|
453
|
-
...(normalizeString(row.riskCeiling) ? [{ predicate:
|
|
454
|
-
{ predicate:
|
|
455
|
-
{ predicate:
|
|
456
|
-
...(normalizeString(row.onBehalfOf) ? [{ predicate:
|
|
457
|
-
{ predicate:
|
|
458
|
-
...(normalizeString(row.revokedAt) ? [{ predicate:
|
|
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(
|
|
468
|
-
...(row.actor ? [{ predicate:
|
|
469
|
-
{ predicate:
|
|
470
|
-
{ predicate:
|
|
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,
|
|
476
|
-
const toolCallId = firstLiteral(predicates,
|
|
477
|
-
const toolName = firstLiteral(predicates,
|
|
478
|
-
const target = firstIri(predicates,
|
|
479
|
-
const action = firstIri(predicates,
|
|
480
|
-
const risk = firstLiteral(predicates,
|
|
481
|
-
const status = firstLiteral(predicates,
|
|
482
|
-
const createdAt = firstLiteral(predicates,
|
|
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,
|
|
496
|
-
decisionBy: firstIri(predicates,
|
|
497
|
-
decisionRole: firstLiteral(predicates,
|
|
498
|
-
onBehalfOf: firstIri(predicates,
|
|
499
|
-
reason: firstLiteral(predicates,
|
|
500
|
-
policyVersion: firstLiteral(predicates,
|
|
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,
|
|
513
|
+
resolvedAt: firstLiteral(predicates, ApprovalVocab.resolvedAt),
|
|
503
514
|
};
|
|
504
515
|
}
|
|
505
516
|
function auditRowFromPredicates(url, predicates) {
|
|
506
|
-
const action = firstLiteral(predicates,
|
|
507
|
-
const actor = firstIri(predicates,
|
|
508
|
-
const actorRole = firstLiteral(predicates,
|
|
509
|
-
const createdAt = firstLiteral(predicates,
|
|
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,
|
|
519
|
-
session: firstIri(predicates,
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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,
|
|
529
|
-
const action = firstIri(predicates,
|
|
530
|
-
const effect = firstLiteral(predicates,
|
|
531
|
-
const decisionBy = firstIri(predicates,
|
|
532
|
-
const decisionRole = firstLiteral(predicates,
|
|
533
|
-
const createdAt = firstLiteral(predicates,
|
|
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,
|
|
554
|
+
riskCeiling: firstLiteral(predicates, GrantVocab.riskCeiling),
|
|
543
555
|
decisionBy,
|
|
544
556
|
decisionRole,
|
|
545
|
-
onBehalfOf: firstIri(predicates,
|
|
557
|
+
onBehalfOf: firstIri(predicates, GrantVocab.onBehalfOf),
|
|
546
558
|
createdAt,
|
|
547
|
-
revokedAt: firstLiteral(predicates,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
})
|
|
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
|
|
664
|
-
|
|
659
|
+
const row = await readRemoteApprovalRow(store, {
|
|
660
|
+
approvalId: options.approvalId,
|
|
661
|
+
approvalUri: options.approvalUri,
|
|
662
|
+
});
|
|
665
663
|
if (!row) {
|
|
666
|
-
|
|
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
|
|
741
|
-
store.listApprovals(),
|
|
742
|
-
store.listAudits(),
|
|
743
|
-
]);
|
|
741
|
+
const approvals = await store.listApprovals();
|
|
744
742
|
return approvals
|
|
745
|
-
.map((row) => normalizeApprovalSummary(row
|
|
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
|
|
755
|
-
|
|
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
|
-
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
|
|
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
|