@undefineds.co/linx 0.2.17 → 0.2.19
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 +3 -6
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +256 -143
- 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 +186 -367
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/chat-api.js +177 -19
- 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 +7 -1
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/default-model.js +1 -0
- package/dist/lib/default-model.js.map +1 -1
- package/dist/lib/login-command.js +33 -10
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +2 -27
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/node-warning-filter.js +34 -0
- package/dist/lib/node-warning-filter.js.map +1 -0
- package/dist/lib/oidc-auth.js +130 -18
- 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 +47 -11
- package/dist/lib/pi-adapter/auth.js.map +1 -1
- package/dist/lib/pi-adapter/branding.js +802 -78
- 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 +179 -5
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +8 -0
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -0
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +189 -0
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -0
- package/dist/lib/pi-adapter/pod-mirror.js +416 -0
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -0
- package/dist/lib/pi-adapter/pod-tools.js +104 -0
- package/dist/lib/pi-adapter/pod-tools.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +327 -28
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session.js +608 -0
- package/dist/lib/pi-adapter/session.js.map +1 -0
- package/dist/lib/pi-adapter/stream.js +154 -4
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/theme.js.map +1 -1
- package/dist/lib/pi-adapter/web-fetch.js +154 -0
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -0
- package/dist/lib/pod-chat-store.js +40 -30
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +110 -0
- package/dist/lib/pod-data-session.js.map +1 -0
- package/dist/lib/profile-identity.js +16 -60
- 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 +4 -0
- package/dist/lib/watch/hooks/claude.js.map +1 -1
- package/dist/lib/watch/hooks/codebuddy.js +4 -0
- package/dist/lib/watch/hooks/codebuddy.js.map +1 -1
- package/dist/lib/watch/hooks/codex.js +4 -0
- 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 +0 -1
- 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 +29 -37
- package/dist/lib/watch/pod-ai.js.map +1 -1
- package/dist/lib/watch/pod-approval.js +822 -220
- package/dist/lib/watch/pod-approval.js.map +1 -1
- package/dist/lib/watch/pod-persistence.js +214 -106
- package/dist/lib/watch/pod-persistence.js.map +1 -1
- package/dist/lib/watch/runner.js +243 -38
- package/dist/lib/watch/runner.js.map +1 -1
- package/dist/lib/watch/secretary.js +238 -0
- package/dist/lib/watch/secretary.js.map +1 -0
- package/dist/lib/watch/types.js.map +1 -1
- package/dist/watch-cli.js +8 -34
- package/dist/watch-cli.js.map +1 -1
- package/package.json +3 -9
- package/vendor/agent-runtime/dist/acp.d.ts +27 -0
- package/vendor/agent-runtime/dist/acp.js +86 -0
- package/vendor/agent-runtime/dist/companion-model.d.ts +7 -0
- package/vendor/agent-runtime/dist/companion-model.js +12 -0
- package/vendor/agent-runtime/dist/index.d.ts +3 -0
- package/vendor/agent-runtime/dist/index.js +3 -0
- package/vendor/agent-runtime/dist/turn-controller.d.ts +69 -0
- package/vendor/agent-runtime/dist/turn-controller.js +129 -0
- package/vendor/agent-runtime/package.json +11 -0
- package/vendor/client/dist/client/index.d.ts +0 -118
- package/vendor/client/dist/client/index.js +0 -260
- package/vendor/client/dist/index.d.ts +0 -1
- package/vendor/client/dist/index.js +0 -1
- package/vendor/client/dist/watch/index.d.ts +0 -226
- package/vendor/client/dist/watch/index.js +0 -1114
- package/vendor/client/package.json +0 -9
|
@@ -1,17 +1,22 @@
|
|
|
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, inboxNotificationTable, initSolidTables, solidSchema, } from '../models.js';
|
|
4
|
+
import { resolveWatchGrantCoverage } from './secretary.js';
|
|
5
|
+
const WATCH_CHAT_ID_PREFIX = 'linx-watch';
|
|
3
6
|
const WATCH_AGENT_ID = 'linx-watch-assistant';
|
|
4
7
|
const REMOTE_APPROVAL_POLICY_VERSION = 'linx-watch-remote-approval/v1';
|
|
5
8
|
const DEFAULT_REMOTE_APPROVAL_POLL_MS = 1000;
|
|
9
|
+
const DEFAULT_WARN_ONLY_TIMEOUT_MS = 5000;
|
|
10
|
+
const MAX_GRANT_POLICY_LENGTH = 1200;
|
|
11
|
+
const MAX_APPROVAL_CONTEXT_LENGTH = 1400;
|
|
12
|
+
const MIN_GRANT_COVERAGE_CONFIDENCE = 0.75;
|
|
13
|
+
const MAX_GRANT_COVERAGE_CANDIDATES = 5;
|
|
14
|
+
const remoteApprovalClientCache = new WeakMap();
|
|
6
15
|
function createAbortError() {
|
|
7
16
|
const error = new Error('The operation was aborted.');
|
|
8
17
|
error.name = 'AbortError';
|
|
9
18
|
return error;
|
|
10
19
|
}
|
|
11
|
-
async function dynamicImport(specifier) {
|
|
12
|
-
const loader = new Function('modulePath', 'return import(modulePath)');
|
|
13
|
-
return loader(specifier);
|
|
14
|
-
}
|
|
15
20
|
function normalizeString(value) {
|
|
16
21
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
17
22
|
}
|
|
@@ -37,14 +42,17 @@ function getPodBaseUrl(webIdOrUri) {
|
|
|
37
42
|
}
|
|
38
43
|
return webIdOrUri.replace(/\/$/, '');
|
|
39
44
|
}
|
|
40
|
-
function
|
|
41
|
-
return `${
|
|
45
|
+
function buildWatchChatId(record) {
|
|
46
|
+
return `${WATCH_CHAT_ID_PREFIX}-${record.backend}`;
|
|
47
|
+
}
|
|
48
|
+
function buildThreadUri(webId, record) {
|
|
49
|
+
return `${getPodBaseUrl(webId)}/.data/chat/${buildWatchChatId(record)}/index.ttl#${record.id}`;
|
|
42
50
|
}
|
|
43
|
-
function
|
|
44
|
-
return
|
|
51
|
+
function isAbsoluteIri(value) {
|
|
52
|
+
return value.startsWith('http://') || value.startsWith('https://');
|
|
45
53
|
}
|
|
46
|
-
function
|
|
47
|
-
return `${getPodBaseUrl(webIdOrUri)}/settings/autonomy/
|
|
54
|
+
function buildGrantSchemaUri(webIdOrUri) {
|
|
55
|
+
return `${getPodBaseUrl(webIdOrUri)}/settings/autonomy/schema/grant.ttl#GrantWikiPage`;
|
|
48
56
|
}
|
|
49
57
|
function buildAgentUri(webId) {
|
|
50
58
|
return `${getPodBaseUrl(webId)}/.data/agents/${WATCH_AGENT_ID}.ttl`;
|
|
@@ -103,16 +111,6 @@ function buildRequestMessage(request) {
|
|
|
103
111
|
}
|
|
104
112
|
return request.message;
|
|
105
113
|
}
|
|
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
114
|
function extractToolCallId(request) {
|
|
117
115
|
if (!isRecord(request.raw)) {
|
|
118
116
|
return crypto.randomUUID();
|
|
@@ -124,7 +122,7 @@ function extractToolCallId(request) {
|
|
|
124
122
|
?? crypto.randomUUID();
|
|
125
123
|
}
|
|
126
124
|
function encodeDecisionReason(decision, note) {
|
|
127
|
-
return
|
|
125
|
+
return safeJsonStringify({
|
|
128
126
|
decision,
|
|
129
127
|
...(note?.trim() ? { note: note.trim() } : {}),
|
|
130
128
|
});
|
|
@@ -151,35 +149,184 @@ function parseDecisionReason(value) {
|
|
|
151
149
|
return null;
|
|
152
150
|
}
|
|
153
151
|
}
|
|
154
|
-
function
|
|
152
|
+
async function warnOnly(runtime, task) {
|
|
153
|
+
try {
|
|
154
|
+
await Promise.race([
|
|
155
|
+
task(),
|
|
156
|
+
runtime.sleep(DEFAULT_WARN_ONLY_TIMEOUT_MS).then(() => {
|
|
157
|
+
throw new Error(`Pod side-effect sync timed out after ${DEFAULT_WARN_ONLY_TIMEOUT_MS}ms`);
|
|
158
|
+
}),
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (runtime.onWarning) {
|
|
163
|
+
runtime.onWarning(error);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
167
|
+
process.emitWarning(`LinX Pod sync failed: ${message}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function safeJsonStringify(value) {
|
|
171
|
+
try {
|
|
172
|
+
return JSON.stringify(value);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return JSON.stringify({ error: 'unserializable_context' });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function truncatePodLiteral(value, maxLength) {
|
|
179
|
+
if (value.length <= maxLength) {
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
return `${value.slice(0, Math.max(0, maxLength - 15))}...[truncated]`;
|
|
183
|
+
}
|
|
184
|
+
function safeCompactJson(value, maxLength) {
|
|
185
|
+
return truncatePodLiteral(safeJsonStringify(value), maxLength);
|
|
186
|
+
}
|
|
187
|
+
function compactApprovalContext(request) {
|
|
188
|
+
return safeCompactJson({
|
|
189
|
+
kind: request.kind,
|
|
190
|
+
message: request.message,
|
|
191
|
+
toolName: request.toolName,
|
|
192
|
+
action: request.action,
|
|
193
|
+
risk: request.risk,
|
|
194
|
+
...(request.command ? { command: request.command } : {}),
|
|
195
|
+
...(request.cwd ? { cwd: request.cwd } : {}),
|
|
196
|
+
...(request.approvalOptions ? { approvalOptions: request.approvalOptions } : {}),
|
|
197
|
+
...(request.expiresAt ? { expiresAt: normalizeDateLike(request.expiresAt) } : {}),
|
|
198
|
+
...(request.context ? { sourceContext: truncatePodLiteral(request.context, 500) } : {}),
|
|
199
|
+
}, MAX_APPROVAL_CONTEXT_LENGTH);
|
|
200
|
+
}
|
|
201
|
+
function grantWikiTitleFromApproval(row, explicitTitle) {
|
|
202
|
+
const explicit = normalizeString(explicitTitle);
|
|
203
|
+
if (explicit) {
|
|
204
|
+
return truncatePodLiteral(explicit, 160);
|
|
205
|
+
}
|
|
206
|
+
return truncatePodLiteral(`${row.toolName} grant wiki for ${extractSessionId(row.session)}`, 160);
|
|
207
|
+
}
|
|
208
|
+
function grantWikiSummaryFromApproval(row, explicitSummary) {
|
|
209
|
+
const explicit = normalizeString(explicitSummary);
|
|
210
|
+
if (explicit) {
|
|
211
|
+
return truncatePodLiteral(explicit, 500);
|
|
212
|
+
}
|
|
213
|
+
return truncatePodLiteral(`Authorization wiki page for ${row.toolName}. AI Secretary must read the page body before reusing this grant.`, 500);
|
|
214
|
+
}
|
|
215
|
+
function grantWikiBodyFromApproval(row, explicitBody) {
|
|
216
|
+
const explicit = normalizeString(explicitBody);
|
|
217
|
+
if (explicit) {
|
|
218
|
+
return truncatePodLiteral(explicit, MAX_GRANT_POLICY_LENGTH);
|
|
219
|
+
}
|
|
220
|
+
return truncatePodLiteral([
|
|
221
|
+
'# Grant Semantics',
|
|
222
|
+
'',
|
|
223
|
+
'This page follows the LLM Wiki pattern: it is the maintained wiki view AI Secretary reads before reusing an authorization.',
|
|
224
|
+
'',
|
|
225
|
+
'## Covers',
|
|
226
|
+
`- Requests semantically inside target ${row.target}.`,
|
|
227
|
+
`- Action family ${row.action}.`,
|
|
228
|
+
`- Risk no higher than ${row.risk}.`,
|
|
229
|
+
'',
|
|
230
|
+
'## Does Not Cover',
|
|
231
|
+
'- Requests that are materially broader than the source approval.',
|
|
232
|
+
'- Requests that change from read-oriented to write/destructive behavior.',
|
|
233
|
+
'- Requests that touch credentials, secrets, package installation, new network side effects, or workspace boundaries unless explicitly documented here.',
|
|
234
|
+
'',
|
|
235
|
+
'## Source Context',
|
|
236
|
+
row.context ?? safeJsonStringify({ toolName: row.toolName, action: row.action, risk: row.risk }),
|
|
237
|
+
].join('\n'), MAX_GRANT_POLICY_LENGTH);
|
|
238
|
+
}
|
|
239
|
+
function grantIndexTextFromWikiBody(body) {
|
|
240
|
+
return truncatePodLiteral(body, MAX_GRANT_POLICY_LENGTH);
|
|
241
|
+
}
|
|
242
|
+
function grantWikiTagsFromApproval(row, explicitTags) {
|
|
243
|
+
const tags = [
|
|
244
|
+
'autonomy',
|
|
245
|
+
'grant',
|
|
246
|
+
row.toolName,
|
|
247
|
+
row.risk,
|
|
248
|
+
...(explicitTags ?? []),
|
|
249
|
+
]
|
|
250
|
+
.map((tag) => tag.trim())
|
|
251
|
+
.filter(Boolean);
|
|
252
|
+
return safeJsonStringify([...new Set(tags)]);
|
|
253
|
+
}
|
|
254
|
+
function grantContextFromApproval(row) {
|
|
255
|
+
return safeCompactJson({
|
|
256
|
+
sourceApproval: row.approvalUri ?? row.id,
|
|
257
|
+
session: row.session,
|
|
258
|
+
toolCallId: row.toolCallId,
|
|
259
|
+
toolName: row.toolName,
|
|
260
|
+
target: row.target,
|
|
261
|
+
action: row.action,
|
|
262
|
+
risk: row.risk,
|
|
263
|
+
approvalContext: row.context,
|
|
264
|
+
}, MAX_APPROVAL_CONTEXT_LENGTH);
|
|
265
|
+
}
|
|
266
|
+
function grantSourceHash(row) {
|
|
267
|
+
return `approval:${row.id}:${row.toolCallId}:${row.risk}`;
|
|
268
|
+
}
|
|
269
|
+
function encodeApprovalOptions(options) {
|
|
270
|
+
if (!options || options.length === 0) {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
return safeJsonStringify(options);
|
|
274
|
+
}
|
|
275
|
+
function parseApprovalOptions(value) {
|
|
155
276
|
if (typeof value !== 'string' || !value.trim()) {
|
|
156
|
-
return
|
|
277
|
+
return undefined;
|
|
157
278
|
}
|
|
158
279
|
try {
|
|
159
280
|
const parsed = JSON.parse(value);
|
|
160
|
-
if (!
|
|
161
|
-
return
|
|
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;
|
|
281
|
+
if (!Array.isArray(parsed)) {
|
|
282
|
+
return undefined;
|
|
169
283
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
284
|
+
const options = parsed
|
|
285
|
+
.map((option) => {
|
|
286
|
+
if (!isRecord(option)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const optionId = normalizeString(option.optionId);
|
|
290
|
+
const label = normalizeString(option.label);
|
|
291
|
+
if (!optionId || !label) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const kind = normalizeString(option.kind);
|
|
295
|
+
const description = normalizeString(option.description);
|
|
296
|
+
return {
|
|
297
|
+
optionId,
|
|
298
|
+
label,
|
|
299
|
+
...(kind ? { kind } : {}),
|
|
300
|
+
...(description ? { description } : {}),
|
|
301
|
+
};
|
|
302
|
+
})
|
|
303
|
+
.filter((option) => option !== null);
|
|
304
|
+
return options.length > 0 ? options : undefined;
|
|
178
305
|
}
|
|
179
306
|
catch {
|
|
180
|
-
return
|
|
307
|
+
return undefined;
|
|
181
308
|
}
|
|
182
309
|
}
|
|
310
|
+
function normalizeDateLike(value) {
|
|
311
|
+
if (value instanceof Date) {
|
|
312
|
+
return Number.isFinite(value.getTime()) ? value.toISOString() : undefined;
|
|
313
|
+
}
|
|
314
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
const parsed = new Date(value);
|
|
318
|
+
return Number.isFinite(parsed.getTime()) ? parsed.toISOString() : undefined;
|
|
319
|
+
}
|
|
320
|
+
function resolveApprovalExpiresAt(request, now) {
|
|
321
|
+
const explicit = normalizeDateLike(request.expiresAt);
|
|
322
|
+
if (explicit) {
|
|
323
|
+
return explicit;
|
|
324
|
+
}
|
|
325
|
+
if (typeof request.timeoutMs === 'number' && Number.isFinite(request.timeoutMs) && request.timeoutMs > 0) {
|
|
326
|
+
return new Date(now.getTime() + request.timeoutMs);
|
|
327
|
+
}
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
183
330
|
function extractSessionId(sessionUri) {
|
|
184
331
|
if (sessionUri.includes('#')) {
|
|
185
332
|
return sessionUri.split('#').pop() || sessionUri;
|
|
@@ -200,36 +347,42 @@ function decisionFromApprovalRow(row) {
|
|
|
200
347
|
}
|
|
201
348
|
return 'accept';
|
|
202
349
|
}
|
|
203
|
-
function
|
|
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);
|
|
350
|
+
function normalizeApprovalSummary(row) {
|
|
212
351
|
const createdAt = toIsoString(row.createdAt, new Date(0).toISOString());
|
|
213
352
|
const sessionUri = row.session;
|
|
214
353
|
const decision = decisionFromApprovalRow(row);
|
|
354
|
+
const approvalOptions = parseApprovalOptions(row.approvalOptions);
|
|
215
355
|
return {
|
|
216
356
|
id: row.id,
|
|
357
|
+
...(normalizeString(row.approvalUri) ? { approvalUri: normalizeString(row.approvalUri) } : {}),
|
|
217
358
|
sessionId: extractSessionId(sessionUri),
|
|
218
359
|
sessionUri,
|
|
219
360
|
toolCallId: row.toolCallId,
|
|
220
361
|
toolName: row.toolName,
|
|
221
362
|
risk: normalizeString(row.risk) ?? 'medium',
|
|
222
363
|
status: normalizeString(row.status) ?? 'pending',
|
|
223
|
-
message:
|
|
224
|
-
...(requestContext?.command ? { command: requestContext.command } : {}),
|
|
225
|
-
...(requestContext?.cwd ? { cwd: requestContext.cwd } : {}),
|
|
364
|
+
message: formatApprovalMessage(row),
|
|
226
365
|
...(normalizeString(row.assignedTo) ? { assignedTo: normalizeString(row.assignedTo) } : {}),
|
|
227
366
|
...(normalizeString(row.decisionBy) ? { decisionBy: normalizeString(row.decisionBy) } : {}),
|
|
228
367
|
...(decision ? { decision } : {}),
|
|
368
|
+
...(approvalOptions ? { approvalOptions } : {}),
|
|
229
369
|
createdAt,
|
|
370
|
+
...(row.expiresAt ? { expiresAt: toIsoString(row.expiresAt, createdAt) } : {}),
|
|
230
371
|
...(row.resolvedAt ? { resolvedAt: toIsoString(row.resolvedAt, createdAt) } : {}),
|
|
231
372
|
};
|
|
232
373
|
}
|
|
374
|
+
function formatApprovalMessage(row) {
|
|
375
|
+
if (row.toolName === 'commandExecution') {
|
|
376
|
+
return 'Command execution approval';
|
|
377
|
+
}
|
|
378
|
+
if (row.toolName === 'fileChange') {
|
|
379
|
+
return 'File change approval';
|
|
380
|
+
}
|
|
381
|
+
if (row.toolName === 'permissionRequest') {
|
|
382
|
+
return 'Permission approval';
|
|
383
|
+
}
|
|
384
|
+
return row.toolName;
|
|
385
|
+
}
|
|
233
386
|
function formatSummaryHeadline(summary) {
|
|
234
387
|
return `${summary.id} | ${summary.status} | ${summary.risk} | session=${summary.sessionId}`;
|
|
235
388
|
}
|
|
@@ -248,72 +401,11 @@ export function isRemoteApprovalAbortError(error) {
|
|
|
248
401
|
function missingRemoteApprovalCredentialsMessage() {
|
|
249
402
|
return 'LinX remote approval requires `linx login` first.';
|
|
250
403
|
}
|
|
251
|
-
function unsupportedRemoteApprovalAuthMessage() {
|
|
252
|
-
return 'LinX remote approval requires client credentials auth in `~/.linx`.';
|
|
253
|
-
}
|
|
254
404
|
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
|
-
]);
|
|
260
405
|
return {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
};
|
|
406
|
+
getPodDataSession: getDefaultPodDataSession,
|
|
407
|
+
createStore(session, db) {
|
|
408
|
+
return createNativeRemoteApprovalStore(session.webId, db);
|
|
317
409
|
},
|
|
318
410
|
sleep(ms) {
|
|
319
411
|
return delay(ms);
|
|
@@ -321,100 +413,523 @@ async function createDefaultRuntime() {
|
|
|
321
413
|
now() {
|
|
322
414
|
return new Date();
|
|
323
415
|
},
|
|
416
|
+
resolveGrantCoverage: resolveWatchGrantCoverage,
|
|
324
417
|
};
|
|
325
418
|
}
|
|
326
419
|
async function withRemoteApprovalStore(runtime, fn) {
|
|
327
|
-
const
|
|
328
|
-
if (!
|
|
420
|
+
const client = await getRemoteApprovalClient(runtime);
|
|
421
|
+
if (!client) {
|
|
329
422
|
throw new Error(missingRemoteApprovalCredentialsMessage());
|
|
330
423
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
424
|
+
return await fn({
|
|
425
|
+
store: client.store,
|
|
426
|
+
webId: client.session.webId,
|
|
427
|
+
stored: client.session.credentials,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
async function getRemoteApprovalClient(runtime) {
|
|
431
|
+
let promise = remoteApprovalClientCache.get(runtime);
|
|
432
|
+
if (!promise) {
|
|
433
|
+
promise = createRemoteApprovalClient(runtime)
|
|
434
|
+
.then((client) => {
|
|
435
|
+
if (!client) {
|
|
436
|
+
remoteApprovalClientCache.delete(runtime);
|
|
437
|
+
}
|
|
438
|
+
return client;
|
|
439
|
+
})
|
|
440
|
+
.catch((error) => {
|
|
441
|
+
remoteApprovalClientCache.delete(runtime);
|
|
442
|
+
throw error;
|
|
443
|
+
});
|
|
444
|
+
remoteApprovalClientCache.set(runtime, promise);
|
|
334
445
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
446
|
+
return promise;
|
|
447
|
+
}
|
|
448
|
+
async function createRemoteApprovalClient(runtime) {
|
|
449
|
+
const session = await runtime.getPodDataSession();
|
|
450
|
+
if (!session) {
|
|
451
|
+
return null;
|
|
340
452
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
453
|
+
const db = createRemoteApprovalDb(session);
|
|
454
|
+
await initSolidTables(db, [
|
|
455
|
+
approvalResource,
|
|
456
|
+
auditResource,
|
|
457
|
+
grantResource,
|
|
458
|
+
inboxNotificationTable,
|
|
459
|
+
]);
|
|
460
|
+
return {
|
|
461
|
+
session,
|
|
462
|
+
store: runtime.createStore(session, db),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function createRemoteApprovalDb(session) {
|
|
466
|
+
return drizzle(session.solidSession, {
|
|
467
|
+
logger: false,
|
|
468
|
+
disableInteropDiscovery: true,
|
|
469
|
+
schema: solidSchema,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
function createNativeRemoteApprovalStore(_webId, db) {
|
|
473
|
+
return {
|
|
474
|
+
listApprovals: () => listApprovalRows(db),
|
|
475
|
+
findApproval: (id, options) => findApprovalRow(db, id, options),
|
|
476
|
+
resolveApprovalReference: (locator) => resolveResourceReference(db, approvalResource, locator),
|
|
477
|
+
insertApproval: (row) => writeApprovalRow(db, row),
|
|
478
|
+
async updateApproval(id, patch) {
|
|
479
|
+
const data = normalizeApprovalUpdate(patch);
|
|
480
|
+
const approvalUri = normalizeString(patch.approvalUri);
|
|
481
|
+
const targetIri = approvalUri ?? (isAbsoluteIri(id) ? id : undefined);
|
|
482
|
+
if (targetIri) {
|
|
483
|
+
const updateByIri = db.updateByIri;
|
|
484
|
+
if (typeof updateByIri !== 'function') {
|
|
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;
|
|
492
|
+
}
|
|
493
|
+
const updateByLocator = db.updateByLocator;
|
|
494
|
+
if (typeof updateByLocator !== 'function') {
|
|
495
|
+
throw new Error('Solid database does not support updateByLocator');
|
|
496
|
+
}
|
|
497
|
+
const updated = await updateByLocator.call(db, approvalResource, { id }, data);
|
|
498
|
+
if (!updated) {
|
|
499
|
+
throw new Error(`Remote approval not found: ${id}`);
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
listAudits: () => listAuditRows(db),
|
|
503
|
+
insertAudit: (row) => writeAuditRow(db, row),
|
|
504
|
+
listGrants: () => listGrantRows(db),
|
|
505
|
+
resolveGrantReference: (locator) => resolveResourceReference(db, grantResource, locator),
|
|
506
|
+
insertGrant: (row) => writeGrantRow(db, row),
|
|
507
|
+
insertInboxNotification: (row) => writeInboxNotificationRow(db, row),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async function findApprovalRow(db, id, options = {}) {
|
|
511
|
+
const approvalUri = normalizeString(options.approvalUri) ?? (isAbsoluteIri(id) ? id : undefined);
|
|
512
|
+
if (approvalUri) {
|
|
513
|
+
const findByIri = db.findByIri;
|
|
514
|
+
if (typeof findByIri !== 'function') {
|
|
515
|
+
throw new Error('Solid database does not support findByIri');
|
|
516
|
+
}
|
|
517
|
+
const row = await findByIri.call(db, approvalResource, approvalUri);
|
|
518
|
+
return normalizeApprovalRow(row);
|
|
347
519
|
}
|
|
348
|
-
|
|
349
|
-
|
|
520
|
+
const findByLocator = db.findByLocator;
|
|
521
|
+
if (typeof findByLocator !== 'function') {
|
|
522
|
+
throw new Error('Solid database does not support findByLocator');
|
|
523
|
+
}
|
|
524
|
+
const row = await findByLocator.call(db, approvalResource, {
|
|
525
|
+
id,
|
|
526
|
+
...(options.createdAt ? { createdAt: options.createdAt } : {}),
|
|
527
|
+
});
|
|
528
|
+
return normalizeApprovalRow(row);
|
|
529
|
+
}
|
|
530
|
+
function resolveResourceReference(db, resource, locator) {
|
|
531
|
+
if (typeof db.resolveLocatorIri !== 'function' || typeof db.resolveLocatorId !== 'function') {
|
|
532
|
+
throw new Error('Solid database does not support locator reference resolution');
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
id: db.resolveLocatorId(resource, locator),
|
|
536
|
+
iri: db.resolveLocatorIri(resource, locator),
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
async function listApprovalRows(db) {
|
|
540
|
+
const rows = await db.select().from(approvalResource).execute();
|
|
541
|
+
return rows.map((row) => normalizeApprovalRow(row)).filter((row) => row !== null);
|
|
542
|
+
}
|
|
543
|
+
async function writeApprovalRow(db, row) {
|
|
544
|
+
await db.insert(approvalResource).values(normalizeApprovalInsert(row)).execute();
|
|
545
|
+
}
|
|
546
|
+
async function listAuditRows(db) {
|
|
547
|
+
const rows = await db.select().from(auditResource).execute();
|
|
548
|
+
return rows.map((row) => normalizeAuditRow(row)).filter((row) => row !== null);
|
|
549
|
+
}
|
|
550
|
+
async function writeAuditRow(db, row) {
|
|
551
|
+
await db.insert(auditResource).values(normalizeAuditInsert(row)).execute();
|
|
552
|
+
}
|
|
553
|
+
async function listGrantRows(db) {
|
|
554
|
+
const rows = await db.select().from(grantResource).execute();
|
|
555
|
+
return rows.map((row) => normalizeGrantRow(row)).filter((row) => row !== null);
|
|
556
|
+
}
|
|
557
|
+
async function writeGrantRow(db, row) {
|
|
558
|
+
const id = normalizeString(row.id) ?? crypto.randomUUID();
|
|
559
|
+
const target = normalizeString(row.target);
|
|
560
|
+
const action = normalizeString(row.action);
|
|
561
|
+
const effect = normalizeString(row.effect);
|
|
562
|
+
const decisionBy = normalizeString(row.decisionBy);
|
|
563
|
+
const decisionRole = normalizeString(row.decisionRole);
|
|
564
|
+
if (!target || !action || !effect || !decisionBy || !decisionRole) {
|
|
565
|
+
throw new Error(`Invalid remote approval grant row: ${id}`);
|
|
350
566
|
}
|
|
567
|
+
await db.insert(grantResource).values(normalizeGrantInsert({ ...row, id, target, action, effect, decisionBy, decisionRole })).execute();
|
|
568
|
+
}
|
|
569
|
+
async function writeInboxNotificationRow(db, row) {
|
|
570
|
+
await db.insert(inboxNotificationTable).values(normalizeInboxNotificationInsert(row)).execute();
|
|
571
|
+
}
|
|
572
|
+
function normalizeApprovalRow(row) {
|
|
573
|
+
if (!row)
|
|
574
|
+
return null;
|
|
575
|
+
return {
|
|
576
|
+
id: String(row.id),
|
|
577
|
+
approvalUri: normalizeString(row['@id']) ?? normalizeString(row.subject) ?? normalizeString(row.uri),
|
|
578
|
+
session: row.session,
|
|
579
|
+
toolCallId: row.toolCallId,
|
|
580
|
+
toolName: row.toolName,
|
|
581
|
+
target: row.target,
|
|
582
|
+
action: row.action,
|
|
583
|
+
risk: row.risk,
|
|
584
|
+
status: row.status,
|
|
585
|
+
assignedTo: row.assignedTo,
|
|
586
|
+
decisionBy: row.decisionBy,
|
|
587
|
+
decisionRole: row.decisionRole,
|
|
588
|
+
onBehalfOf: row.onBehalfOf,
|
|
589
|
+
reason: row.reason,
|
|
590
|
+
context: row.context,
|
|
591
|
+
approvalOptions: row.approvalOptions,
|
|
592
|
+
policyVersion: row.policyVersion,
|
|
593
|
+
createdAt: row.createdAt,
|
|
594
|
+
expiresAt: row.expiresAt,
|
|
595
|
+
resolvedAt: row.resolvedAt,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function normalizeAuditRow(row) {
|
|
599
|
+
if (!row)
|
|
600
|
+
return null;
|
|
601
|
+
return {
|
|
602
|
+
id: String(row.id),
|
|
603
|
+
action: row.action,
|
|
604
|
+
actor: row.actor,
|
|
605
|
+
actorRole: row.actorRole,
|
|
606
|
+
onBehalfOf: row.onBehalfOf,
|
|
607
|
+
session: row.session,
|
|
608
|
+
entry: row.entry,
|
|
609
|
+
toolCallId: row.toolCallId,
|
|
610
|
+
toolName: row.toolName,
|
|
611
|
+
approval: row.approval,
|
|
612
|
+
policyVersion: row.policyVersion,
|
|
613
|
+
createdAt: row.createdAt,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function normalizeGrantRow(row) {
|
|
617
|
+
if (!row)
|
|
618
|
+
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
|
+
}
|
|
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
|
+
return {
|
|
703
|
+
id: row.id,
|
|
704
|
+
action: row.action,
|
|
705
|
+
actor: row.actor,
|
|
706
|
+
actorRole: row.actorRole,
|
|
707
|
+
...(row.onBehalfOf ? { onBehalfOf: row.onBehalfOf } : {}),
|
|
708
|
+
...(row.session ? { session: row.session } : {}),
|
|
709
|
+
...(row.entry ? { entry: row.entry } : {}),
|
|
710
|
+
...(row.toolCallId ? { toolCallId: row.toolCallId } : {}),
|
|
711
|
+
...(row.toolName ? { toolName: row.toolName } : {}),
|
|
712
|
+
...(row.approval ? { approval: row.approval } : {}),
|
|
713
|
+
...(row.policyVersion ? { policyVersion: row.policyVersion } : {}),
|
|
714
|
+
createdAt: new Date(toIsoString(row.createdAt, new Date().toISOString())),
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function normalizeGrantInsert(row) {
|
|
718
|
+
return {
|
|
719
|
+
id: row.id,
|
|
720
|
+
target: row.target,
|
|
721
|
+
action: row.action,
|
|
722
|
+
...(row.title ? { title: truncatePodLiteral(row.title, 160) } : {}),
|
|
723
|
+
...(row.summary ? { summary: truncatePodLiteral(row.summary, 500) } : {}),
|
|
724
|
+
...(row.body ? { body: truncatePodLiteral(row.body, MAX_GRANT_POLICY_LENGTH) } : {}),
|
|
725
|
+
...(row.schema ? { schema: row.schema } : {}),
|
|
726
|
+
...(row.pageKind ? { pageKind: row.pageKind } : {}),
|
|
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())),
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function isActiveAllowGrant(grant) {
|
|
754
|
+
return grant.effect === 'allow' && !grant.revokedAt && !!(normalizeString(grant.body) || normalizeString(grant.policy));
|
|
755
|
+
}
|
|
756
|
+
function isGrantRiskCandidate(grant, requestRisk) {
|
|
757
|
+
const ceiling = riskScore(typeof grant.riskCeiling === 'string' ? grant.riskCeiling : undefined);
|
|
758
|
+
return ceiling === 0 || ceiling >= riskScore(requestRisk);
|
|
759
|
+
}
|
|
760
|
+
function rankGrantCandidate(grant, requestContext) {
|
|
761
|
+
let score = 0;
|
|
762
|
+
if (grant.target === requestContext.target) {
|
|
763
|
+
score += 4;
|
|
764
|
+
}
|
|
765
|
+
if (grant.action === requestContext.action) {
|
|
766
|
+
score += 3;
|
|
767
|
+
}
|
|
768
|
+
if (grant.schema) {
|
|
769
|
+
score += 2;
|
|
770
|
+
}
|
|
771
|
+
if (grant.pageKind === 'autonomy-grant') {
|
|
772
|
+
score += 1;
|
|
773
|
+
}
|
|
774
|
+
return score;
|
|
775
|
+
}
|
|
776
|
+
function selectSemanticGrantCandidates(grants, requestContext) {
|
|
777
|
+
const risk = normalizeString(requestContext.risk) ?? 'medium';
|
|
778
|
+
return grants
|
|
779
|
+
.filter((grant) => isActiveAllowGrant(grant) && isGrantRiskCandidate(grant, risk))
|
|
780
|
+
.sort((left, right) => rankGrantCandidate(right, requestContext) - rankGrantCandidate(left, requestContext))
|
|
781
|
+
.slice(0, MAX_GRANT_COVERAGE_CANDIDATES);
|
|
782
|
+
}
|
|
783
|
+
function acceptsGrantCoverage(decision) {
|
|
784
|
+
return decision?.covers === true
|
|
785
|
+
&& typeof decision.confidence === 'number'
|
|
786
|
+
&& decision.confidence >= MIN_GRANT_COVERAGE_CONFIDENCE;
|
|
787
|
+
}
|
|
788
|
+
async function resolveSemanticGrantDecision(options) {
|
|
789
|
+
const candidates = selectSemanticGrantCandidates(options.grants, options.requestContext);
|
|
790
|
+
if (candidates.length === 0) {
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
const resolver = options.runtime.resolveGrantCoverage ?? resolveWatchGrantCoverage;
|
|
794
|
+
for (const grant of candidates) {
|
|
795
|
+
const coverage = await resolver({
|
|
796
|
+
record: options.record,
|
|
797
|
+
request: options.request,
|
|
798
|
+
requestContext: options.requestContext,
|
|
799
|
+
grant,
|
|
800
|
+
}).catch(() => null);
|
|
801
|
+
if (acceptsGrantCoverage(coverage)) {
|
|
802
|
+
return 'accept_for_session';
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
807
|
+
function buildWatchGrantRequestContext(input) {
|
|
808
|
+
return {
|
|
809
|
+
session: buildThreadUri(input.webId, input.record),
|
|
810
|
+
target: buildThreadUri(input.webId, input.record),
|
|
811
|
+
action: buildActionUri(input.request),
|
|
812
|
+
risk: buildRisk(input.request),
|
|
813
|
+
toolName: buildToolName(input.request),
|
|
814
|
+
cwd: input.record.cwd,
|
|
815
|
+
backend: input.record.backend,
|
|
816
|
+
mode: input.record.mode,
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function buildGenericGrantRequestContext(input) {
|
|
820
|
+
return {
|
|
821
|
+
session: input.subject.sessionUri,
|
|
822
|
+
target: input.subject.targetUri ?? input.subject.sessionUri,
|
|
823
|
+
action: input.request.action,
|
|
824
|
+
risk: input.request.risk,
|
|
825
|
+
toolName: input.request.toolName,
|
|
826
|
+
cwd: input.request.cwd,
|
|
827
|
+
kind: input.request.kind,
|
|
828
|
+
};
|
|
351
829
|
}
|
|
352
830
|
export async function createRemoteWatchApproval(options) {
|
|
353
831
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
354
|
-
return
|
|
355
|
-
|
|
832
|
+
return createRemoteApproval({
|
|
833
|
+
subject: ({ webId }) => ({
|
|
834
|
+
sessionUri: buildThreadUri(webId, options.record),
|
|
835
|
+
actorUri: buildAgentUri(webId),
|
|
836
|
+
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
837
|
+
}),
|
|
838
|
+
request: ({ sessionUri }) => ({
|
|
839
|
+
kind: options.request.kind,
|
|
840
|
+
message: buildRequestMessage(options.request),
|
|
841
|
+
toolCallId: extractToolCallId(options.request),
|
|
842
|
+
toolName: buildToolName(options.request),
|
|
843
|
+
action: buildActionUri(options.request),
|
|
844
|
+
risk: buildRisk(options.request),
|
|
845
|
+
...(options.request.kind === 'command-approval' && options.request.command ? { command: options.request.command } : {}),
|
|
846
|
+
...(options.request.kind === 'command-approval' && options.request.cwd ? { cwd: options.request.cwd } : {}),
|
|
847
|
+
...(options.request.approvalOptions ? { approvalOptions: options.request.approvalOptions } : {}),
|
|
848
|
+
...(options.request.timeoutMs ? { timeoutMs: options.request.timeoutMs } : {}),
|
|
849
|
+
...(options.request.expiresAt ? { expiresAt: options.request.expiresAt } : {}),
|
|
850
|
+
entry: sessionUri,
|
|
851
|
+
}),
|
|
852
|
+
runtime: activeRuntime,
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
export async function createRemoteApproval(options) {
|
|
856
|
+
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
857
|
+
return withRemoteApprovalStore(activeRuntime, async ({ store, webId, stored }) => {
|
|
858
|
+
const subject = typeof options.subject === 'function'
|
|
859
|
+
? options.subject({ webId, stored })
|
|
860
|
+
: options.subject;
|
|
861
|
+
const request = typeof options.request === 'function'
|
|
862
|
+
? options.request({ webId, stored, sessionUri: subject.sessionUri })
|
|
863
|
+
: options.request;
|
|
864
|
+
const approvalLocalId = crypto.randomUUID();
|
|
356
865
|
const now = activeRuntime.now();
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
const
|
|
866
|
+
const approvalReference = store.resolveApprovalReference({ id: approvalLocalId, createdAt: now });
|
|
867
|
+
const approvalId = approvalReference.id;
|
|
868
|
+
const sessionUri = subject.sessionUri;
|
|
869
|
+
const approvalUri = approvalReference.iri;
|
|
870
|
+
const targetUri = subject.targetUri ?? sessionUri;
|
|
871
|
+
const assignedTo = subject.assignedTo ?? webId;
|
|
872
|
+
const onBehalfOf = subject.onBehalfOf ?? webId;
|
|
873
|
+
const policyVersion = subject.policyVersion ?? REMOTE_APPROVAL_POLICY_VERSION;
|
|
874
|
+
const requestEntry = request.entry ?? approvalUri;
|
|
875
|
+
const expiresAt = resolveApprovalExpiresAt(request, now);
|
|
876
|
+
const approvalOptions = encodeApprovalOptions(request.approvalOptions);
|
|
877
|
+
const context = compactApprovalContext(request);
|
|
361
878
|
await store.insertApproval({
|
|
362
879
|
id: approvalId,
|
|
880
|
+
approvalUri,
|
|
363
881
|
session: sessionUri,
|
|
364
|
-
toolCallId,
|
|
365
|
-
toolName:
|
|
366
|
-
target:
|
|
367
|
-
action:
|
|
368
|
-
risk:
|
|
882
|
+
toolCallId: request.toolCallId,
|
|
883
|
+
toolName: request.toolName,
|
|
884
|
+
target: targetUri,
|
|
885
|
+
action: request.action,
|
|
886
|
+
risk: request.risk,
|
|
369
887
|
status: 'pending',
|
|
370
|
-
assignedTo
|
|
371
|
-
|
|
888
|
+
assignedTo,
|
|
889
|
+
context,
|
|
890
|
+
...(approvalOptions ? { approvalOptions } : {}),
|
|
891
|
+
policyVersion,
|
|
372
892
|
createdAt: now,
|
|
893
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
373
894
|
});
|
|
374
|
-
|
|
895
|
+
const requestAudit = {
|
|
375
896
|
id: crypto.randomUUID(),
|
|
376
897
|
action: 'approval_requested',
|
|
377
|
-
actor:
|
|
898
|
+
actor: subject.actorUri,
|
|
378
899
|
actorRole: 'secretary',
|
|
379
|
-
onBehalfOf
|
|
900
|
+
onBehalfOf,
|
|
380
901
|
session: sessionUri,
|
|
381
|
-
|
|
902
|
+
entry: requestEntry,
|
|
903
|
+
toolCallId: request.toolCallId,
|
|
904
|
+
toolName: request.toolName,
|
|
382
905
|
approval: approvalUri,
|
|
383
|
-
|
|
384
|
-
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
906
|
+
policyVersion,
|
|
385
907
|
createdAt: now,
|
|
386
|
-
}
|
|
387
|
-
await store.
|
|
908
|
+
};
|
|
909
|
+
await warnOnly(activeRuntime, () => store.insertAudit(requestAudit));
|
|
910
|
+
await warnOnly(activeRuntime, () => store.insertInboxNotification({
|
|
388
911
|
id: crypto.randomUUID(),
|
|
389
|
-
actor:
|
|
912
|
+
actor: subject.actorUri,
|
|
390
913
|
object: approvalUri,
|
|
391
914
|
createdAt: now,
|
|
392
|
-
})
|
|
915
|
+
}));
|
|
393
916
|
return normalizeApprovalSummary({
|
|
394
917
|
id: approvalId,
|
|
918
|
+
approvalUri,
|
|
395
919
|
session: sessionUri,
|
|
396
|
-
toolCallId,
|
|
397
|
-
toolName:
|
|
398
|
-
target:
|
|
399
|
-
action:
|
|
400
|
-
risk:
|
|
920
|
+
toolCallId: request.toolCallId,
|
|
921
|
+
toolName: request.toolName,
|
|
922
|
+
target: targetUri,
|
|
923
|
+
action: request.action,
|
|
924
|
+
risk: request.risk,
|
|
401
925
|
status: 'pending',
|
|
402
|
-
assignedTo
|
|
403
|
-
|
|
926
|
+
assignedTo,
|
|
927
|
+
context,
|
|
928
|
+
...(approvalOptions ? { approvalOptions } : {}),
|
|
929
|
+
policyVersion,
|
|
404
930
|
createdAt: now,
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
}]);
|
|
931
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
932
|
+
});
|
|
418
933
|
});
|
|
419
934
|
}
|
|
420
935
|
export async function waitForRemoteWatchApproval(options) {
|
|
@@ -424,10 +939,13 @@ export async function waitForRemoteWatchApproval(options) {
|
|
|
424
939
|
if (options.signal?.aborted) {
|
|
425
940
|
throw createAbortError();
|
|
426
941
|
}
|
|
427
|
-
const
|
|
428
|
-
|
|
942
|
+
const row = await readRemoteApprovalRow(store, {
|
|
943
|
+
approvalId: options.approvalId,
|
|
944
|
+
approvalUri: options.approvalUri,
|
|
945
|
+
});
|
|
429
946
|
if (!row) {
|
|
430
|
-
|
|
947
|
+
await activeRuntime.sleep(options.pollMs ?? DEFAULT_REMOTE_APPROVAL_POLL_MS);
|
|
948
|
+
continue;
|
|
431
949
|
}
|
|
432
950
|
const decision = decisionFromApprovalRow(row);
|
|
433
951
|
if (decision) {
|
|
@@ -441,17 +959,20 @@ export async function requestRemoteWatchApproval(options) {
|
|
|
441
959
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
442
960
|
const delegated = await withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
443
961
|
const grants = await store.listGrants();
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
962
|
+
return resolveSemanticGrantDecision({
|
|
963
|
+
runtime: activeRuntime,
|
|
964
|
+
grants,
|
|
965
|
+
record: options.record,
|
|
966
|
+
request: options.request,
|
|
967
|
+
requestContext: buildWatchGrantRequestContext({
|
|
968
|
+
webId,
|
|
969
|
+
record: options.record,
|
|
970
|
+
request: options.request,
|
|
971
|
+
}),
|
|
972
|
+
});
|
|
452
973
|
});
|
|
453
974
|
if (delegated) {
|
|
454
|
-
return
|
|
975
|
+
return delegated;
|
|
455
976
|
}
|
|
456
977
|
const summary = await createRemoteWatchApproval({
|
|
457
978
|
record: options.record,
|
|
@@ -460,6 +981,62 @@ export async function requestRemoteWatchApproval(options) {
|
|
|
460
981
|
});
|
|
461
982
|
return waitForRemoteWatchApproval({
|
|
462
983
|
approvalId: summary.id,
|
|
984
|
+
approvalUri: summary.approvalUri,
|
|
985
|
+
pollMs: options.pollMs,
|
|
986
|
+
signal: options.signal,
|
|
987
|
+
runtime: activeRuntime,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
export async function resolveExistingRemoteWatchGrant(options) {
|
|
991
|
+
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
992
|
+
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
993
|
+
const grants = await store.listGrants();
|
|
994
|
+
return resolveSemanticGrantDecision({
|
|
995
|
+
runtime: activeRuntime,
|
|
996
|
+
grants,
|
|
997
|
+
record: options.record,
|
|
998
|
+
request: options.request,
|
|
999
|
+
requestContext: buildWatchGrantRequestContext({
|
|
1000
|
+
webId,
|
|
1001
|
+
record: options.record,
|
|
1002
|
+
request: options.request,
|
|
1003
|
+
}),
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
export async function requestRemoteApproval(options) {
|
|
1008
|
+
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
1009
|
+
const delegated = await withRemoteApprovalStore(activeRuntime, async ({ store, webId, stored }) => {
|
|
1010
|
+
const subject = typeof options.subject === 'function'
|
|
1011
|
+
? options.subject({ webId, stored })
|
|
1012
|
+
: options.subject;
|
|
1013
|
+
const request = typeof options.request === 'function'
|
|
1014
|
+
? options.request({ webId, stored, sessionUri: subject.sessionUri })
|
|
1015
|
+
: options.request;
|
|
1016
|
+
const grants = await store.listGrants();
|
|
1017
|
+
const requestContext = buildGenericGrantRequestContext({ subject, request });
|
|
1018
|
+
return resolveSemanticGrantDecision({
|
|
1019
|
+
runtime: activeRuntime,
|
|
1020
|
+
grants,
|
|
1021
|
+
request: {
|
|
1022
|
+
...request,
|
|
1023
|
+
session: subject.sessionUri,
|
|
1024
|
+
target: requestContext.target,
|
|
1025
|
+
},
|
|
1026
|
+
requestContext,
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
if (delegated) {
|
|
1030
|
+
return delegated;
|
|
1031
|
+
}
|
|
1032
|
+
const summary = await createRemoteApproval({
|
|
1033
|
+
subject: options.subject,
|
|
1034
|
+
request: options.request,
|
|
1035
|
+
runtime: activeRuntime,
|
|
1036
|
+
});
|
|
1037
|
+
return waitForRemoteWatchApproval({
|
|
1038
|
+
approvalId: summary.id,
|
|
1039
|
+
approvalUri: summary.approvalUri,
|
|
463
1040
|
pollMs: options.pollMs,
|
|
464
1041
|
signal: options.signal,
|
|
465
1042
|
runtime: activeRuntime,
|
|
@@ -469,12 +1046,9 @@ export async function listRemoteWatchApprovals(options = {}) {
|
|
|
469
1046
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
470
1047
|
const requestedStatus = options.status ?? 'pending';
|
|
471
1048
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
472
|
-
const
|
|
473
|
-
store.listApprovals(),
|
|
474
|
-
store.listAudits(),
|
|
475
|
-
]);
|
|
1049
|
+
const approvals = await store.listApprovals();
|
|
476
1050
|
return approvals
|
|
477
|
-
.map((row) => normalizeApprovalSummary(row
|
|
1051
|
+
.map((row) => normalizeApprovalSummary(row))
|
|
478
1052
|
.filter((summary) => !summary.assignedTo || summary.assignedTo === webId)
|
|
479
1053
|
.filter((summary) => requestedStatus === 'all' || summary.status === requestedStatus)
|
|
480
1054
|
.sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
@@ -483,96 +1057,124 @@ export async function listRemoteWatchApprovals(options = {}) {
|
|
|
483
1057
|
export async function resolveRemoteWatchApproval(options) {
|
|
484
1058
|
const activeRuntime = options.runtime ?? await createDefaultRuntime();
|
|
485
1059
|
return withRemoteApprovalStore(activeRuntime, async ({ store, webId }) => {
|
|
486
|
-
const
|
|
487
|
-
|
|
1060
|
+
const row = await readRemoteApprovalRow(store, {
|
|
1061
|
+
approvalId: options.approvalId,
|
|
1062
|
+
approvalUri: options.approvalUri,
|
|
1063
|
+
});
|
|
488
1064
|
if (!row) {
|
|
489
1065
|
throw new Error(`Remote approval not found: ${options.approvalId}`);
|
|
490
1066
|
}
|
|
491
1067
|
if (row.status !== 'pending') {
|
|
492
|
-
|
|
493
|
-
return normalizeApprovalSummary(row, audits);
|
|
1068
|
+
return normalizeApprovalSummary(row);
|
|
494
1069
|
}
|
|
495
1070
|
const now = activeRuntime.now();
|
|
496
|
-
const approvalUri =
|
|
1071
|
+
const approvalUri = normalizeString(row.approvalUri)
|
|
1072
|
+
?? store.resolveApprovalReference({ id: row.id, createdAt: row.createdAt }).iri;
|
|
497
1073
|
const nextStatus = options.decision === 'accept' || options.decision === 'accept_for_session'
|
|
498
1074
|
? 'approved'
|
|
499
1075
|
: 'rejected';
|
|
1076
|
+
const decisionRole = options.decisionRole ?? 'human';
|
|
500
1077
|
await store.updateApproval(row.id, {
|
|
1078
|
+
approvalUri,
|
|
501
1079
|
status: nextStatus,
|
|
502
1080
|
decisionBy: webId,
|
|
503
|
-
decisionRole
|
|
1081
|
+
decisionRole,
|
|
504
1082
|
onBehalfOf: webId,
|
|
505
1083
|
reason: encodeDecisionReason(options.decision, options.note),
|
|
506
1084
|
resolvedAt: now,
|
|
507
1085
|
});
|
|
508
|
-
await store.insertAudit({
|
|
1086
|
+
await warnOnly(activeRuntime, () => store.insertAudit({
|
|
509
1087
|
id: crypto.randomUUID(),
|
|
510
1088
|
action: nextStatus === 'approved' ? 'approval_approved' : 'approval_rejected',
|
|
511
1089
|
actor: webId,
|
|
512
|
-
actorRole:
|
|
1090
|
+
actorRole: decisionRole,
|
|
513
1091
|
onBehalfOf: webId,
|
|
514
1092
|
session: row.session,
|
|
1093
|
+
entry: approvalUri,
|
|
515
1094
|
toolCallId: row.toolCallId,
|
|
1095
|
+
toolName: row.toolName,
|
|
516
1096
|
approval: approvalUri,
|
|
517
|
-
context: JSON.stringify({
|
|
518
|
-
decision: options.decision,
|
|
519
|
-
...(options.note?.trim() ? { note: options.note.trim() } : {}),
|
|
520
|
-
}),
|
|
521
1097
|
policyVersion: REMOTE_APPROVAL_POLICY_VERSION,
|
|
522
1098
|
createdAt: now,
|
|
523
|
-
});
|
|
1099
|
+
}));
|
|
524
1100
|
if (options.decision === 'accept_for_session') {
|
|
525
1101
|
const grantId = crypto.randomUUID();
|
|
1102
|
+
const body = grantWikiBodyFromApproval(row, options.grantWikiBody);
|
|
526
1103
|
await store.insertGrant({
|
|
527
1104
|
id: grantId,
|
|
528
1105
|
target: row.target,
|
|
529
1106
|
action: row.action,
|
|
1107
|
+
title: grantWikiTitleFromApproval(row, options.grantWikiTitle),
|
|
1108
|
+
summary: grantWikiSummaryFromApproval(row, options.grantWikiSummary),
|
|
1109
|
+
body,
|
|
1110
|
+
schema: buildGrantSchemaUri(webId),
|
|
1111
|
+
pageKind: 'autonomy-grant',
|
|
1112
|
+
wikiStatus: 'active',
|
|
1113
|
+
tags: grantWikiTagsFromApproval(row, options.grantWikiTags),
|
|
1114
|
+
source: 'approval',
|
|
1115
|
+
sourceHash: grantSourceHash(row),
|
|
1116
|
+
compiledAt: now,
|
|
1117
|
+
compiledFrom: [approvalUri],
|
|
1118
|
+
related: [row.session],
|
|
530
1119
|
effect: 'allow',
|
|
531
1120
|
riskCeiling: row.risk,
|
|
1121
|
+
policy: grantIndexTextFromWikiBody(body),
|
|
1122
|
+
context: grantContextFromApproval(row),
|
|
532
1123
|
decisionBy: webId,
|
|
533
|
-
decisionRole
|
|
1124
|
+
decisionRole,
|
|
534
1125
|
onBehalfOf: webId,
|
|
535
1126
|
createdAt: now,
|
|
536
1127
|
});
|
|
537
|
-
await store.insertInboxNotification({
|
|
1128
|
+
await warnOnly(activeRuntime, () => store.insertInboxNotification({
|
|
538
1129
|
id: crypto.randomUUID(),
|
|
539
1130
|
actor: webId,
|
|
540
|
-
object:
|
|
1131
|
+
object: store.resolveGrantReference({ id: grantId }).iri,
|
|
541
1132
|
createdAt: now,
|
|
542
|
-
})
|
|
1133
|
+
}));
|
|
543
1134
|
}
|
|
544
|
-
await store.insertInboxNotification({
|
|
1135
|
+
await warnOnly(activeRuntime, () => store.insertInboxNotification({
|
|
545
1136
|
id: crypto.randomUUID(),
|
|
546
1137
|
actor: webId,
|
|
547
1138
|
object: approvalUri,
|
|
548
1139
|
createdAt: now,
|
|
549
|
-
})
|
|
1140
|
+
}));
|
|
550
1141
|
const nextRow = {
|
|
551
1142
|
...row,
|
|
1143
|
+
approvalUri,
|
|
552
1144
|
status: nextStatus,
|
|
553
1145
|
decisionBy: webId,
|
|
554
|
-
decisionRole
|
|
1146
|
+
decisionRole,
|
|
555
1147
|
onBehalfOf: webId,
|
|
556
1148
|
reason: encodeDecisionReason(options.decision, options.note),
|
|
557
1149
|
resolvedAt: now,
|
|
558
1150
|
};
|
|
559
|
-
|
|
560
|
-
return normalizeApprovalSummary(nextRow, audits);
|
|
1151
|
+
return normalizeApprovalSummary(nextRow);
|
|
561
1152
|
});
|
|
562
1153
|
}
|
|
1154
|
+
async function readRemoteApprovalRow(store, options) {
|
|
1155
|
+
if (store.findApproval) {
|
|
1156
|
+
const row = await store.findApproval(options.approvalId, {
|
|
1157
|
+
approvalUri: options.approvalUri,
|
|
1158
|
+
});
|
|
1159
|
+
return row;
|
|
1160
|
+
}
|
|
1161
|
+
const approvals = await store.listApprovals();
|
|
1162
|
+
return approvals.find((entry) => entry.id === options.approvalId) ?? null;
|
|
1163
|
+
}
|
|
563
1164
|
export const __podApprovalInternal = {
|
|
564
1165
|
createAbortError,
|
|
1166
|
+
createDefaultRuntime,
|
|
565
1167
|
buildActionUri,
|
|
566
|
-
buildRequestAuditContext,
|
|
567
1168
|
buildRisk,
|
|
568
1169
|
buildToolName,
|
|
1170
|
+
createNativeRemoteApprovalStore,
|
|
569
1171
|
extractToolCallId,
|
|
570
1172
|
decisionFromApprovalRow,
|
|
571
1173
|
encodeDecisionReason,
|
|
572
1174
|
formatSummaryHeadline,
|
|
1175
|
+
readRemoteApprovalRow,
|
|
573
1176
|
isRemoteApprovalAbortError,
|
|
574
1177
|
normalizeApprovalSummary,
|
|
575
1178
|
parseDecisionReason,
|
|
576
|
-
parseRequestAuditContext,
|
|
577
1179
|
};
|
|
578
1180
|
//# sourceMappingURL=pod-approval.js.map
|