@undefineds.co/linx 0.2.19 → 0.2.24
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/README.md +23 -21
- package/dist/index.js +93 -142
- package/dist/index.js.map +1 -1
- package/dist/lib/ai-command.js +34 -30
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +235 -0
- package/dist/lib/auto-mode/archive.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/auth.js +6 -6
- package/dist/lib/auto-mode/auth.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/codex-composer.js +1 -1
- package/dist/lib/auto-mode/codex-composer.js.map +1 -0
- package/dist/lib/auto-mode/codex-footer.js.map +1 -0
- package/dist/lib/auto-mode/codex-overlay.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/codex-request-form.js +2 -2
- package/dist/lib/auto-mode/codex-request-form.js.map +1 -0
- package/dist/lib/auto-mode/codex-request-input.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/display.js +63 -57
- package/dist/lib/auto-mode/display.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/format.js +24 -16
- package/dist/lib/auto-mode/format.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/hooks/claude.js +3 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/hooks/codebuddy.js +3 -2
- package/dist/lib/auto-mode/hooks/codebuddy.js.map +1 -0
- package/dist/lib/auto-mode/hooks/codex.js +18 -0
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -0
- package/dist/lib/auto-mode/hooks/index.js +27 -0
- package/dist/lib/auto-mode/hooks/index.js.map +1 -0
- package/dist/lib/auto-mode/hooks/shared.js.map +1 -0
- package/dist/lib/auto-mode/index.js.map +1 -0
- package/dist/lib/auto-mode/pod-ai.js +116 -0
- package/dist/lib/auto-mode/pod-ai.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/pod-approval.js +495 -325
- package/dist/lib/auto-mode/pod-approval.js.map +1 -0
- package/dist/lib/auto-mode/pod-persistence.js +412 -0
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/runner.js +280 -193
- package/dist/lib/auto-mode/runner.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/secretary.js +8 -8
- package/dist/lib/auto-mode/secretary.js.map +1 -0
- package/dist/lib/{watch → auto-mode}/types.js.map +1 -1
- package/dist/lib/auto-mode-command.js +114 -0
- package/dist/lib/auto-mode-command.js.map +1 -0
- package/dist/lib/codex-plugin/bridge.js +11 -12
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +11 -12
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/linx-tui-contract.js +26 -0
- package/dist/lib/linx-tui-contract.js.map +1 -0
- package/dist/lib/login-command.js +9 -3
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +2 -2
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +83 -104
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +7 -1
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auth.js +9 -15
- package/dist/lib/pi-adapter/auth.js.map +1 -1
- package/dist/lib/pi-adapter/branding.js +14 -44
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/interactive.js +19 -23
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +239 -1
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +64 -59
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +479 -0
- package/dist/lib/pi-adapter/pod-native.js.map +1 -0
- package/dist/lib/pi-adapter/pod-tools.js +76 -40
- package/dist/lib/pi-adapter/pod-tools.js.map +1 -1
- package/dist/lib/pi-adapter/runtime.js +11 -66
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session.js +276 -33
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/web-fetch.js +47 -30
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +36 -38
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +97 -30
- package/dist/lib/pod-data-session.js.map +1 -1
- package/package.json +3 -3
- package/vendor/agent-runtime/dist/auto-mode.d.ts +287 -0
- package/vendor/agent-runtime/dist/auto-mode.js +1498 -0
- package/vendor/agent-runtime/dist/index.d.ts +2 -0
- package/vendor/agent-runtime/dist/index.js +2 -0
- package/vendor/agent-runtime/dist/runtime.d.ts +47 -0
- package/vendor/agent-runtime/dist/runtime.js +36 -0
- package/vendor/agent-runtime/dist/turn-controller.d.ts +4 -4
- package/vendor/agent-runtime/dist/turn-controller.js +7 -7
- package/vendor/agent-runtime/package.json +2 -0
- package/dist/lib/watch/archive.js +0 -110
- package/dist/lib/watch/archive.js.map +0 -1
- package/dist/lib/watch/auth.js.map +0 -1
- package/dist/lib/watch/codex-composer.js.map +0 -1
- package/dist/lib/watch/codex-footer.js.map +0 -1
- package/dist/lib/watch/codex-overlay.js.map +0 -1
- package/dist/lib/watch/codex-request-form.js.map +0 -1
- package/dist/lib/watch/codex-request-input.js.map +0 -1
- package/dist/lib/watch/display.js.map +0 -1
- package/dist/lib/watch/format.js.map +0 -1
- package/dist/lib/watch/hooks/claude.js.map +0 -1
- package/dist/lib/watch/hooks/codebuddy.js.map +0 -1
- package/dist/lib/watch/hooks/codex.js +0 -17
- package/dist/lib/watch/hooks/codex.js.map +0 -1
- package/dist/lib/watch/hooks/index.js +0 -24
- package/dist/lib/watch/hooks/index.js.map +0 -1
- package/dist/lib/watch/hooks/shared.js.map +0 -1
- package/dist/lib/watch/index.js.map +0 -1
- package/dist/lib/watch/pod-ai.js +0 -160
- package/dist/lib/watch/pod-ai.js.map +0 -1
- package/dist/lib/watch/pod-approval.js.map +0 -1
- package/dist/lib/watch/pod-persistence.js +0 -334
- package/dist/lib/watch/pod-persistence.js.map +0 -1
- package/dist/lib/watch/runner.js.map +0 -1
- package/dist/lib/watch/secretary.js.map +0 -1
- package/dist/watch-cli.js +0 -116
- package/dist/watch-cli.js.map +0 -1
- /package/dist/lib/{watch → auto-mode}/codex-footer.js +0 -0
- /package/dist/lib/{watch → auto-mode}/codex-overlay.js +0 -0
- /package/dist/lib/{watch → auto-mode}/codex-request-input.js +0 -0
- /package/dist/lib/{watch → auto-mode}/hooks/shared.js +0 -0
- /package/dist/lib/{watch → auto-mode}/index.js +0 -0
- /package/dist/lib/{watch → auto-mode}/types.js +0 -0
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
2
|
import { getDefaultPodDataSession } from '../pod-data-session.js';
|
|
3
|
-
import { approvalResource, auditResource, drizzle, grantResource,
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { approvalResource, auditResource, buildApprovalSubjectPath, buildGrantSubjectPath, drizzle, grantResource, inboxNotificationResource, solidResources, } from '../models.js';
|
|
4
|
+
import { AS, ODRL, UDFS } from '@undefineds.co/models/namespaces';
|
|
5
|
+
import { ApprovalVocab, AuditVocab, GrantVocab, InboxNotificationVocab } from '@undefineds.co/models/vocab/sidecar';
|
|
6
|
+
import { autoModeApprovalActionUri, autoModeApprovalRequestMessage, autoModeApprovalRisk, autoModeApprovalToolName, } from '../../../vendor/agent-runtime/dist/auto-mode.js';
|
|
7
|
+
import { resolveAutoModeGrantCoverage } from './secretary.js';
|
|
8
|
+
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';
|
|
9
|
+
const AUTO_MODE_CHAT_ID_PREFIX = 'linx-auto-mode';
|
|
10
|
+
const AUTO_MODE_AGENT_ID = 'linx-auto-mode-assistant';
|
|
11
|
+
const REMOTE_APPROVAL_POLICY_VERSION = 'linx-auto-mode-remote-approval/v1';
|
|
8
12
|
const DEFAULT_REMOTE_APPROVAL_POLL_MS = 1000;
|
|
9
13
|
const DEFAULT_WARN_ONLY_TIMEOUT_MS = 5000;
|
|
14
|
+
const DEFAULT_APPROVAL_LIST_DAYS = 7;
|
|
10
15
|
const MAX_GRANT_POLICY_LENGTH = 1200;
|
|
11
16
|
const MAX_APPROVAL_CONTEXT_LENGTH = 1400;
|
|
12
17
|
const MIN_GRANT_COVERAGE_CONFIDENCE = 0.75;
|
|
@@ -42,53 +47,41 @@ function getPodBaseUrl(webIdOrUri) {
|
|
|
42
47
|
}
|
|
43
48
|
return webIdOrUri.replace(/\/$/, '');
|
|
44
49
|
}
|
|
45
|
-
function
|
|
46
|
-
return `${
|
|
50
|
+
function buildAutoModeChatId(record) {
|
|
51
|
+
return `${AUTO_MODE_CHAT_ID_PREFIX}-${record.backend}`;
|
|
47
52
|
}
|
|
48
53
|
function buildThreadUri(webId, record) {
|
|
49
|
-
return `${getPodBaseUrl(webId)}/.data/chat/${
|
|
54
|
+
return `${getPodBaseUrl(webId)}/.data/chat/${buildAutoModeChatId(record)}/index.ttl#${record.id}`;
|
|
50
55
|
}
|
|
51
|
-
function
|
|
52
|
-
return
|
|
56
|
+
function buildApprovalUriForDate(webIdOrUri, approvalId, createdAt) {
|
|
57
|
+
return buildPodResourceIri(webIdOrUri, buildApprovalSubjectPath(approvalId, createdAt));
|
|
58
|
+
}
|
|
59
|
+
function documentUrlFromResourceUri(resourceUri) {
|
|
60
|
+
return resourceUri.split('#', 1)[0] ?? resourceUri;
|
|
61
|
+
}
|
|
62
|
+
function buildGrantUri(webIdOrUri, grantId) {
|
|
63
|
+
return buildPodResourceIri(webIdOrUri, buildGrantSubjectPath(grantId));
|
|
64
|
+
}
|
|
65
|
+
function buildPodResourceIri(webIdOrUri, relativeUri) {
|
|
66
|
+
if (/^https?:\/\//.test(relativeUri)) {
|
|
67
|
+
return relativeUri;
|
|
68
|
+
}
|
|
69
|
+
return new URL(relativeUri.replace(/^\//, ''), `${getPodBaseUrl(webIdOrUri)}/`).toString();
|
|
53
70
|
}
|
|
54
71
|
function buildGrantSchemaUri(webIdOrUri) {
|
|
55
72
|
return `${getPodBaseUrl(webIdOrUri)}/settings/autonomy/schema/grant.ttl#GrantWikiPage`;
|
|
56
73
|
}
|
|
57
74
|
function buildAgentUri(webId) {
|
|
58
|
-
return `${getPodBaseUrl(webId)}/.data/agents/${
|
|
75
|
+
return `${getPodBaseUrl(webId)}/.data/agents/${AUTO_MODE_AGENT_ID}.ttl`;
|
|
59
76
|
}
|
|
60
77
|
function buildActionUri(request) {
|
|
61
|
-
|
|
62
|
-
return 'https://undefineds.co/ns#commandExecution';
|
|
63
|
-
}
|
|
64
|
-
if (request.kind === 'file-change-approval') {
|
|
65
|
-
return 'https://undefineds.co/ns#fileChange';
|
|
66
|
-
}
|
|
67
|
-
if (request.kind === 'permissions-approval') {
|
|
68
|
-
return 'https://undefineds.co/ns#permissionRequest';
|
|
69
|
-
}
|
|
70
|
-
return 'https://undefineds.co/ns#runtimeApproval';
|
|
78
|
+
return autoModeApprovalActionUri(request);
|
|
71
79
|
}
|
|
72
80
|
function buildToolName(request) {
|
|
73
|
-
|
|
74
|
-
return 'commandExecution';
|
|
75
|
-
}
|
|
76
|
-
if (request.kind === 'file-change-approval') {
|
|
77
|
-
return 'fileChange';
|
|
78
|
-
}
|
|
79
|
-
if (request.kind === 'permissions-approval') {
|
|
80
|
-
return 'permissionRequest';
|
|
81
|
-
}
|
|
82
|
-
return 'runtimeApproval';
|
|
81
|
+
return autoModeApprovalToolName(request);
|
|
83
82
|
}
|
|
84
83
|
function buildRisk(request) {
|
|
85
|
-
|
|
86
|
-
return 'high';
|
|
87
|
-
}
|
|
88
|
-
if (request.kind === 'file-change-approval') {
|
|
89
|
-
return 'high';
|
|
90
|
-
}
|
|
91
|
-
return 'medium';
|
|
84
|
+
return autoModeApprovalRisk(request);
|
|
92
85
|
}
|
|
93
86
|
function riskScore(risk) {
|
|
94
87
|
switch (risk) {
|
|
@@ -103,13 +96,7 @@ function riskScore(risk) {
|
|
|
103
96
|
}
|
|
104
97
|
}
|
|
105
98
|
function buildRequestMessage(request) {
|
|
106
|
-
|
|
107
|
-
return request.command?.trim() || request.message;
|
|
108
|
-
}
|
|
109
|
-
if (request.kind === 'file-change-approval') {
|
|
110
|
-
return request.reason?.trim() || request.message;
|
|
111
|
-
}
|
|
112
|
-
return request.message;
|
|
99
|
+
return autoModeApprovalRequestMessage(request);
|
|
113
100
|
}
|
|
114
101
|
function extractToolCallId(request) {
|
|
115
102
|
if (!isRecord(request.raw)) {
|
|
@@ -253,7 +240,7 @@ function grantWikiTagsFromApproval(row, explicitTags) {
|
|
|
253
240
|
}
|
|
254
241
|
function grantContextFromApproval(row) {
|
|
255
242
|
return safeCompactJson({
|
|
256
|
-
sourceApproval: row.
|
|
243
|
+
sourceApproval: buildApprovalUriForDate(row.session, row.id, new Date(toIsoString(row.createdAt, new Date().toISOString()))),
|
|
257
244
|
session: row.session,
|
|
258
245
|
toolCallId: row.toolCallId,
|
|
259
246
|
toolName: row.toolName,
|
|
@@ -263,6 +250,16 @@ function grantContextFromApproval(row) {
|
|
|
263
250
|
approvalContext: row.context,
|
|
264
251
|
}, MAX_APPROVAL_CONTEXT_LENGTH);
|
|
265
252
|
}
|
|
253
|
+
function literalValues(predicates, predicate) {
|
|
254
|
+
return (predicates.get(predicate) ?? [])
|
|
255
|
+
.map((object) => isRecord(object) && object.type === 'literal' && typeof object.value === 'string' ? object.value : '')
|
|
256
|
+
.filter(Boolean);
|
|
257
|
+
}
|
|
258
|
+
function iriValues(predicates, predicate) {
|
|
259
|
+
return (predicates.get(predicate) ?? [])
|
|
260
|
+
.map((object) => isRecord(object) && object.type === 'iri' && typeof object.value === 'string' ? object.value : '')
|
|
261
|
+
.filter(Boolean);
|
|
262
|
+
}
|
|
266
263
|
function grantSourceHash(row) {
|
|
267
264
|
return `approval:${row.id}:${row.toolCallId}:${row.risk}`;
|
|
268
265
|
}
|
|
@@ -386,7 +383,7 @@ function formatApprovalMessage(row) {
|
|
|
386
383
|
function formatSummaryHeadline(summary) {
|
|
387
384
|
return `${summary.id} | ${summary.status} | ${summary.risk} | session=${summary.sessionId}`;
|
|
388
385
|
}
|
|
389
|
-
export function
|
|
386
|
+
export function formatRemoteAutoModeApprovalSummary(summary) {
|
|
390
387
|
const detail = summary.command ?? summary.message;
|
|
391
388
|
const secondary = [
|
|
392
389
|
summary.toolName,
|
|
@@ -404,8 +401,11 @@ function missingRemoteApprovalCredentialsMessage() {
|
|
|
404
401
|
async function createDefaultRuntime() {
|
|
405
402
|
return {
|
|
406
403
|
getPodDataSession: getDefaultPodDataSession,
|
|
407
|
-
createStore(
|
|
408
|
-
|
|
404
|
+
createStore(webId, fetcher, session) {
|
|
405
|
+
if (session) {
|
|
406
|
+
return createOrmRemoteApprovalStore(session);
|
|
407
|
+
}
|
|
408
|
+
return createNativeRemoteApprovalStore(webId, fetcher);
|
|
409
409
|
},
|
|
410
410
|
sleep(ms) {
|
|
411
411
|
return delay(ms);
|
|
@@ -413,7 +413,7 @@ async function createDefaultRuntime() {
|
|
|
413
413
|
now() {
|
|
414
414
|
return new Date();
|
|
415
415
|
},
|
|
416
|
-
resolveGrantCoverage:
|
|
416
|
+
resolveGrantCoverage: resolveAutoModeGrantCoverage,
|
|
417
417
|
};
|
|
418
418
|
}
|
|
419
419
|
async function withRemoteApprovalStore(runtime, fn) {
|
|
@@ -450,112 +450,331 @@ async function createRemoteApprovalClient(runtime) {
|
|
|
450
450
|
if (!session) {
|
|
451
451
|
return null;
|
|
452
452
|
}
|
|
453
|
-
const db = createRemoteApprovalDb(session);
|
|
454
|
-
await initSolidTables(db, [
|
|
455
|
-
approvalResource,
|
|
456
|
-
auditResource,
|
|
457
|
-
grantResource,
|
|
458
|
-
inboxNotificationTable,
|
|
459
|
-
]);
|
|
460
453
|
return {
|
|
461
454
|
session,
|
|
462
|
-
store: runtime.createStore(session,
|
|
455
|
+
store: runtime.createStore(session.webId, session.fetch, session),
|
|
463
456
|
};
|
|
464
457
|
}
|
|
465
|
-
function
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
458
|
+
function createOrmRemoteApprovalStore(podSession) {
|
|
459
|
+
let dbPromise = null;
|
|
460
|
+
const getDb = async () => {
|
|
461
|
+
if (!dbPromise) {
|
|
462
|
+
dbPromise = Promise.resolve().then(async () => {
|
|
463
|
+
const db = drizzle(podSession.solidSession, {
|
|
464
|
+
logger: false,
|
|
465
|
+
disableInteropDiscovery: true,
|
|
466
|
+
podUrl: podSession.podUrl,
|
|
467
|
+
resourcePreparation: 'best-effort',
|
|
468
|
+
schema: solidResources,
|
|
469
|
+
});
|
|
470
|
+
return db;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
return dbPromise;
|
|
474
|
+
};
|
|
475
|
+
return createSharedModelRemoteApprovalStore(podSession.webId, getDb);
|
|
471
476
|
}
|
|
472
|
-
function
|
|
477
|
+
function createSharedModelRemoteApprovalStore(webId, getDb) {
|
|
478
|
+
const listApprovals = async () => {
|
|
479
|
+
const rows = await modelList(getDb, approvalResource);
|
|
480
|
+
return rows.map((row) => enrichApprovalRow(webId, row));
|
|
481
|
+
};
|
|
473
482
|
return {
|
|
474
|
-
listApprovals
|
|
475
|
-
findApproval: (id, options) =>
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
throw new Error('Solid database does not support updateByIri');
|
|
486
|
-
}
|
|
487
|
-
const updated = await updateByIri.call(db, approvalResource, targetIri, data);
|
|
488
|
-
if (!updated) {
|
|
489
|
-
throw new Error(`Remote approval not found: ${id}`);
|
|
490
|
-
}
|
|
491
|
-
return;
|
|
483
|
+
listApprovals,
|
|
484
|
+
findApproval: async (id, options = {}) => {
|
|
485
|
+
if (options.resourceUri) {
|
|
486
|
+
const row = await modelFindByIri(getDb, approvalResource, options.resourceUri);
|
|
487
|
+
return row ? enrichApprovalRow(webId, row, options.resourceUri) : null;
|
|
488
|
+
}
|
|
489
|
+
if (options.createdAt) {
|
|
490
|
+
const createdAt = new Date(toIsoString(options.createdAt, new Date().toISOString()));
|
|
491
|
+
const iri = buildApprovalUriForDate(webId, id, createdAt);
|
|
492
|
+
const row = await modelFindByIri(getDb, approvalResource, iri);
|
|
493
|
+
return row ? enrichApprovalRow(webId, row, iri) : null;
|
|
492
494
|
}
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
495
|
+
const row = await modelFindById(getDb, approvalResource, id);
|
|
496
|
+
return row ? enrichApprovalRow(webId, row) : null;
|
|
497
|
+
},
|
|
498
|
+
insertApproval: async (row) => {
|
|
499
|
+
await modelInsert(getDb, approvalResource, omitInternalFields(row));
|
|
500
|
+
},
|
|
501
|
+
updateApproval: async (id, patch, options = {}) => {
|
|
502
|
+
const explicitIri = options.resourceUri
|
|
503
|
+
?? normalizeString(patch.approvalUri)
|
|
504
|
+
?? (options.createdAt ? buildApprovalUriForDate(webId, id, new Date(toIsoString(options.createdAt, new Date().toISOString()))) : undefined);
|
|
505
|
+
if (explicitIri) {
|
|
506
|
+
await modelUpdateByIri(getDb, approvalResource, explicitIri, omitInternalFields(patch));
|
|
507
|
+
return;
|
|
496
508
|
}
|
|
497
|
-
const updated = await
|
|
509
|
+
const updated = await modelUpdateById(getDb, approvalResource, id, omitInternalFields(patch));
|
|
498
510
|
if (!updated) {
|
|
499
511
|
throw new Error(`Remote approval not found: ${id}`);
|
|
500
512
|
}
|
|
501
513
|
},
|
|
502
|
-
listAudits: () =>
|
|
503
|
-
insertAudit: (row) =>
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
514
|
+
listAudits: () => modelList(getDb, auditResource),
|
|
515
|
+
insertAudit: async (row) => {
|
|
516
|
+
await modelInsert(getDb, auditResource, omitInternalFields(row));
|
|
517
|
+
},
|
|
518
|
+
listGrants: () => modelList(getDb, grantResource),
|
|
519
|
+
insertGrant: async (row) => {
|
|
520
|
+
await modelInsert(getDb, grantResource, omitInternalFields(row));
|
|
521
|
+
},
|
|
522
|
+
insertInboxNotification: async (row) => {
|
|
523
|
+
await modelInsert(getDb, inboxNotificationResource, omitInternalFields(row));
|
|
524
|
+
},
|
|
508
525
|
};
|
|
509
526
|
}
|
|
510
|
-
async function
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return normalizeApprovalRow(row);
|
|
527
|
+
async function modelList(getDb, resource) {
|
|
528
|
+
const db = await getDb();
|
|
529
|
+
return await db.select().from(resource).execute();
|
|
530
|
+
}
|
|
531
|
+
async function modelFindByIri(getDb, resource, iri) {
|
|
532
|
+
const db = await getDb();
|
|
533
|
+
if (typeof db.findByIri === 'function') {
|
|
534
|
+
return await db.findByIri(resource, iri);
|
|
519
535
|
}
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
536
|
+
const rows = await db.select().from(resource).whereByIri(iri).execute();
|
|
537
|
+
return rows[0] ?? null;
|
|
538
|
+
}
|
|
539
|
+
async function modelFindById(getDb, resource, id) {
|
|
540
|
+
const db = await getDb();
|
|
541
|
+
if (typeof db.findById === 'function') {
|
|
542
|
+
return await db.findById(resource, id);
|
|
523
543
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
544
|
+
throw new Error('Remote approval shared model store requires findById support');
|
|
545
|
+
}
|
|
546
|
+
async function modelInsert(getDb, resource, row) {
|
|
547
|
+
const db = await getDb();
|
|
548
|
+
await db.insert(resource).values(stripUndefined(row)).execute();
|
|
549
|
+
}
|
|
550
|
+
async function modelUpdateByIri(getDb, resource, iri, patch) {
|
|
551
|
+
const db = await getDb();
|
|
552
|
+
const update = stripUndefined(patch);
|
|
553
|
+
delete update.id;
|
|
554
|
+
delete update.approvalUri;
|
|
555
|
+
if (typeof db.updateByIri === 'function') {
|
|
556
|
+
await db.updateByIri(resource, iri, update);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const query = db.update(resource).set(update);
|
|
560
|
+
if (typeof query.whereByIri !== 'function') {
|
|
561
|
+
throw new Error('Remote approval shared model store requires updateByIri/whereByIri support');
|
|
562
|
+
}
|
|
563
|
+
await query.whereByIri(iri).execute();
|
|
564
|
+
}
|
|
565
|
+
async function modelUpdateById(getDb, resource, id, patch) {
|
|
566
|
+
const db = await getDb();
|
|
567
|
+
const update = stripUndefined(patch);
|
|
568
|
+
delete update.id;
|
|
569
|
+
delete update.approvalUri;
|
|
570
|
+
if (typeof db.updateById === 'function') {
|
|
571
|
+
return await db.updateById(resource, id, update);
|
|
572
|
+
}
|
|
573
|
+
const row = await modelFindById(getDb, resource, id);
|
|
574
|
+
const iri = rowSubject(row ?? {});
|
|
575
|
+
if (!iri) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
await modelUpdateByIri(getDb, resource, iri, update);
|
|
579
|
+
return await modelFindByIri(getDb, resource, iri);
|
|
529
580
|
}
|
|
530
|
-
function
|
|
531
|
-
|
|
532
|
-
|
|
581
|
+
function stripUndefined(row) {
|
|
582
|
+
const next = {};
|
|
583
|
+
for (const [key, value] of Object.entries(row)) {
|
|
584
|
+
if (value !== undefined) {
|
|
585
|
+
next[key] = value;
|
|
586
|
+
}
|
|
533
587
|
}
|
|
588
|
+
return next;
|
|
589
|
+
}
|
|
590
|
+
function omitInternalFields(row) {
|
|
591
|
+
const next = stripUndefined(row);
|
|
592
|
+
delete next.approvalUri;
|
|
593
|
+
delete next['@id'];
|
|
594
|
+
delete next.subject;
|
|
595
|
+
delete next.source;
|
|
596
|
+
delete next.uri;
|
|
597
|
+
return next;
|
|
598
|
+
}
|
|
599
|
+
function rowSubject(row) {
|
|
600
|
+
return normalizeString(row['@id'])
|
|
601
|
+
?? normalizeString(row.subject)
|
|
602
|
+
?? normalizeString(row.uri);
|
|
603
|
+
}
|
|
604
|
+
function enrichApprovalRow(webId, row, explicitIri) {
|
|
605
|
+
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
534
606
|
return {
|
|
535
|
-
|
|
536
|
-
|
|
607
|
+
...row,
|
|
608
|
+
approvalUri: explicitIri
|
|
609
|
+
?? normalizeString(row.approvalUri)
|
|
610
|
+
?? rowSubject(row)
|
|
611
|
+
?? buildApprovalUriForDate(webId, row.id, createdAt),
|
|
537
612
|
};
|
|
538
613
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
614
|
+
function createNativeRemoteApprovalStore(webId, fetcher) {
|
|
615
|
+
return {
|
|
616
|
+
listApprovals: () => listApprovalRows(webId, fetcher),
|
|
617
|
+
findApproval: (id, options) => findApprovalRow(webId, fetcher, id, options),
|
|
618
|
+
insertApproval: (row) => writeApprovalRow(webId, fetcher, row),
|
|
619
|
+
async updateApproval(id, patch, options = {}) {
|
|
620
|
+
const explicitIri = options.resourceUri
|
|
621
|
+
?? normalizeString(patch.approvalUri)
|
|
622
|
+
?? (options.createdAt ? buildApprovalResourceUrl(webId, id, new Date(toIsoString(options.createdAt, new Date().toISOString()))) : undefined);
|
|
623
|
+
const existing = explicitIri
|
|
624
|
+
? await readApprovalRowFromResource(fetcher, explicitIri)
|
|
625
|
+
: (await listApprovalRows(webId, fetcher)).find((row) => row.id === id);
|
|
626
|
+
if (!existing) {
|
|
627
|
+
throw new Error(`Remote approval not found: ${id}`);
|
|
628
|
+
}
|
|
629
|
+
await writeApprovalRow(webId, fetcher, { ...existing, ...patch });
|
|
630
|
+
},
|
|
631
|
+
listAudits: () => listAuditRows(webId, fetcher),
|
|
632
|
+
insertAudit: (row) => writeAuditRow(webId, fetcher, row),
|
|
633
|
+
listGrants: () => listGrantRows(webId, fetcher),
|
|
634
|
+
insertGrant: (row) => writeGrantRow(webId, fetcher, row),
|
|
635
|
+
insertInboxNotification: (row) => writeInboxNotificationRow(webId, fetcher, row),
|
|
636
|
+
};
|
|
542
637
|
}
|
|
543
|
-
async function
|
|
544
|
-
|
|
638
|
+
async function findApprovalRow(webId, fetcher, id, options = {}) {
|
|
639
|
+
if (options.resourceUri) {
|
|
640
|
+
return readApprovalRowFromResource(fetcher, options.resourceUri);
|
|
641
|
+
}
|
|
642
|
+
if (options.createdAt) {
|
|
643
|
+
const createdAt = new Date(toIsoString(options.createdAt, new Date().toISOString()));
|
|
644
|
+
return readApprovalRowFromResource(fetcher, buildApprovalResourceUrl(webId, id, createdAt));
|
|
645
|
+
}
|
|
646
|
+
return (await listApprovalRows(webId, fetcher)).find((row) => row.id === id) ?? null;
|
|
545
647
|
}
|
|
546
|
-
async function
|
|
547
|
-
const
|
|
548
|
-
|
|
648
|
+
async function readApprovalRowFromResource(fetcher, resourceUri) {
|
|
649
|
+
const turtle = await readTurtleResource(fetcher, documentUrlFromResourceUri(resourceUri));
|
|
650
|
+
if (!turtle) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, documentUrlFromResourceUri(resourceUri))) {
|
|
654
|
+
if (subject !== resourceUri) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
const row = approvalRowFromPredicates(subject, predicates);
|
|
658
|
+
if (row) {
|
|
659
|
+
return row;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return null;
|
|
549
663
|
}
|
|
550
|
-
async function
|
|
551
|
-
|
|
664
|
+
async function listApprovalRows(webId, fetcher) {
|
|
665
|
+
const urls = [
|
|
666
|
+
...recentApprovalDocumentUrls(webId),
|
|
667
|
+
...await listTurtleResources(fetcher, `${getPodBaseUrl(webId)}/.data/approvals/`).catch(() => []),
|
|
668
|
+
];
|
|
669
|
+
const rows = [];
|
|
670
|
+
for (const url of [...new Set(urls)].filter((entry) => entry.endsWith('.ttl'))) {
|
|
671
|
+
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
672
|
+
if (!turtle)
|
|
673
|
+
continue;
|
|
674
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
675
|
+
const row = approvalRowFromPredicates(subject, predicates);
|
|
676
|
+
if (row)
|
|
677
|
+
rows.push(row);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return rows;
|
|
681
|
+
}
|
|
682
|
+
function recentApprovalDocumentUrls(webId, days = DEFAULT_APPROVAL_LIST_DAYS) {
|
|
683
|
+
const urls = [];
|
|
684
|
+
const base = Date.now();
|
|
685
|
+
for (let offset = 0; offset < days; offset += 1) {
|
|
686
|
+
const date = new Date(base - offset * 24 * 60 * 60 * 1000);
|
|
687
|
+
urls.push(buildApprovalDocumentUrl(webId, date));
|
|
688
|
+
}
|
|
689
|
+
return urls;
|
|
690
|
+
}
|
|
691
|
+
async function writeApprovalRow(webId, fetcher, row) {
|
|
692
|
+
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
693
|
+
const documentUrl = buildApprovalDocumentUrl(webId, createdAt);
|
|
694
|
+
const subjectUrl = buildApprovalResourceUrl(webId, row.id, createdAt);
|
|
695
|
+
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
696
|
+
subject: subjectUrl,
|
|
697
|
+
triples: [
|
|
698
|
+
{ predicate: RDF_TYPE, object: iri(UDFS.ApprovalRequest) },
|
|
699
|
+
{ predicate: ApprovalVocab.session, object: iri(row.session) },
|
|
700
|
+
{ predicate: ApprovalVocab.toolCallId, object: literal(row.toolCallId) },
|
|
701
|
+
{ predicate: ApprovalVocab.toolName, object: literal(row.toolName) },
|
|
702
|
+
{ predicate: ApprovalVocab.target, object: iri(row.target) },
|
|
703
|
+
{ predicate: ApprovalVocab.action, object: iri(row.action) },
|
|
704
|
+
{ predicate: ApprovalVocab.risk, object: literal(row.risk) },
|
|
705
|
+
{ predicate: ApprovalVocab.status, object: literal(row.status) },
|
|
706
|
+
...(row.assignedTo ? [{ predicate: ApprovalVocab.assignedTo, object: iri(row.assignedTo) }] : []),
|
|
707
|
+
...(row.decisionBy ? [{ predicate: ApprovalVocab.decisionBy, object: iri(row.decisionBy) }] : []),
|
|
708
|
+
...(row.decisionRole ? [{ predicate: ApprovalVocab.decisionRole, object: literal(row.decisionRole) }] : []),
|
|
709
|
+
...(row.onBehalfOf ? [{ predicate: ApprovalVocab.onBehalfOf, object: iri(row.onBehalfOf) }] : []),
|
|
710
|
+
...(row.reason ? [{ predicate: ApprovalVocab.reason, object: literal(row.reason) }] : []),
|
|
711
|
+
...(row.context ? [{ predicate: ApprovalVocab.context, object: literal(row.context) }] : []),
|
|
712
|
+
...(row.approvalOptions ? [{ predicate: ApprovalVocab.approvalOptions, object: literal(row.approvalOptions) }] : []),
|
|
713
|
+
...(row.policyVersion ? [{ predicate: ApprovalVocab.policyVersion, object: literal(row.policyVersion) }] : []),
|
|
714
|
+
{ predicate: ApprovalVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
|
|
715
|
+
...(row.expiresAt ? [{ predicate: ApprovalVocab.expiresAt, object: literal(toIsoString(row.expiresAt, new Date().toISOString())) }] : []),
|
|
716
|
+
...(row.resolvedAt ? [{ predicate: ApprovalVocab.resolvedAt, object: literal(toIsoString(row.resolvedAt, new Date().toISOString())) }] : []),
|
|
717
|
+
],
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
async function listAuditRows(webId, fetcher) {
|
|
721
|
+
const urls = await listTurtleResourcesRecursive(fetcher, `${getPodBaseUrl(webId)}/.data/audits/`);
|
|
722
|
+
const rows = [];
|
|
723
|
+
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
724
|
+
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
725
|
+
if (!turtle)
|
|
726
|
+
continue;
|
|
727
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
728
|
+
const row = auditRowFromPredicates(subject, predicates);
|
|
729
|
+
if (row)
|
|
730
|
+
rows.push(row);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return rows;
|
|
734
|
+
}
|
|
735
|
+
async function writeAuditRow(webId, fetcher, row) {
|
|
736
|
+
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
737
|
+
const documentUrl = buildAuditDocumentUrl(webId, createdAt);
|
|
738
|
+
const subjectUrl = buildAuditResourceUrl(webId, row.id, createdAt);
|
|
739
|
+
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
740
|
+
subject: subjectUrl,
|
|
741
|
+
triples: [
|
|
742
|
+
{ predicate: RDF_TYPE, object: iri(UDFS.AuditEntry) },
|
|
743
|
+
{ predicate: AuditVocab.action, object: literal(row.action) },
|
|
744
|
+
{ predicate: AuditVocab.actor, object: iri(row.actor) },
|
|
745
|
+
{ predicate: AuditVocab.actorRole, object: literal(row.actorRole) },
|
|
746
|
+
...(row.onBehalfOf ? [{ predicate: AuditVocab.onBehalfOf, object: iri(row.onBehalfOf) }] : []),
|
|
747
|
+
...(row.session ? [{ predicate: AuditVocab.session, object: iri(row.session) }] : []),
|
|
748
|
+
...(row.entry ? [{ predicate: AuditVocab.entry, object: iri(row.entry) }] : []),
|
|
749
|
+
...(row.toolCallId ? [{ predicate: AuditVocab.toolCallId, object: literal(row.toolCallId) }] : []),
|
|
750
|
+
...(row.toolName ? [{ predicate: AuditVocab.toolName, object: literal(row.toolName) }] : []),
|
|
751
|
+
...(row.approval ? [{ predicate: AuditVocab.approval, object: iri(row.approval) }] : []),
|
|
752
|
+
...(row.policyVersion ? [{ predicate: AuditVocab.policyVersion, object: literal(row.policyVersion) }] : []),
|
|
753
|
+
{ predicate: AuditVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
|
|
754
|
+
],
|
|
755
|
+
});
|
|
552
756
|
}
|
|
553
|
-
async function listGrantRows(
|
|
554
|
-
const
|
|
555
|
-
|
|
757
|
+
async function listGrantRows(webId, fetcher) {
|
|
758
|
+
const urls = [
|
|
759
|
+
...await listTurtleResources(fetcher, `${getPodBaseUrl(webId)}/settings/autonomy/grants/`).catch(() => []),
|
|
760
|
+
];
|
|
761
|
+
const rows = [];
|
|
762
|
+
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
763
|
+
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
764
|
+
if (!turtle)
|
|
765
|
+
continue;
|
|
766
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
767
|
+
const row = grantRowFromPredicates(subject, predicates);
|
|
768
|
+
if (row)
|
|
769
|
+
rows.push(row);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return rows;
|
|
556
773
|
}
|
|
557
|
-
async function writeGrantRow(
|
|
774
|
+
async function writeGrantRow(webId, fetcher, row) {
|
|
558
775
|
const id = normalizeString(row.id) ?? crypto.randomUUID();
|
|
776
|
+
const subjectUrl = buildGrantResourceUrl(webId, id);
|
|
777
|
+
const documentUrl = subjectUrl;
|
|
559
778
|
const target = normalizeString(row.target);
|
|
560
779
|
const action = normalizeString(row.action);
|
|
561
780
|
const effect = normalizeString(row.effect);
|
|
@@ -564,190 +783,141 @@ async function writeGrantRow(db, row) {
|
|
|
564
783
|
if (!target || !action || !effect || !decisionBy || !decisionRole) {
|
|
565
784
|
throw new Error(`Invalid remote approval grant row: ${id}`);
|
|
566
785
|
}
|
|
567
|
-
await
|
|
786
|
+
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
787
|
+
subject: subjectUrl,
|
|
788
|
+
triples: [
|
|
789
|
+
{ predicate: RDF_TYPE, object: iri(ODRL.Policy) },
|
|
790
|
+
{ predicate: RDF_TYPE, object: iri(UDFS.AutonomyGrant) },
|
|
791
|
+
{ predicate: GrantVocab.target, object: iri(target) },
|
|
792
|
+
{ predicate: GrantVocab.action, object: iri(action) },
|
|
793
|
+
...(normalizeString(row.title) ? [{ predicate: GrantVocab.title, object: literal(truncatePodLiteral(normalizeString(row.title), 160)) }] : []),
|
|
794
|
+
...(normalizeString(row.summary) ? [{ predicate: GrantVocab.summary, object: literal(truncatePodLiteral(normalizeString(row.summary), 500)) }] : []),
|
|
795
|
+
...(normalizeString(row.body) ? [{ predicate: GrantVocab.body, object: literal(truncatePodLiteral(normalizeString(row.body), MAX_GRANT_POLICY_LENGTH)) }] : []),
|
|
796
|
+
...(normalizeString(row.schema) ? [{ predicate: GrantVocab.schema, object: iri(normalizeString(row.schema)) }] : []),
|
|
797
|
+
...(normalizeString(row.pageKind) ? [{ predicate: GrantVocab.pageKind, object: literal(normalizeString(row.pageKind)) }] : []),
|
|
798
|
+
...(normalizeString(row.wikiStatus) ? [{ predicate: GrantVocab.wikiStatus, object: literal(normalizeString(row.wikiStatus)) }] : []),
|
|
799
|
+
...(normalizeString(row.tags) ? [{ predicate: GrantVocab.tags, object: literal(truncatePodLiteral(normalizeString(row.tags), 500)) }] : []),
|
|
800
|
+
...(normalizeString(row.source) ? [{ predicate: GrantVocab.source, object: literal(normalizeString(row.source)) }] : []),
|
|
801
|
+
...(normalizeString(row.sourceHash) ? [{ predicate: GrantVocab.sourceHash, object: literal(normalizeString(row.sourceHash)) }] : []),
|
|
802
|
+
...(row.compiledAt ? [{ predicate: GrantVocab.compiledAt, object: literal(toIsoString(row.compiledAt, new Date().toISOString())) }] : []),
|
|
803
|
+
...(row.compiledFrom ?? []).map((value) => ({ predicate: GrantVocab.compiledFrom, object: iri(value) })),
|
|
804
|
+
...(row.related ?? []).map((value) => ({ predicate: GrantVocab.related, object: iri(value) })),
|
|
805
|
+
{ predicate: GrantVocab.effect, object: literal(effect) },
|
|
806
|
+
...(normalizeString(row.riskCeiling) ? [{ predicate: GrantVocab.riskCeiling, object: literal(normalizeString(row.riskCeiling)) }] : []),
|
|
807
|
+
...(normalizeString(row.policy) ? [{ predicate: GrantVocab.policy, object: literal(truncatePodLiteral(normalizeString(row.policy), MAX_GRANT_POLICY_LENGTH)) }] : []),
|
|
808
|
+
...(normalizeString(row.context) ? [{ predicate: GrantVocab.context, object: literal(truncatePodLiteral(normalizeString(row.context), MAX_APPROVAL_CONTEXT_LENGTH)) }] : []),
|
|
809
|
+
{ predicate: GrantVocab.decisionBy, object: iri(decisionBy) },
|
|
810
|
+
{ predicate: GrantVocab.decisionRole, object: literal(decisionRole) },
|
|
811
|
+
...(normalizeString(row.onBehalfOf) ? [{ predicate: GrantVocab.onBehalfOf, object: iri(normalizeString(row.onBehalfOf)) }] : []),
|
|
812
|
+
{ predicate: GrantVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
|
|
813
|
+
...(normalizeString(row.revokedAt) ? [{ predicate: GrantVocab.revokedAt, object: literal(normalizeString(row.revokedAt)) }] : []),
|
|
814
|
+
],
|
|
815
|
+
});
|
|
568
816
|
}
|
|
569
|
-
async function writeInboxNotificationRow(
|
|
570
|
-
|
|
817
|
+
async function writeInboxNotificationRow(webId, fetcher, row) {
|
|
818
|
+
const url = buildInboxResourceUrl(webId, row.id);
|
|
819
|
+
await upsertManagedTurtleBlock(fetcher, url, {
|
|
820
|
+
subject: url,
|
|
821
|
+
triples: [
|
|
822
|
+
{ predicate: RDF_TYPE, object: iri(AS.Announce) },
|
|
823
|
+
...(row.actor ? [{ predicate: InboxNotificationVocab.actor, object: iri(row.actor) }] : []),
|
|
824
|
+
{ predicate: InboxNotificationVocab.object, object: iri(row.object) },
|
|
825
|
+
{ predicate: InboxNotificationVocab.createdAt, object: literal(toIsoString(row.createdAt, new Date().toISOString())) },
|
|
826
|
+
],
|
|
827
|
+
});
|
|
571
828
|
}
|
|
572
|
-
function
|
|
573
|
-
|
|
829
|
+
function approvalRowFromPredicates(url, predicates) {
|
|
830
|
+
const session = firstIri(predicates, ApprovalVocab.session);
|
|
831
|
+
const toolCallId = firstLiteral(predicates, ApprovalVocab.toolCallId);
|
|
832
|
+
const toolName = firstLiteral(predicates, ApprovalVocab.toolName);
|
|
833
|
+
const target = firstIri(predicates, ApprovalVocab.target);
|
|
834
|
+
const action = firstIri(predicates, ApprovalVocab.action);
|
|
835
|
+
const risk = firstLiteral(predicates, ApprovalVocab.risk);
|
|
836
|
+
const status = firstLiteral(predicates, ApprovalVocab.status);
|
|
837
|
+
const createdAt = firstLiteral(predicates, ApprovalVocab.createdAt);
|
|
838
|
+
if (!session || !toolCallId || !toolName || !target || !action || !risk || !status || !createdAt) {
|
|
574
839
|
return null;
|
|
840
|
+
}
|
|
575
841
|
return {
|
|
576
|
-
id:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
resolvedAt: row.resolvedAt,
|
|
842
|
+
id: subjectIdFromResourceUrl(url),
|
|
843
|
+
session,
|
|
844
|
+
toolCallId,
|
|
845
|
+
toolName,
|
|
846
|
+
target,
|
|
847
|
+
action,
|
|
848
|
+
risk,
|
|
849
|
+
status,
|
|
850
|
+
assignedTo: firstIri(predicates, ApprovalVocab.assignedTo),
|
|
851
|
+
decisionBy: firstIri(predicates, ApprovalVocab.decisionBy),
|
|
852
|
+
decisionRole: firstLiteral(predicates, ApprovalVocab.decisionRole),
|
|
853
|
+
onBehalfOf: firstIri(predicates, ApprovalVocab.onBehalfOf),
|
|
854
|
+
reason: firstLiteral(predicates, ApprovalVocab.reason),
|
|
855
|
+
context: firstLiteral(predicates, ApprovalVocab.context),
|
|
856
|
+
approvalOptions: firstLiteral(predicates, ApprovalVocab.approvalOptions),
|
|
857
|
+
policyVersion: firstLiteral(predicates, ApprovalVocab.policyVersion),
|
|
858
|
+
createdAt,
|
|
859
|
+
expiresAt: firstLiteral(predicates, ApprovalVocab.expiresAt),
|
|
860
|
+
resolvedAt: firstLiteral(predicates, ApprovalVocab.resolvedAt),
|
|
596
861
|
};
|
|
597
862
|
}
|
|
598
|
-
function
|
|
599
|
-
|
|
863
|
+
function auditRowFromPredicates(url, predicates) {
|
|
864
|
+
const action = firstLiteral(predicates, AuditVocab.action);
|
|
865
|
+
const actor = firstIri(predicates, AuditVocab.actor);
|
|
866
|
+
const actorRole = firstLiteral(predicates, AuditVocab.actorRole);
|
|
867
|
+
const createdAt = firstLiteral(predicates, AuditVocab.createdAt);
|
|
868
|
+
if (!action || !actor || !actorRole || !createdAt) {
|
|
600
869
|
return null;
|
|
870
|
+
}
|
|
601
871
|
return {
|
|
602
|
-
id:
|
|
603
|
-
action
|
|
604
|
-
actor
|
|
605
|
-
actorRole
|
|
606
|
-
onBehalfOf:
|
|
607
|
-
session:
|
|
608
|
-
entry:
|
|
609
|
-
toolCallId:
|
|
610
|
-
toolName:
|
|
611
|
-
approval:
|
|
612
|
-
policyVersion:
|
|
613
|
-
createdAt
|
|
872
|
+
id: subjectIdFromResourceUrl(url),
|
|
873
|
+
action,
|
|
874
|
+
actor,
|
|
875
|
+
actorRole,
|
|
876
|
+
onBehalfOf: firstIri(predicates, AuditVocab.onBehalfOf),
|
|
877
|
+
session: firstIri(predicates, AuditVocab.session),
|
|
878
|
+
entry: firstIri(predicates, AuditVocab.entry),
|
|
879
|
+
toolCallId: firstLiteral(predicates, AuditVocab.toolCallId),
|
|
880
|
+
toolName: firstLiteral(predicates, AuditVocab.toolName),
|
|
881
|
+
approval: firstIri(predicates, AuditVocab.approval),
|
|
882
|
+
policyVersion: firstLiteral(predicates, AuditVocab.policyVersion),
|
|
883
|
+
createdAt,
|
|
614
884
|
};
|
|
615
885
|
}
|
|
616
|
-
function
|
|
617
|
-
|
|
886
|
+
function grantRowFromPredicates(url, predicates) {
|
|
887
|
+
const target = firstIri(predicates, GrantVocab.target);
|
|
888
|
+
const action = firstIri(predicates, GrantVocab.action);
|
|
889
|
+
const effect = firstLiteral(predicates, GrantVocab.effect);
|
|
890
|
+
const decisionBy = firstIri(predicates, GrantVocab.decisionBy);
|
|
891
|
+
const decisionRole = firstLiteral(predicates, GrantVocab.decisionRole);
|
|
892
|
+
const createdAt = firstLiteral(predicates, GrantVocab.createdAt);
|
|
893
|
+
if (!target || !action || !effect || !decisionBy || !decisionRole || !createdAt) {
|
|
618
894
|
return null;
|
|
619
|
-
return {
|
|
620
|
-
id: String(row.id),
|
|
621
|
-
target: row.target,
|
|
622
|
-
action: row.action,
|
|
623
|
-
title: row.title,
|
|
624
|
-
summary: row.summary,
|
|
625
|
-
body: row.body,
|
|
626
|
-
schema: row.schema,
|
|
627
|
-
pageKind: row.pageKind,
|
|
628
|
-
wikiStatus: row.wikiStatus,
|
|
629
|
-
tags: row.tags,
|
|
630
|
-
source: row.source,
|
|
631
|
-
sourceHash: row.sourceHash,
|
|
632
|
-
compiledAt: row.compiledAt,
|
|
633
|
-
compiledFrom: row.compiledFrom,
|
|
634
|
-
related: row.related,
|
|
635
|
-
effect: row.effect,
|
|
636
|
-
riskCeiling: row.riskCeiling,
|
|
637
|
-
policy: row.policy,
|
|
638
|
-
context: row.context,
|
|
639
|
-
decisionBy: row.decisionBy,
|
|
640
|
-
decisionRole: row.decisionRole,
|
|
641
|
-
onBehalfOf: row.onBehalfOf,
|
|
642
|
-
createdAt: row.createdAt,
|
|
643
|
-
revokedAt: row.revokedAt,
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function normalizeApprovalInsert(row) {
|
|
647
|
-
return {
|
|
648
|
-
id: row.id,
|
|
649
|
-
session: row.session,
|
|
650
|
-
toolCallId: row.toolCallId,
|
|
651
|
-
toolName: row.toolName,
|
|
652
|
-
target: row.target,
|
|
653
|
-
action: row.action,
|
|
654
|
-
risk: row.risk,
|
|
655
|
-
status: row.status,
|
|
656
|
-
...(row.assignedTo ? { assignedTo: row.assignedTo } : {}),
|
|
657
|
-
...(row.decisionBy ? { decisionBy: row.decisionBy } : {}),
|
|
658
|
-
...(row.decisionRole ? { decisionRole: row.decisionRole } : {}),
|
|
659
|
-
...(row.onBehalfOf ? { onBehalfOf: row.onBehalfOf } : {}),
|
|
660
|
-
...(row.reason ? { reason: row.reason } : {}),
|
|
661
|
-
...(row.context ? { context: row.context } : {}),
|
|
662
|
-
...(row.approvalOptions ? { approvalOptions: row.approvalOptions } : {}),
|
|
663
|
-
...(row.policyVersion ? { policyVersion: row.policyVersion } : {}),
|
|
664
|
-
createdAt: new Date(toIsoString(row.createdAt, new Date().toISOString())),
|
|
665
|
-
...(row.expiresAt ? { expiresAt: new Date(toIsoString(row.expiresAt, new Date().toISOString())) } : {}),
|
|
666
|
-
...(row.resolvedAt ? { resolvedAt: new Date(toIsoString(row.resolvedAt, new Date().toISOString())) } : {}),
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
function normalizeApprovalUpdate(patch) {
|
|
670
|
-
const next = {};
|
|
671
|
-
for (const key of [
|
|
672
|
-
'session',
|
|
673
|
-
'toolCallId',
|
|
674
|
-
'toolName',
|
|
675
|
-
'target',
|
|
676
|
-
'action',
|
|
677
|
-
'risk',
|
|
678
|
-
'status',
|
|
679
|
-
'assignedTo',
|
|
680
|
-
'decisionBy',
|
|
681
|
-
'decisionRole',
|
|
682
|
-
'onBehalfOf',
|
|
683
|
-
'reason',
|
|
684
|
-
'context',
|
|
685
|
-
'approvalOptions',
|
|
686
|
-
'policyVersion',
|
|
687
|
-
]) {
|
|
688
|
-
if (patch[key] !== undefined) {
|
|
689
|
-
;
|
|
690
|
-
next[key] = patch[key];
|
|
691
|
-
}
|
|
692
895
|
}
|
|
693
|
-
if (patch.createdAt !== undefined)
|
|
694
|
-
next.createdAt = new Date(toIsoString(patch.createdAt, new Date().toISOString()));
|
|
695
|
-
if (patch.expiresAt !== undefined)
|
|
696
|
-
next.expiresAt = new Date(toIsoString(patch.expiresAt, new Date().toISOString()));
|
|
697
|
-
if (patch.resolvedAt !== undefined)
|
|
698
|
-
next.resolvedAt = new Date(toIsoString(patch.resolvedAt, new Date().toISOString()));
|
|
699
|
-
return next;
|
|
700
|
-
}
|
|
701
|
-
function normalizeAuditInsert(row) {
|
|
702
896
|
return {
|
|
703
|
-
id:
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
...(row.wikiStatus ? { wikiStatus: row.wikiStatus } : {}),
|
|
728
|
-
...(row.tags ? { tags: truncatePodLiteral(row.tags, 500) } : {}),
|
|
729
|
-
...(row.source ? { source: row.source } : {}),
|
|
730
|
-
...(row.sourceHash ? { sourceHash: row.sourceHash } : {}),
|
|
731
|
-
...(row.compiledAt ? { compiledAt: new Date(toIsoString(row.compiledAt, new Date().toISOString())) } : {}),
|
|
732
|
-
...(row.compiledFrom ? { compiledFrom: row.compiledFrom } : {}),
|
|
733
|
-
...(row.related ? { related: row.related } : {}),
|
|
734
|
-
effect: row.effect,
|
|
735
|
-
...(row.riskCeiling ? { riskCeiling: row.riskCeiling } : {}),
|
|
736
|
-
...(row.policy ? { policy: truncatePodLiteral(row.policy, MAX_GRANT_POLICY_LENGTH) } : {}),
|
|
737
|
-
...(row.context ? { context: truncatePodLiteral(row.context, MAX_APPROVAL_CONTEXT_LENGTH) } : {}),
|
|
738
|
-
decisionBy: row.decisionBy,
|
|
739
|
-
decisionRole: row.decisionRole,
|
|
740
|
-
...(row.onBehalfOf ? { onBehalfOf: row.onBehalfOf } : {}),
|
|
741
|
-
createdAt: new Date(toIsoString(row.createdAt, new Date().toISOString())),
|
|
742
|
-
...(row.revokedAt ? { revokedAt: new Date(toIsoString(row.revokedAt, new Date().toISOString())) } : {}),
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
function normalizeInboxNotificationInsert(row) {
|
|
746
|
-
return {
|
|
747
|
-
id: row.id,
|
|
748
|
-
...(row.actor ? { actor: row.actor } : {}),
|
|
749
|
-
object: row.object,
|
|
750
|
-
createdAt: new Date(toIsoString(row.createdAt, new Date().toISOString())),
|
|
897
|
+
id: subjectIdFromResourceUrl(url),
|
|
898
|
+
target,
|
|
899
|
+
action,
|
|
900
|
+
title: firstLiteral(predicates, GrantVocab.title),
|
|
901
|
+
summary: firstLiteral(predicates, GrantVocab.summary),
|
|
902
|
+
body: firstLiteral(predicates, GrantVocab.body),
|
|
903
|
+
schema: firstIri(predicates, GrantVocab.schema),
|
|
904
|
+
pageKind: firstLiteral(predicates, GrantVocab.pageKind),
|
|
905
|
+
wikiStatus: firstLiteral(predicates, GrantVocab.wikiStatus),
|
|
906
|
+
tags: firstLiteral(predicates, GrantVocab.tags),
|
|
907
|
+
source: firstLiteral(predicates, GrantVocab.source),
|
|
908
|
+
sourceHash: firstLiteral(predicates, GrantVocab.sourceHash),
|
|
909
|
+
compiledAt: firstLiteral(predicates, GrantVocab.compiledAt),
|
|
910
|
+
compiledFrom: iriValues(predicates, GrantVocab.compiledFrom),
|
|
911
|
+
related: iriValues(predicates, GrantVocab.related),
|
|
912
|
+
effect,
|
|
913
|
+
riskCeiling: firstLiteral(predicates, GrantVocab.riskCeiling),
|
|
914
|
+
policy: firstLiteral(predicates, GrantVocab.policy),
|
|
915
|
+
context: firstLiteral(predicates, GrantVocab.context),
|
|
916
|
+
decisionBy,
|
|
917
|
+
decisionRole,
|
|
918
|
+
onBehalfOf: firstIri(predicates, GrantVocab.onBehalfOf),
|
|
919
|
+
createdAt,
|
|
920
|
+
revokedAt: firstLiteral(predicates, GrantVocab.revokedAt),
|
|
751
921
|
};
|
|
752
922
|
}
|
|
753
923
|
function isActiveAllowGrant(grant) {
|
|
@@ -790,7 +960,7 @@ async function resolveSemanticGrantDecision(options) {
|
|
|
790
960
|
if (candidates.length === 0) {
|
|
791
961
|
return null;
|
|
792
962
|
}
|
|
793
|
-
const resolver = options.runtime.resolveGrantCoverage ??
|
|
963
|
+
const resolver = options.runtime.resolveGrantCoverage ?? resolveAutoModeGrantCoverage;
|
|
794
964
|
for (const grant of candidates) {
|
|
795
965
|
const coverage = await resolver({
|
|
796
966
|
record: options.record,
|
|
@@ -804,7 +974,7 @@ async function resolveSemanticGrantDecision(options) {
|
|
|
804
974
|
}
|
|
805
975
|
return null;
|
|
806
976
|
}
|
|
807
|
-
function
|
|
977
|
+
function buildAutoModeGrantRequestContext(input) {
|
|
808
978
|
return {
|
|
809
979
|
session: buildThreadUri(input.webId, input.record),
|
|
810
980
|
target: buildThreadUri(input.webId, input.record),
|
|
@@ -827,7 +997,7 @@ function buildGenericGrantRequestContext(input) {
|
|
|
827
997
|
kind: input.request.kind,
|
|
828
998
|
};
|
|
829
999
|
}
|
|
830
|
-
export async function
|
|
1000
|
+
export async function createRemoteAutoModeApproval(options) {
|
|
831
1001
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
832
1002
|
return createRemoteApproval({
|
|
833
1003
|
subject: ({ webId }) => ({
|
|
@@ -861,12 +1031,10 @@ export async function createRemoteApproval(options) {
|
|
|
861
1031
|
const request = typeof options.request === 'function'
|
|
862
1032
|
? options.request({ webId, stored, sessionUri: subject.sessionUri })
|
|
863
1033
|
: options.request;
|
|
864
|
-
const
|
|
1034
|
+
const approvalId = crypto.randomUUID();
|
|
865
1035
|
const now = activeRuntime.now();
|
|
866
|
-
const approvalReference = store.resolveApprovalReference({ id: approvalLocalId, createdAt: now });
|
|
867
|
-
const approvalId = approvalReference.id;
|
|
868
1036
|
const sessionUri = subject.sessionUri;
|
|
869
|
-
const approvalUri =
|
|
1037
|
+
const approvalUri = buildApprovalUriForDate(webId, approvalId, now);
|
|
870
1038
|
const targetUri = subject.targetUri ?? sessionUri;
|
|
871
1039
|
const assignedTo = subject.assignedTo ?? webId;
|
|
872
1040
|
const onBehalfOf = subject.onBehalfOf ?? webId;
|
|
@@ -877,7 +1045,6 @@ export async function createRemoteApproval(options) {
|
|
|
877
1045
|
const context = compactApprovalContext(request);
|
|
878
1046
|
await store.insertApproval({
|
|
879
1047
|
id: approvalId,
|
|
880
|
-
approvalUri,
|
|
881
1048
|
session: sessionUri,
|
|
882
1049
|
toolCallId: request.toolCallId,
|
|
883
1050
|
toolName: request.toolName,
|
|
@@ -932,7 +1099,7 @@ export async function createRemoteApproval(options) {
|
|
|
932
1099
|
});
|
|
933
1100
|
});
|
|
934
1101
|
}
|
|
935
|
-
export async function
|
|
1102
|
+
export async function waitForRemoteAutoModeApproval(options) {
|
|
936
1103
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
937
1104
|
return withRemoteApprovalStore(activeRuntime, async ({ store }) => {
|
|
938
1105
|
while (true) {
|
|
@@ -955,7 +1122,7 @@ export async function waitForRemoteWatchApproval(options) {
|
|
|
955
1122
|
}
|
|
956
1123
|
});
|
|
957
1124
|
}
|
|
958
|
-
export async function
|
|
1125
|
+
export async function requestRemoteAutoModeApproval(options) {
|
|
959
1126
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
960
1127
|
const delegated = await withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
961
1128
|
const grants = await store.listGrants();
|
|
@@ -964,7 +1131,7 @@ export async function requestRemoteWatchApproval(options) {
|
|
|
964
1131
|
grants,
|
|
965
1132
|
record: options.record,
|
|
966
1133
|
request: options.request,
|
|
967
|
-
requestContext:
|
|
1134
|
+
requestContext: buildAutoModeGrantRequestContext({
|
|
968
1135
|
webId,
|
|
969
1136
|
record: options.record,
|
|
970
1137
|
request: options.request,
|
|
@@ -974,12 +1141,12 @@ export async function requestRemoteWatchApproval(options) {
|
|
|
974
1141
|
if (delegated) {
|
|
975
1142
|
return delegated;
|
|
976
1143
|
}
|
|
977
|
-
const summary = await
|
|
1144
|
+
const summary = await createRemoteAutoModeApproval({
|
|
978
1145
|
record: options.record,
|
|
979
1146
|
request: options.request,
|
|
980
1147
|
runtime: activeRuntime,
|
|
981
1148
|
});
|
|
982
|
-
return
|
|
1149
|
+
return waitForRemoteAutoModeApproval({
|
|
983
1150
|
approvalId: summary.id,
|
|
984
1151
|
approvalUri: summary.approvalUri,
|
|
985
1152
|
pollMs: options.pollMs,
|
|
@@ -987,7 +1154,7 @@ export async function requestRemoteWatchApproval(options) {
|
|
|
987
1154
|
runtime: activeRuntime,
|
|
988
1155
|
});
|
|
989
1156
|
}
|
|
990
|
-
export async function
|
|
1157
|
+
export async function resolveExistingRemoteAutoModeGrant(options) {
|
|
991
1158
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
992
1159
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
993
1160
|
const grants = await store.listGrants();
|
|
@@ -996,7 +1163,7 @@ export async function resolveExistingRemoteWatchGrant(options) {
|
|
|
996
1163
|
grants,
|
|
997
1164
|
record: options.record,
|
|
998
1165
|
request: options.request,
|
|
999
|
-
requestContext:
|
|
1166
|
+
requestContext: buildAutoModeGrantRequestContext({
|
|
1000
1167
|
webId,
|
|
1001
1168
|
record: options.record,
|
|
1002
1169
|
request: options.request,
|
|
@@ -1034,7 +1201,7 @@ export async function requestRemoteApproval(options) {
|
|
|
1034
1201
|
request: options.request,
|
|
1035
1202
|
runtime: activeRuntime,
|
|
1036
1203
|
});
|
|
1037
|
-
return
|
|
1204
|
+
return waitForRemoteAutoModeApproval({
|
|
1038
1205
|
approvalId: summary.id,
|
|
1039
1206
|
approvalUri: summary.approvalUri,
|
|
1040
1207
|
pollMs: options.pollMs,
|
|
@@ -1042,7 +1209,7 @@ export async function requestRemoteApproval(options) {
|
|
|
1042
1209
|
runtime: activeRuntime,
|
|
1043
1210
|
});
|
|
1044
1211
|
}
|
|
1045
|
-
export async function
|
|
1212
|
+
export async function listRemoteAutoModeApprovals(options = {}) {
|
|
1046
1213
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
1047
1214
|
const requestedStatus = options.status ?? 'pending';
|
|
1048
1215
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
@@ -1054,7 +1221,7 @@ export async function listRemoteWatchApprovals(options = {}) {
|
|
|
1054
1221
|
.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
1055
1222
|
});
|
|
1056
1223
|
}
|
|
1057
|
-
export async function
|
|
1224
|
+
export async function resolveRemoteAutoModeApproval(options) {
|
|
1058
1225
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
1059
1226
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
1060
1227
|
const row = await readRemoteApprovalRow(store, {
|
|
@@ -1068,20 +1235,21 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
1068
1235
|
return normalizeApprovalSummary(row);
|
|
1069
1236
|
}
|
|
1070
1237
|
const now = activeRuntime.now();
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1238
|
+
const approvalCreatedAt = new Date(toIsoString(row.createdAt, now.toISOString()));
|
|
1239
|
+
const approvalUri = buildApprovalUriForDate(row.session, row.id, approvalCreatedAt);
|
|
1073
1240
|
const nextStatus = options.decision === 'accept' || options.decision === 'accept_for_session'
|
|
1074
1241
|
? 'approved'
|
|
1075
1242
|
: 'rejected';
|
|
1076
1243
|
const decisionRole = options.decisionRole ?? 'human';
|
|
1077
1244
|
await store.updateApproval(row.id, {
|
|
1078
|
-
approvalUri,
|
|
1079
1245
|
status: nextStatus,
|
|
1080
1246
|
decisionBy: webId,
|
|
1081
1247
|
decisionRole,
|
|
1082
1248
|
onBehalfOf: webId,
|
|
1083
1249
|
reason: encodeDecisionReason(options.decision, options.note),
|
|
1084
1250
|
resolvedAt: now,
|
|
1251
|
+
}, {
|
|
1252
|
+
resourceUri: options.approvalUri ?? row.approvalUri ?? approvalUri,
|
|
1085
1253
|
});
|
|
1086
1254
|
await warnOnly(activeRuntime, () => store.insertAudit({
|
|
1087
1255
|
id: crypto.randomUUID(),
|
|
@@ -1128,7 +1296,7 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
1128
1296
|
await warnOnly(activeRuntime, () => store.insertInboxNotification({
|
|
1129
1297
|
id: crypto.randomUUID(),
|
|
1130
1298
|
actor: webId,
|
|
1131
|
-
object:
|
|
1299
|
+
object: buildGrantUri(row.session, grantId),
|
|
1132
1300
|
createdAt: now,
|
|
1133
1301
|
}));
|
|
1134
1302
|
}
|
|
@@ -1140,7 +1308,6 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
1140
1308
|
}));
|
|
1141
1309
|
const nextRow = {
|
|
1142
1310
|
...row,
|
|
1143
|
-
approvalUri,
|
|
1144
1311
|
status: nextStatus,
|
|
1145
1312
|
decisionBy: webId,
|
|
1146
1313
|
decisionRole,
|
|
@@ -1154,9 +1321,11 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
1154
1321
|
async function readRemoteApprovalRow(store, options) {
|
|
1155
1322
|
if (store.findApproval) {
|
|
1156
1323
|
const row = await store.findApproval(options.approvalId, {
|
|
1157
|
-
|
|
1324
|
+
resourceUri: options.approvalUri,
|
|
1158
1325
|
});
|
|
1159
|
-
|
|
1326
|
+
if (row || options.approvalUri) {
|
|
1327
|
+
return row;
|
|
1328
|
+
}
|
|
1160
1329
|
}
|
|
1161
1330
|
const approvals = await store.listApprovals();
|
|
1162
1331
|
return approvals.find((entry) => entry.id === options.approvalId) ?? null;
|
|
@@ -1167,6 +1336,7 @@ export const __podApprovalInternal = {
|
|
|
1167
1336
|
buildActionUri,
|
|
1168
1337
|
buildRisk,
|
|
1169
1338
|
buildToolName,
|
|
1339
|
+
createSharedModelRemoteApprovalStore,
|
|
1170
1340
|
createNativeRemoteApprovalStore,
|
|
1171
1341
|
extractToolCallId,
|
|
1172
1342
|
decisionFromApprovalRow,
|