@undefineds.co/linx 0.2.15 → 0.2.17
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/generated/version.js +3 -0
- package/dist/generated/version.js.map +1 -0
- package/dist/index.js +51 -225
- package/dist/index.js.map +1 -1
- package/dist/lib/account-api.js +1 -1
- package/dist/lib/account-api.js.map +1 -1
- package/dist/lib/account-session.js +1 -1
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +59 -32
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/chat-api.js +19 -177
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/codex-plugin/index.js.map +1 -1
- package/dist/lib/codex-plugin/runner.js.map +1 -1
- package/dist/lib/credentials-store.js +1 -7
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/default-model.js +0 -1
- package/dist/lib/default-model.js.map +1 -1
- package/dist/lib/login-command.js +2 -17
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +27 -2
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +13 -78
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auth.js +5 -12
- package/dist/lib/pi-adapter/auth.js.map +1 -1
- package/dist/lib/pi-adapter/branding.js +75 -553
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/index.js.map +1 -1
- package/dist/lib/pi-adapter/interactive.js +4 -154
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/runtime.js +23 -138
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +4 -154
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/theme.js.map +1 -1
- package/dist/lib/pod-chat-store.js +1 -1
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/profile-identity.js +60 -16
- package/dist/lib/profile-identity.js.map +1 -1
- package/dist/lib/prompt.js.map +1 -1
- package/dist/lib/runtime-target.js +1 -1
- package/dist/lib/runtime-target.js.map +1 -1
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/thread-utils.js.map +1 -1
- package/dist/lib/watch/archive.js +1 -1
- package/dist/lib/watch/archive.js.map +1 -1
- package/dist/lib/watch/auth.js +1 -1
- package/dist/lib/watch/auth.js.map +1 -1
- package/dist/lib/watch/codex-composer.js.map +1 -1
- package/dist/lib/watch/codex-footer.js.map +1 -1
- package/dist/lib/watch/codex-overlay.js.map +1 -1
- package/dist/lib/watch/codex-request-form.js +1 -1
- package/dist/lib/watch/codex-request-form.js.map +1 -1
- package/dist/lib/watch/codex-request-input.js.map +1 -1
- package/dist/lib/watch/display.js.map +1 -1
- package/dist/lib/watch/format.js.map +1 -1
- package/dist/lib/watch/hooks/claude.js.map +1 -1
- package/dist/lib/watch/hooks/codebuddy.js.map +1 -1
- package/dist/lib/watch/hooks/codex.js.map +1 -1
- package/dist/lib/watch/hooks/index.js.map +1 -1
- package/dist/lib/watch/hooks/shared.js +1 -0
- package/dist/lib/watch/hooks/shared.js.map +1 -1
- package/dist/lib/watch/index.js.map +1 -1
- package/dist/lib/watch/pod-ai.js +32 -16
- package/dist/lib/watch/pod-ai.js.map +1 -1
- package/dist/lib/watch/pod-approval.js +203 -481
- package/dist/lib/watch/pod-approval.js.map +1 -1
- package/dist/lib/watch/pod-persistence.js +37 -24
- package/dist/lib/watch/pod-persistence.js.map +1 -1
- package/dist/lib/watch/runner.js +1 -4
- package/dist/lib/watch/runner.js.map +1 -1
- package/dist/lib/watch/types.js.map +1 -1
- package/dist/skills/drizzle-solid/SKILL.md +340 -0
- package/dist/skills/pod-storage/SKILL.md +60 -0
- package/dist/skills/solid-modeling/SKILL.md +274 -0
- package/dist/skills/xpod-componentsjs/SKILL.md +284 -0
- package/dist/watch-cli.js.map +1 -1
- package/package.json +10 -3
- package/vendor/client/dist/client/index.d.ts +118 -0
- package/vendor/client/dist/client/index.js +260 -0
- package/vendor/client/dist/index.d.ts +1 -0
- package/vendor/client/dist/index.js +1 -0
- package/vendor/client/dist/watch/index.d.ts +226 -0
- package/vendor/client/dist/watch/index.js +1114 -0
- package/vendor/client/package.json +9 -0
- package/dist/lib/node-warning-filter.js +0 -34
- package/dist/lib/node-warning-filter.js.map +0 -1
- package/dist/lib/pi-adapter/pod-approval.js +0 -8
- package/dist/lib/pi-adapter/pod-approval.js.map +0 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +0 -189
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +0 -1
- package/dist/lib/pi-adapter/pod-mirror.js +0 -334
- package/dist/lib/pi-adapter/pod-mirror.js.map +0 -1
- package/dist/lib/pi-adapter/pod-native.js +0 -477
- package/dist/lib/pi-adapter/pod-native.js.map +0 -1
- package/dist/lib/pi-adapter/session.js +0 -727
- package/dist/lib/pi-adapter/session.js.map +0 -1
- package/dist/lib/pod-data-session.js +0 -70
- package/dist/lib/pod-data-session.js.map +0 -1
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
-
import { getDefaultPodDataSession } from '../pod-data-session.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';
|
|
6
2
|
const WATCH_CHAT_ID = 'linx-watch';
|
|
7
3
|
const WATCH_AGENT_ID = 'linx-watch-assistant';
|
|
8
4
|
const REMOTE_APPROVAL_POLICY_VERSION = 'linx-watch-remote-approval/v1';
|
|
9
5
|
const DEFAULT_REMOTE_APPROVAL_POLL_MS = 1000;
|
|
10
|
-
const remoteApprovalClientCache = new WeakMap();
|
|
11
6
|
function createAbortError() {
|
|
12
7
|
const error = new Error('The operation was aborted.');
|
|
13
8
|
error.name = 'AbortError';
|
|
14
9
|
return error;
|
|
15
10
|
}
|
|
11
|
+
async function dynamicImport(specifier) {
|
|
12
|
+
const loader = new Function('modulePath', 'return import(modulePath)');
|
|
13
|
+
return loader(specifier);
|
|
14
|
+
}
|
|
16
15
|
function normalizeString(value) {
|
|
17
16
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
18
17
|
}
|
|
@@ -42,19 +41,10 @@ function buildThreadUri(webId, threadId) {
|
|
|
42
41
|
return `${getPodBaseUrl(webId)}/.data/chat/${WATCH_CHAT_ID}/index.ttl#${threadId}`;
|
|
43
42
|
}
|
|
44
43
|
function buildApprovalUri(webIdOrUri, approvalId) {
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
function buildApprovalUriForDate(webIdOrUri, approvalId, createdAt) {
|
|
48
|
-
return buildApprovalResourceUrl(webIdOrUri, approvalId, createdAt);
|
|
49
|
-
}
|
|
50
|
-
function documentUrlFromResourceUri(resourceUri) {
|
|
51
|
-
return resourceUri.split('#', 1)[0] ?? resourceUri;
|
|
44
|
+
return `${getPodBaseUrl(webIdOrUri)}/.data/approvals/${approvalId}.ttl`;
|
|
52
45
|
}
|
|
53
46
|
function buildGrantUri(webIdOrUri, grantId) {
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
function buildGrantDocumentUrl(webIdOrUri) {
|
|
57
|
-
return `${getPodBaseUrl(webIdOrUri)}/settings/autonomy/grants.ttl`;
|
|
47
|
+
return `${getPodBaseUrl(webIdOrUri)}/settings/autonomy/grants/${grantId}.ttl`;
|
|
58
48
|
}
|
|
59
49
|
function buildAgentUri(webId) {
|
|
60
50
|
return `${getPodBaseUrl(webId)}/.data/agents/${WATCH_AGENT_ID}.ttl`;
|
|
@@ -113,6 +103,16 @@ function buildRequestMessage(request) {
|
|
|
113
103
|
}
|
|
114
104
|
return request.message;
|
|
115
105
|
}
|
|
106
|
+
function buildRequestAuditContext(record, request) {
|
|
107
|
+
return {
|
|
108
|
+
kind: request.kind,
|
|
109
|
+
message: buildRequestMessage(request),
|
|
110
|
+
...(request.kind === 'command-approval' && request.command ? { command: request.command } : {}),
|
|
111
|
+
...(request.kind === 'command-approval' && request.cwd ? { cwd: request.cwd } : {}),
|
|
112
|
+
backend: record.backend,
|
|
113
|
+
sessionId: record.id,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
116
|
function extractToolCallId(request) {
|
|
117
117
|
if (!isRecord(request.raw)) {
|
|
118
118
|
return crypto.randomUUID();
|
|
@@ -124,7 +124,7 @@ function extractToolCallId(request) {
|
|
|
124
124
|
?? crypto.randomUUID();
|
|
125
125
|
}
|
|
126
126
|
function encodeDecisionReason(decision, note) {
|
|
127
|
-
return
|
|
127
|
+
return JSON.stringify({
|
|
128
128
|
decision,
|
|
129
129
|
...(note?.trim() ? { note: note.trim() } : {}),
|
|
130
130
|
});
|
|
@@ -151,25 +151,33 @@ function parseDecisionReason(value) {
|
|
|
151
151
|
return null;
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
if (runtime.onWarning) {
|
|
160
|
-
runtime.onWarning(error);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
164
|
-
process.emitWarning(`LinX Pod sync failed: ${message}`);
|
|
154
|
+
function parseRequestAuditContext(value) {
|
|
155
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
156
|
+
return null;
|
|
165
157
|
}
|
|
166
|
-
}
|
|
167
|
-
function safeJsonStringify(value) {
|
|
168
158
|
try {
|
|
169
|
-
|
|
159
|
+
const parsed = JSON.parse(value);
|
|
160
|
+
if (!isRecord(parsed)) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const kind = normalizeString(parsed.kind);
|
|
164
|
+
const message = normalizeString(parsed.message);
|
|
165
|
+
const backend = normalizeString(parsed.backend);
|
|
166
|
+
const sessionId = normalizeString(parsed.sessionId);
|
|
167
|
+
if (!kind || !message || !backend || !sessionId) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
kind: kind,
|
|
172
|
+
message,
|
|
173
|
+
backend: backend,
|
|
174
|
+
sessionId,
|
|
175
|
+
...(normalizeString(parsed.command) ? { command: normalizeString(parsed.command) } : {}),
|
|
176
|
+
...(normalizeString(parsed.cwd) ? { cwd: normalizeString(parsed.cwd) } : {}),
|
|
177
|
+
};
|
|
170
178
|
}
|
|
171
179
|
catch {
|
|
172
|
-
return
|
|
180
|
+
return null;
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
function extractSessionId(sessionUri) {
|
|
@@ -192,20 +200,29 @@ function decisionFromApprovalRow(row) {
|
|
|
192
200
|
}
|
|
193
201
|
return 'accept';
|
|
194
202
|
}
|
|
195
|
-
function
|
|
203
|
+
function requestAuditForApproval(approvalUri, audits) {
|
|
204
|
+
const matches = audits.filter((audit) => audit.approval === approvalUri && audit.action === 'approval_requested');
|
|
205
|
+
matches.sort((left, right) => toIsoString(right.createdAt, '').localeCompare(toIsoString(left.createdAt, '')));
|
|
206
|
+
return matches[0];
|
|
207
|
+
}
|
|
208
|
+
function normalizeApprovalSummary(row, audits) {
|
|
209
|
+
const approvalUri = buildApprovalUri(row.session, row.id);
|
|
210
|
+
const requestAudit = requestAuditForApproval(approvalUri, audits);
|
|
211
|
+
const requestContext = parseRequestAuditContext(requestAudit?.context);
|
|
196
212
|
const createdAt = toIsoString(row.createdAt, new Date(0).toISOString());
|
|
197
213
|
const sessionUri = row.session;
|
|
198
214
|
const decision = decisionFromApprovalRow(row);
|
|
199
215
|
return {
|
|
200
216
|
id: row.id,
|
|
201
|
-
...(normalizeString(row.approvalUri) ? { approvalUri: normalizeString(row.approvalUri) } : {}),
|
|
202
217
|
sessionId: extractSessionId(sessionUri),
|
|
203
218
|
sessionUri,
|
|
204
219
|
toolCallId: row.toolCallId,
|
|
205
220
|
toolName: row.toolName,
|
|
206
221
|
risk: normalizeString(row.risk) ?? 'medium',
|
|
207
222
|
status: normalizeString(row.status) ?? 'pending',
|
|
208
|
-
message:
|
|
223
|
+
message: requestContext?.message ?? row.toolName,
|
|
224
|
+
...(requestContext?.command ? { command: requestContext.command } : {}),
|
|
225
|
+
...(requestContext?.cwd ? { cwd: requestContext.cwd } : {}),
|
|
209
226
|
...(normalizeString(row.assignedTo) ? { assignedTo: normalizeString(row.assignedTo) } : {}),
|
|
210
227
|
...(normalizeString(row.decisionBy) ? { decisionBy: normalizeString(row.decisionBy) } : {}),
|
|
211
228
|
...(decision ? { decision } : {}),
|
|
@@ -213,18 +230,6 @@ function normalizeApprovalSummary(row) {
|
|
|
213
230
|
...(row.resolvedAt ? { resolvedAt: toIsoString(row.resolvedAt, createdAt) } : {}),
|
|
214
231
|
};
|
|
215
232
|
}
|
|
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
|
-
}
|
|
228
233
|
function formatSummaryHeadline(summary) {
|
|
229
234
|
return `${summary.id} | ${summary.status} | ${summary.risk} | session=${summary.sessionId}`;
|
|
230
235
|
}
|
|
@@ -243,11 +248,72 @@ export function isRemoteApprovalAbortError(error) {
|
|
|
243
248
|
function missingRemoteApprovalCredentialsMessage() {
|
|
244
249
|
return 'LinX remote approval requires `linx login` first.';
|
|
245
250
|
}
|
|
251
|
+
function unsupportedRemoteApprovalAuthMessage() {
|
|
252
|
+
return 'LinX remote approval requires client credentials auth in `~/.linx`.';
|
|
253
|
+
}
|
|
246
254
|
async function createDefaultRuntime() {
|
|
255
|
+
const [credentialsStore, solidAuth, models] = await Promise.all([
|
|
256
|
+
dynamicImport(new URL('../credentials-store.js', import.meta.url).href),
|
|
257
|
+
dynamicImport(new URL('../solid-auth.js', import.meta.url).href),
|
|
258
|
+
dynamicImport(new URL('...co/models.js', import.meta.url).href),
|
|
259
|
+
]);
|
|
247
260
|
return {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
261
|
+
loadCredentials: credentialsStore.loadCredentials,
|
|
262
|
+
getClientCredentials: credentialsStore.getClientCredentials,
|
|
263
|
+
authenticate: solidAuth.authenticate,
|
|
264
|
+
createStore(session) {
|
|
265
|
+
const db = models.drizzle(session, {
|
|
266
|
+
logger: false,
|
|
267
|
+
disableInteropDiscovery: true,
|
|
268
|
+
schema: models.solidSchema,
|
|
269
|
+
});
|
|
270
|
+
let initialized = false;
|
|
271
|
+
async function ensureInitialized() {
|
|
272
|
+
if (initialized) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
initialized = true;
|
|
276
|
+
await db.init([
|
|
277
|
+
models.approvalTable,
|
|
278
|
+
models.auditTable,
|
|
279
|
+
models.grantTable,
|
|
280
|
+
models.inboxNotificationTable,
|
|
281
|
+
]).catch(() => undefined);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
async listApprovals() {
|
|
285
|
+
await ensureInitialized();
|
|
286
|
+
return await db.select().from(models.approvalTable).execute();
|
|
287
|
+
},
|
|
288
|
+
async insertApproval(row) {
|
|
289
|
+
await ensureInitialized();
|
|
290
|
+
await db.insert(models.approvalTable).values(row).execute();
|
|
291
|
+
},
|
|
292
|
+
async updateApproval(id, patch) {
|
|
293
|
+
await ensureInitialized();
|
|
294
|
+
await db.update(models.approvalTable).set(patch).where(models.eq(models.approvalTable.id, id)).execute();
|
|
295
|
+
},
|
|
296
|
+
async listAudits() {
|
|
297
|
+
await ensureInitialized();
|
|
298
|
+
return await db.select().from(models.auditTable).execute();
|
|
299
|
+
},
|
|
300
|
+
async insertAudit(row) {
|
|
301
|
+
await ensureInitialized();
|
|
302
|
+
await db.insert(models.auditTable).values(row).execute();
|
|
303
|
+
},
|
|
304
|
+
async listGrants() {
|
|
305
|
+
await ensureInitialized();
|
|
306
|
+
return await db.select().from(models.grantTable).execute();
|
|
307
|
+
},
|
|
308
|
+
async insertGrant(row) {
|
|
309
|
+
await ensureInitialized();
|
|
310
|
+
await db.insert(models.grantTable).values(row).execute();
|
|
311
|
+
},
|
|
312
|
+
async insertInboxNotification(row) {
|
|
313
|
+
await ensureInitialized();
|
|
314
|
+
await db.insert(models.inboxNotificationTable).values(row).execute();
|
|
315
|
+
},
|
|
316
|
+
};
|
|
251
317
|
},
|
|
252
318
|
sleep(ms) {
|
|
253
319
|
return delay(ms);
|
|
@@ -258,395 +324,97 @@ async function createDefaultRuntime() {
|
|
|
258
324
|
};
|
|
259
325
|
}
|
|
260
326
|
async function withRemoteApprovalStore(runtime, fn) {
|
|
261
|
-
const
|
|
262
|
-
if (!
|
|
327
|
+
const stored = runtime.loadCredentials();
|
|
328
|
+
if (!stored) {
|
|
263
329
|
throw new Error(missingRemoteApprovalCredentialsMessage());
|
|
264
330
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
stored: client.session.credentials,
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
async function getRemoteApprovalClient(runtime) {
|
|
272
|
-
let promise = remoteApprovalClientCache.get(runtime);
|
|
273
|
-
if (!promise) {
|
|
274
|
-
promise = createRemoteApprovalClient(runtime)
|
|
275
|
-
.then((client) => {
|
|
276
|
-
if (!client) {
|
|
277
|
-
remoteApprovalClientCache.delete(runtime);
|
|
278
|
-
}
|
|
279
|
-
return client;
|
|
280
|
-
})
|
|
281
|
-
.catch((error) => {
|
|
282
|
-
remoteApprovalClientCache.delete(runtime);
|
|
283
|
-
throw error;
|
|
284
|
-
});
|
|
285
|
-
remoteApprovalClientCache.set(runtime, promise);
|
|
286
|
-
}
|
|
287
|
-
return promise;
|
|
288
|
-
}
|
|
289
|
-
async function createRemoteApprovalClient(runtime) {
|
|
290
|
-
const session = await runtime.getPodDataSession();
|
|
291
|
-
if (!session) {
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
return {
|
|
295
|
-
session,
|
|
296
|
-
store: runtime.createStore(session.webId, session.fetch),
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
function createNativeRemoteApprovalStore(webId, fetcher) {
|
|
300
|
-
return {
|
|
301
|
-
listApprovals: () => listApprovalRows(webId, fetcher),
|
|
302
|
-
findApproval: (id, options) => findApprovalRow(webId, fetcher, id, options),
|
|
303
|
-
insertApproval: (row) => writeApprovalRow(webId, fetcher, row),
|
|
304
|
-
async updateApproval(id, patch) {
|
|
305
|
-
const existing = (await listApprovalRows(webId, fetcher)).find((row) => row.id === id);
|
|
306
|
-
if (!existing) {
|
|
307
|
-
throw new Error(`Remote approval not found: ${id}`);
|
|
308
|
-
}
|
|
309
|
-
await writeApprovalRow(webId, fetcher, { ...existing, ...patch });
|
|
310
|
-
},
|
|
311
|
-
listAudits: () => listAuditRows(webId, fetcher),
|
|
312
|
-
insertAudit: (row) => writeAuditRow(webId, fetcher, row),
|
|
313
|
-
listGrants: () => listGrantRows(webId, fetcher),
|
|
314
|
-
insertGrant: (row) => writeGrantRow(webId, fetcher, row),
|
|
315
|
-
insertInboxNotification: (row) => writeInboxNotificationRow(webId, fetcher, row),
|
|
316
|
-
};
|
|
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));
|
|
331
|
+
const clientCredentials = runtime.getClientCredentials(stored);
|
|
332
|
+
if (!clientCredentials) {
|
|
333
|
+
throw new Error(unsupportedRemoteApprovalAuthMessage());
|
|
325
334
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
}
|
|
335
|
+
const { session } = await runtime.authenticate(clientCredentials.clientId, clientCredentials.clientSecret, stored.url);
|
|
336
|
+
const webId = session.info.webId ?? stored.webId;
|
|
337
|
+
if (!webId) {
|
|
338
|
+
await session.logout().catch(() => undefined);
|
|
339
|
+
throw new Error('Remote approval authentication succeeded without a WebID.');
|
|
341
340
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
]);
|
|
349
|
-
const urls = [...new Set([...currentUrls, ...legacyUrls])];
|
|
350
|
-
const rows = [];
|
|
351
|
-
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
352
|
-
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
353
|
-
if (!turtle)
|
|
354
|
-
continue;
|
|
355
|
-
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
356
|
-
const row = approvalRowFromPredicates(subject, predicates);
|
|
357
|
-
if (row)
|
|
358
|
-
rows.push(row);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return rows;
|
|
362
|
-
}
|
|
363
|
-
async function writeApprovalRow(webId, fetcher, row) {
|
|
364
|
-
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
365
|
-
const documentUrl = buildApprovalDocumentUrl(webId, createdAt);
|
|
366
|
-
const subjectUrl = buildApprovalResourceUrl(webId, row.id, createdAt);
|
|
367
|
-
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
368
|
-
subject: subjectUrl,
|
|
369
|
-
triples: [
|
|
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())) }] : []),
|
|
386
|
-
],
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
async function listAuditRows(webId, fetcher) {
|
|
390
|
-
const urls = await listTurtleResourcesRecursive(fetcher, `${getPodBaseUrl(webId)}/.data/audits/`);
|
|
391
|
-
const rows = [];
|
|
392
|
-
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
393
|
-
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
394
|
-
if (!turtle)
|
|
395
|
-
continue;
|
|
396
|
-
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
397
|
-
const row = auditRowFromPredicates(subject, predicates);
|
|
398
|
-
if (row)
|
|
399
|
-
rows.push(row);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return rows;
|
|
403
|
-
}
|
|
404
|
-
async function writeAuditRow(webId, fetcher, row) {
|
|
405
|
-
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
406
|
-
const documentUrl = buildAuditDocumentUrl(webId, createdAt);
|
|
407
|
-
const subjectUrl = buildAuditResourceUrl(webId, row.id, createdAt);
|
|
408
|
-
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
409
|
-
subject: subjectUrl,
|
|
410
|
-
triples: [
|
|
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())) },
|
|
423
|
-
],
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
async function listGrantRows(webId, fetcher) {
|
|
427
|
-
const urls = [
|
|
428
|
-
`${getPodBaseUrl(webId)}/settings/autonomy/grants.ttl`,
|
|
429
|
-
...await listTurtleResources(fetcher, `${getPodBaseUrl(webId)}/settings/autonomy/grants/`).catch(() => []),
|
|
430
|
-
];
|
|
431
|
-
const rows = [];
|
|
432
|
-
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
433
|
-
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
434
|
-
if (!turtle)
|
|
435
|
-
continue;
|
|
436
|
-
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
437
|
-
const row = grantRowFromPredicates(subject, predicates);
|
|
438
|
-
if (row)
|
|
439
|
-
rows.push(row);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return rows;
|
|
443
|
-
}
|
|
444
|
-
async function writeGrantRow(webId, fetcher, row) {
|
|
445
|
-
const id = normalizeString(row.id) ?? crypto.randomUUID();
|
|
446
|
-
const documentUrl = buildGrantDocumentUrl(webId);
|
|
447
|
-
const subjectUrl = buildGrantResourceUrl(webId, id);
|
|
448
|
-
const target = normalizeString(row.target);
|
|
449
|
-
const action = normalizeString(row.action);
|
|
450
|
-
const effect = normalizeString(row.effect);
|
|
451
|
-
const decisionBy = normalizeString(row.decisionBy);
|
|
452
|
-
const decisionRole = normalizeString(row.decisionRole);
|
|
453
|
-
if (!target || !action || !effect || !decisionBy || !decisionRole) {
|
|
454
|
-
throw new Error(`Invalid remote approval grant row: ${id}`);
|
|
455
|
-
}
|
|
456
|
-
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
457
|
-
subject: subjectUrl,
|
|
458
|
-
triples: [
|
|
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)) }] : []),
|
|
470
|
-
],
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
async function writeInboxNotificationRow(webId, fetcher, row) {
|
|
474
|
-
const url = buildInboxResourceUrl(webId, row.id);
|
|
475
|
-
await upsertManagedTurtleBlock(fetcher, url, {
|
|
476
|
-
subject: url,
|
|
477
|
-
triples: [
|
|
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())) },
|
|
482
|
-
],
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
function approvalRowFromPredicates(url, 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);
|
|
494
|
-
if (!session || !toolCallId || !toolName || !target || !action || !risk || !status || !createdAt) {
|
|
495
|
-
return null;
|
|
496
|
-
}
|
|
497
|
-
return {
|
|
498
|
-
id: subjectIdFromResourceUrl(url),
|
|
499
|
-
session,
|
|
500
|
-
toolCallId,
|
|
501
|
-
toolName,
|
|
502
|
-
target,
|
|
503
|
-
action,
|
|
504
|
-
risk,
|
|
505
|
-
status,
|
|
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),
|
|
512
|
-
createdAt,
|
|
513
|
-
resolvedAt: firstLiteral(predicates, ApprovalVocab.resolvedAt),
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
function auditRowFromPredicates(url, 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);
|
|
521
|
-
if (!action || !actor || !actorRole || !createdAt) {
|
|
522
|
-
return null;
|
|
341
|
+
try {
|
|
342
|
+
return await fn({
|
|
343
|
+
store: runtime.createStore(session),
|
|
344
|
+
webId,
|
|
345
|
+
stored,
|
|
346
|
+
});
|
|
523
347
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
action,
|
|
527
|
-
actor,
|
|
528
|
-
actorRole,
|
|
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),
|
|
536
|
-
createdAt,
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
function grantRowFromPredicates(url, 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);
|
|
546
|
-
if (!target || !action || !effect || !decisionBy || !decisionRole || !createdAt) {
|
|
547
|
-
return null;
|
|
348
|
+
finally {
|
|
349
|
+
await session.logout().catch(() => undefined);
|
|
548
350
|
}
|
|
549
|
-
return {
|
|
550
|
-
id: subjectIdFromResourceUrl(url),
|
|
551
|
-
target,
|
|
552
|
-
action,
|
|
553
|
-
effect,
|
|
554
|
-
riskCeiling: firstLiteral(predicates, GrantVocab.riskCeiling),
|
|
555
|
-
decisionBy,
|
|
556
|
-
decisionRole,
|
|
557
|
-
onBehalfOf: firstIri(predicates, GrantVocab.onBehalfOf),
|
|
558
|
-
createdAt,
|
|
559
|
-
revokedAt: firstLiteral(predicates, GrantVocab.revokedAt),
|
|
560
|
-
};
|
|
561
351
|
}
|
|
562
352
|
export async function createRemoteWatchApproval(options) {
|
|
563
353
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
564
|
-
return
|
|
565
|
-
subject: ({ webId }) => ({
|
|
566
|
-
sessionUri: buildThreadUri(webId, options.record.id),
|
|
567
|
-
actorUri: buildAgentUri(webId),
|
|
568
|
-
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
569
|
-
}),
|
|
570
|
-
request: ({ sessionUri }) => ({
|
|
571
|
-
kind: options.request.kind,
|
|
572
|
-
message: buildRequestMessage(options.request),
|
|
573
|
-
toolCallId: extractToolCallId(options.request),
|
|
574
|
-
toolName: buildToolName(options.request),
|
|
575
|
-
action: buildActionUri(options.request),
|
|
576
|
-
risk: buildRisk(options.request),
|
|
577
|
-
...(options.request.kind === 'command-approval' && options.request.command ? { command: options.request.command } : {}),
|
|
578
|
-
...(options.request.kind === 'command-approval' && options.request.cwd ? { cwd: options.request.cwd } : {}),
|
|
579
|
-
entry: sessionUri,
|
|
580
|
-
}),
|
|
581
|
-
runtime: activeRuntime,
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
export async function createRemoteApproval(options) {
|
|
585
|
-
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
586
|
-
return withRemoteApprovalStore(activeRuntime, async ({ store, webId, stored }) => {
|
|
587
|
-
const subject = typeof options.subject === 'function'
|
|
588
|
-
? options.subject({ webId, stored })
|
|
589
|
-
: options.subject;
|
|
590
|
-
const request = typeof options.request === 'function'
|
|
591
|
-
? options.request({ webId, stored, sessionUri: subject.sessionUri })
|
|
592
|
-
: options.request;
|
|
354
|
+
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
593
355
|
const approvalId = crypto.randomUUID();
|
|
594
356
|
const now = activeRuntime.now();
|
|
595
|
-
const sessionUri =
|
|
596
|
-
const approvalUri =
|
|
597
|
-
const
|
|
598
|
-
const
|
|
599
|
-
const onBehalfOf = subject.onBehalfOf ?? webId;
|
|
600
|
-
const policyVersion = subject.policyVersion ?? REMOTE_APPROVAL_POLICY_VERSION;
|
|
601
|
-
const requestEntry = request.entry ?? approvalUri;
|
|
357
|
+
const sessionUri = buildThreadUri(webId, options.record.id);
|
|
358
|
+
const approvalUri = buildApprovalUri(webId, approvalId);
|
|
359
|
+
const toolCallId = extractToolCallId(options.request);
|
|
360
|
+
const requestContext = buildRequestAuditContext(options.record, options.request);
|
|
602
361
|
await store.insertApproval({
|
|
603
362
|
id: approvalId,
|
|
604
363
|
session: sessionUri,
|
|
605
|
-
toolCallId
|
|
606
|
-
toolName: request
|
|
607
|
-
target:
|
|
608
|
-
action: request
|
|
609
|
-
risk: request
|
|
364
|
+
toolCallId,
|
|
365
|
+
toolName: buildToolName(options.request),
|
|
366
|
+
target: sessionUri,
|
|
367
|
+
action: buildActionUri(options.request),
|
|
368
|
+
risk: buildRisk(options.request),
|
|
610
369
|
status: 'pending',
|
|
611
|
-
assignedTo,
|
|
612
|
-
policyVersion,
|
|
370
|
+
assignedTo: webId,
|
|
371
|
+
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
613
372
|
createdAt: now,
|
|
614
373
|
});
|
|
615
|
-
|
|
374
|
+
await store.insertAudit({
|
|
616
375
|
id: crypto.randomUUID(),
|
|
617
376
|
action: 'approval_requested',
|
|
618
|
-
actor:
|
|
377
|
+
actor: buildAgentUri(webId),
|
|
619
378
|
actorRole: 'secretary',
|
|
620
|
-
onBehalfOf,
|
|
379
|
+
onBehalfOf: webId,
|
|
621
380
|
session: sessionUri,
|
|
622
|
-
|
|
623
|
-
toolCallId: request.toolCallId,
|
|
624
|
-
toolName: request.toolName,
|
|
381
|
+
toolCallId,
|
|
625
382
|
approval: approvalUri,
|
|
626
|
-
|
|
383
|
+
context: JSON.stringify(requestContext),
|
|
384
|
+
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
627
385
|
createdAt: now,
|
|
628
|
-
};
|
|
629
|
-
await
|
|
630
|
-
await warnOnly(activeRuntime, () => store.insertInboxNotification({
|
|
386
|
+
});
|
|
387
|
+
await store.insertInboxNotification({
|
|
631
388
|
id: crypto.randomUUID(),
|
|
632
|
-
actor:
|
|
389
|
+
actor: buildAgentUri(webId),
|
|
633
390
|
object: approvalUri,
|
|
634
391
|
createdAt: now,
|
|
635
|
-
}));
|
|
392
|
+
}).catch(() => undefined);
|
|
636
393
|
return normalizeApprovalSummary({
|
|
637
394
|
id: approvalId,
|
|
638
|
-
approvalUri,
|
|
639
395
|
session: sessionUri,
|
|
640
|
-
toolCallId
|
|
641
|
-
toolName: request
|
|
642
|
-
target:
|
|
643
|
-
action: request
|
|
644
|
-
risk: request
|
|
396
|
+
toolCallId,
|
|
397
|
+
toolName: buildToolName(options.request),
|
|
398
|
+
target: sessionUri,
|
|
399
|
+
action: buildActionUri(options.request),
|
|
400
|
+
risk: buildRisk(options.request),
|
|
645
401
|
status: 'pending',
|
|
646
|
-
assignedTo,
|
|
647
|
-
policyVersion,
|
|
402
|
+
assignedTo: webId,
|
|
403
|
+
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
648
404
|
createdAt: now,
|
|
649
|
-
}
|
|
405
|
+
}, [{
|
|
406
|
+
id: crypto.randomUUID(),
|
|
407
|
+
action: 'approval_requested',
|
|
408
|
+
actor: buildAgentUri(webId),
|
|
409
|
+
actorRole: 'secretary',
|
|
410
|
+
onBehalfOf: webId,
|
|
411
|
+
session: sessionUri,
|
|
412
|
+
toolCallId,
|
|
413
|
+
approval: approvalUri,
|
|
414
|
+
context: JSON.stringify(requestContext),
|
|
415
|
+
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
416
|
+
createdAt: now,
|
|
417
|
+
}]);
|
|
650
418
|
});
|
|
651
419
|
}
|
|
652
420
|
export async function waitForRemoteWatchApproval(options) {
|
|
@@ -656,13 +424,10 @@ export async function waitForRemoteWatchApproval(options) {
|
|
|
656
424
|
if (options.signal?.aborted) {
|
|
657
425
|
throw createAbortError();
|
|
658
426
|
}
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
approvalUri: options.approvalUri,
|
|
662
|
-
});
|
|
427
|
+
const approvals = await store.listApprovals();
|
|
428
|
+
const row = approvals.find((entry) => entry.id === options.approvalId);
|
|
663
429
|
if (!row) {
|
|
664
|
-
|
|
665
|
-
continue;
|
|
430
|
+
throw new Error(`Remote approval disappeared before resolution: ${options.approvalId}`);
|
|
666
431
|
}
|
|
667
432
|
const decision = decisionFromApprovalRow(row);
|
|
668
433
|
if (decision) {
|
|
@@ -695,40 +460,6 @@ export async function requestRemoteWatchApproval(options) {
|
|
|
695
460
|
});
|
|
696
461
|
return waitForRemoteWatchApproval({
|
|
697
462
|
approvalId: summary.id,
|
|
698
|
-
approvalUri: summary.approvalUri,
|
|
699
|
-
pollMs: options.pollMs,
|
|
700
|
-
signal: options.signal,
|
|
701
|
-
runtime: activeRuntime,
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
export async function requestRemoteApproval(options) {
|
|
705
|
-
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
706
|
-
const delegated = await withRemoteApprovalStore(activeRuntime, async ({ store, webId, stored }) => {
|
|
707
|
-
const subject = typeof options.subject === 'function'
|
|
708
|
-
? options.subject({ webId, stored })
|
|
709
|
-
: options.subject;
|
|
710
|
-
const request = typeof options.request === 'function'
|
|
711
|
-
? options.request({ webId, stored, sessionUri: subject.sessionUri })
|
|
712
|
-
: options.request;
|
|
713
|
-
const grants = await store.listGrants();
|
|
714
|
-
const requestTarget = subject.targetUri ?? subject.sessionUri;
|
|
715
|
-
return grants.some((grant) => (grant.effect === 'allow'
|
|
716
|
-
&& grant.action === request.action
|
|
717
|
-
&& grant.target === requestTarget
|
|
718
|
-
&& riskScore(typeof grant.riskCeiling === 'string' ? grant.riskCeiling : undefined) >= riskScore(request.risk)
|
|
719
|
-
&& !grant.revokedAt));
|
|
720
|
-
});
|
|
721
|
-
if (delegated) {
|
|
722
|
-
return 'accept_for_session';
|
|
723
|
-
}
|
|
724
|
-
const summary = await createRemoteApproval({
|
|
725
|
-
subject: options.subject,
|
|
726
|
-
request: options.request,
|
|
727
|
-
runtime: activeRuntime,
|
|
728
|
-
});
|
|
729
|
-
return waitForRemoteWatchApproval({
|
|
730
|
-
approvalId: summary.id,
|
|
731
|
-
approvalUri: summary.approvalUri,
|
|
732
463
|
pollMs: options.pollMs,
|
|
733
464
|
signal: options.signal,
|
|
734
465
|
runtime: activeRuntime,
|
|
@@ -738,9 +469,12 @@ export async function listRemoteWatchApprovals(options = {}) {
|
|
|
738
469
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
739
470
|
const requestedStatus = options.status ?? 'pending';
|
|
740
471
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
741
|
-
const approvals = await
|
|
472
|
+
const [approvals, audits] = await Promise.all([
|
|
473
|
+
store.listApprovals(),
|
|
474
|
+
store.listAudits(),
|
|
475
|
+
]);
|
|
742
476
|
return approvals
|
|
743
|
-
.map((row) => normalizeApprovalSummary(row))
|
|
477
|
+
.map((row) => normalizeApprovalSummary(row, audits))
|
|
744
478
|
.filter((summary) => !summary.assignedTo || summary.assignedTo === webId)
|
|
745
479
|
.filter((summary) => requestedStatus === 'all' || summary.status === requestedStatus)
|
|
746
480
|
.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
@@ -749,19 +483,17 @@ export async function listRemoteWatchApprovals(options = {}) {
|
|
|
749
483
|
export async function resolveRemoteWatchApproval(options) {
|
|
750
484
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
751
485
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
approvalUri: options.approvalUri,
|
|
755
|
-
});
|
|
486
|
+
const approvals = await store.listApprovals();
|
|
487
|
+
const row = approvals.find((entry) => entry.id === options.approvalId);
|
|
756
488
|
if (!row) {
|
|
757
489
|
throw new Error(`Remote approval not found: ${options.approvalId}`);
|
|
758
490
|
}
|
|
759
491
|
if (row.status !== 'pending') {
|
|
760
|
-
|
|
492
|
+
const audits = await store.listAudits();
|
|
493
|
+
return normalizeApprovalSummary(row, audits);
|
|
761
494
|
}
|
|
762
495
|
const now = activeRuntime.now();
|
|
763
|
-
const
|
|
764
|
-
const approvalUri = buildApprovalUriForDate(row.session, row.id, approvalCreatedAt);
|
|
496
|
+
const approvalUri = buildApprovalUri(row.session, row.id);
|
|
765
497
|
const nextStatus = options.decision === 'accept' || options.decision === 'accept_for_session'
|
|
766
498
|
? 'approved'
|
|
767
499
|
: 'rejected';
|
|
@@ -773,20 +505,22 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
773
505
|
reason: encodeDecisionReason(options.decision, options.note),
|
|
774
506
|
resolvedAt: now,
|
|
775
507
|
});
|
|
776
|
-
await
|
|
508
|
+
await store.insertAudit({
|
|
777
509
|
id: crypto.randomUUID(),
|
|
778
510
|
action: nextStatus === 'approved' ? 'approval_approved' : 'approval_rejected',
|
|
779
511
|
actor: webId,
|
|
780
512
|
actorRole: 'human',
|
|
781
513
|
onBehalfOf: webId,
|
|
782
514
|
session: row.session,
|
|
783
|
-
entry: approvalUri,
|
|
784
515
|
toolCallId: row.toolCallId,
|
|
785
|
-
toolName: row.toolName,
|
|
786
516
|
approval: approvalUri,
|
|
517
|
+
context: JSON.stringify({
|
|
518
|
+
decision: options.decision,
|
|
519
|
+
...(options.note?.trim() ? { note: options.note.trim() } : {}),
|
|
520
|
+
}),
|
|
787
521
|
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
788
522
|
createdAt: now,
|
|
789
|
-
})
|
|
523
|
+
});
|
|
790
524
|
if (options.decision === 'accept_for_session') {
|
|
791
525
|
const grantId = crypto.randomUUID();
|
|
792
526
|
await store.insertGrant({
|
|
@@ -800,19 +534,19 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
800
534
|
onBehalfOf: webId,
|
|
801
535
|
createdAt: now,
|
|
802
536
|
});
|
|
803
|
-
await
|
|
537
|
+
await store.insertInboxNotification({
|
|
804
538
|
id: crypto.randomUUID(),
|
|
805
539
|
actor: webId,
|
|
806
540
|
object: buildGrantUri(row.session, grantId),
|
|
807
541
|
createdAt: now,
|
|
808
|
-
}));
|
|
542
|
+
}).catch(() => undefined);
|
|
809
543
|
}
|
|
810
|
-
await
|
|
544
|
+
await store.insertInboxNotification({
|
|
811
545
|
id: crypto.randomUUID(),
|
|
812
546
|
actor: webId,
|
|
813
547
|
object: approvalUri,
|
|
814
548
|
createdAt: now,
|
|
815
|
-
}));
|
|
549
|
+
}).catch(() => undefined);
|
|
816
550
|
const nextRow = {
|
|
817
551
|
...row,
|
|
818
552
|
status: nextStatus,
|
|
@@ -822,35 +556,23 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
822
556
|
reason: encodeDecisionReason(options.decision, options.note),
|
|
823
557
|
resolvedAt: now,
|
|
824
558
|
};
|
|
825
|
-
|
|
559
|
+
const audits = await store.listAudits();
|
|
560
|
+
return normalizeApprovalSummary(nextRow, audits);
|
|
826
561
|
});
|
|
827
562
|
}
|
|
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
|
-
}
|
|
840
563
|
export const __podApprovalInternal = {
|
|
841
564
|
createAbortError,
|
|
842
|
-
createDefaultRuntime,
|
|
843
565
|
buildActionUri,
|
|
566
|
+
buildRequestAuditContext,
|
|
844
567
|
buildRisk,
|
|
845
568
|
buildToolName,
|
|
846
|
-
createNativeRemoteApprovalStore,
|
|
847
569
|
extractToolCallId,
|
|
848
570
|
decisionFromApprovalRow,
|
|
849
571
|
encodeDecisionReason,
|
|
850
572
|
formatSummaryHeadline,
|
|
851
|
-
readRemoteApprovalRow,
|
|
852
573
|
isRemoteApprovalAbortError,
|
|
853
574
|
normalizeApprovalSummary,
|
|
854
575
|
parseDecisionReason,
|
|
576
|
+
parseRequestAuditContext,
|
|
855
577
|
};
|
|
856
578
|
//# sourceMappingURL=pod-approval.js.map
|