@corenel/harness 0.2.0 → 0.3.2
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/acp/agentMap.d.ts +34 -0
- package/dist/acp/agentMap.d.ts.map +1 -0
- package/dist/acp/agentMap.js +110 -0
- package/dist/acp/agentMap.js.map +1 -0
- package/dist/acp/index.d.ts +4 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +6 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/policyMap.d.ts +55 -0
- package/dist/acp/policyMap.d.ts.map +1 -0
- package/dist/acp/policyMap.js +121 -0
- package/dist/acp/policyMap.js.map +1 -0
- package/dist/acp/types.d.ts +41 -0
- package/dist/acp/types.d.ts.map +1 -0
- package/dist/acp/types.js +14 -0
- package/dist/acp/types.js.map +1 -0
- package/dist/api/backend.d.ts +39 -0
- package/dist/api/backend.d.ts.map +1 -0
- package/dist/api/backend.js +126 -0
- package/dist/api/backend.js.map +1 -0
- package/dist/api/client.d.ts +95 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +212 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/fileMirrorSource.d.ts +24 -0
- package/dist/api/fileMirrorSource.d.ts.map +1 -0
- package/dist/api/fileMirrorSource.js +59 -0
- package/dist/api/fileMirrorSource.js.map +1 -0
- package/dist/api/mirror.d.ts +102 -0
- package/dist/api/mirror.d.ts.map +1 -0
- package/dist/api/mirror.js +204 -0
- package/dist/api/mirror.js.map +1 -0
- package/dist/api/mirrorScheduler.d.ts +56 -0
- package/dist/api/mirrorScheduler.d.ts.map +1 -0
- package/dist/api/mirrorScheduler.js +167 -0
- package/dist/api/mirrorScheduler.js.map +1 -0
- package/dist/core/loop.d.ts +2 -0
- package/dist/core/loop.d.ts.map +1 -1
- package/dist/core/loop.js +1 -0
- package/dist/core/loop.js.map +1 -1
- package/dist/install/lockfile.d.ts +79 -0
- package/dist/install/lockfile.d.ts.map +1 -0
- package/dist/install/lockfile.js +155 -0
- package/dist/install/lockfile.js.map +1 -0
- package/dist/install/store.d.ts +13 -0
- package/dist/install/store.d.ts.map +1 -0
- package/dist/install/store.js +38 -0
- package/dist/install/store.js.map +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +6 -1
- package/dist/log.js.map +1 -1
- package/dist/prompts/host.d.ts +4 -0
- package/dist/prompts/host.d.ts.map +1 -1
- package/dist/prompts/host.js.map +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +4 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/library/defaults.generated.js +1 -1
- package/dist/prompts/library/defaults.generated.js.map +1 -1
- package/dist/providers/gateway.d.ts +3 -0
- package/dist/providers/gateway.d.ts.map +1 -1
- package/dist/providers/gateway.js +3 -0
- package/dist/providers/gateway.js.map +1 -1
- package/dist/sessions/apiLeaseStore.d.ts +10 -0
- package/dist/sessions/apiLeaseStore.d.ts.map +1 -0
- package/dist/sessions/apiLeaseStore.js +41 -0
- package/dist/sessions/apiLeaseStore.js.map +1 -0
- package/dist/sessions/head.d.ts +90 -0
- package/dist/sessions/head.d.ts.map +1 -0
- package/dist/sessions/head.js +82 -0
- package/dist/sessions/head.js.map +1 -0
- package/dist/sessions/inflight.d.ts +58 -0
- package/dist/sessions/inflight.d.ts.map +1 -0
- package/dist/sessions/inflight.js +0 -0
- package/dist/sessions/inflight.js.map +1 -0
- package/dist/sessions/lease.d.ts +59 -0
- package/dist/sessions/lease.d.ts.map +1 -0
- package/dist/sessions/lease.js +115 -0
- package/dist/sessions/lease.js.map +1 -0
- package/dist/sessions/leaseStore.d.ts +46 -0
- package/dist/sessions/leaseStore.d.ts.map +1 -0
- package/dist/sessions/leaseStore.js +58 -0
- package/dist/sessions/leaseStore.js.map +1 -0
- package/dist/sessions/timeline.d.ts +134 -0
- package/dist/sessions/timeline.d.ts.map +1 -0
- package/dist/sessions/timeline.js +127 -0
- package/dist/sessions/timeline.js.map +1 -0
- package/dist/sidecar/host.d.ts +223 -3
- package/dist/sidecar/host.d.ts.map +1 -1
- package/dist/sidecar/host.js +592 -2
- package/dist/sidecar/host.js.map +1 -1
- package/dist/sidecar/server.d.ts +124 -2
- package/dist/sidecar/server.d.ts.map +1 -1
- package/dist/sidecar/server.js +608 -9
- package/dist/sidecar/server.js.map +1 -1
- package/dist/snapshot/effects.d.ts +69 -0
- package/dist/snapshot/effects.d.ts.map +1 -0
- package/dist/snapshot/effects.js +67 -0
- package/dist/snapshot/effects.js.map +1 -0
- package/dist/snapshot/manifest.d.ts +73 -0
- package/dist/snapshot/manifest.d.ts.map +1 -0
- package/dist/snapshot/manifest.js +141 -0
- package/dist/snapshot/manifest.js.map +1 -0
- package/dist/snapshot/tree.d.ts +62 -0
- package/dist/snapshot/tree.d.ts.map +1 -0
- package/dist/snapshot/tree.js +147 -0
- package/dist/snapshot/tree.js.map +1 -0
- package/dist/tools/builtins.d.ts.map +1 -1
- package/dist/tools/builtins.js +61 -2
- package/dist/tools/builtins.js.map +1 -1
- package/dist/tools/namespaces.d.ts +1 -1
- package/dist/tools/namespaces.d.ts.map +1 -1
- package/dist/tools/namespaces.js +24 -0
- package/dist/tools/namespaces.js.map +1 -1
- package/dist/tools/todo.d.ts.map +1 -1
- package/dist/tools/todo.js +12 -0
- package/dist/tools/todo.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentEvent } from '@corenel/protocol';
|
|
2
|
+
import type { AcpToolKind } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Describe one of our tools in ACP's `kind` vocabulary.
|
|
5
|
+
*
|
|
6
|
+
* Accepts either a bare wire name (`write_file`) or a policy address
|
|
7
|
+
* (`fs:write_file`), because callers have one or the other depending on where in
|
|
8
|
+
* the loop they sit.
|
|
9
|
+
*
|
|
10
|
+
* The `fs` namespace fallback is `read` rather than `edit` on purpose: a tool we
|
|
11
|
+
* cannot name is more likely a reader, and this value drives only the client's
|
|
12
|
+
* icon and progress display. The AUTHORIZATION for that same call is decided by
|
|
13
|
+
* our own Policy before we ever emit this.
|
|
14
|
+
*/
|
|
15
|
+
export declare function kindForTool(nameOrAddress: string): AcpToolKind;
|
|
16
|
+
/** An ACP `session/update` payload, as the agent sends it. */
|
|
17
|
+
export interface AcpSessionUpdate {
|
|
18
|
+
sessionUpdate: string;
|
|
19
|
+
[k: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Render one agent-loop event as an ACP `session/update`, or null when the event
|
|
23
|
+
* has no ACP equivalent worth sending.
|
|
24
|
+
*
|
|
25
|
+
* Returning null rather than a placeholder matters: our loop emits far more than
|
|
26
|
+
* ACP models (budget breaches, heals, policy notes, usage), and forwarding those
|
|
27
|
+
* as text would clutter a client's transcript with our internals. A client that
|
|
28
|
+
* wants them can read the run's RUN.jsonl.
|
|
29
|
+
*/
|
|
30
|
+
export declare function eventToSessionUpdate(ev: AgentEvent): AcpSessionUpdate | null;
|
|
31
|
+
/** Map our loop's stop reason onto ACP's. Unknown reasons report `end_turn`
|
|
32
|
+
* rather than inventing a value the client would not recognize. */
|
|
33
|
+
export declare function stopReasonForAcp(reason: string): string;
|
|
34
|
+
//# sourceMappingURL=agentMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentMap.d.ts","sourceRoot":"","sources":["../../acp/agentMap.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAmC3C;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,CAQ9D;AAED,8DAA8D;AAC9D,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,GAAG,gBAAgB,GAAG,IAAI,CA6B5E;AAED;mEACmE;AACnE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAavD"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Explicit kinds for built-in tools whose namespace alone is ambiguous. */
|
|
2
|
+
const TOOL_KINDS = {
|
|
3
|
+
read_file: 'read',
|
|
4
|
+
stat_file: 'read',
|
|
5
|
+
list_files: 'read',
|
|
6
|
+
search_files: 'search',
|
|
7
|
+
search_sessions: 'search',
|
|
8
|
+
search_packages: 'search',
|
|
9
|
+
write_file: 'edit',
|
|
10
|
+
create_file: 'edit',
|
|
11
|
+
propose_edit: 'edit',
|
|
12
|
+
delete_file: 'delete',
|
|
13
|
+
rename_file: 'move',
|
|
14
|
+
web_search: 'fetch',
|
|
15
|
+
compile_prompd: 'other',
|
|
16
|
+
todo_write: 'think',
|
|
17
|
+
ask_user: 'think',
|
|
18
|
+
};
|
|
19
|
+
/** Namespace fallbacks, for tools not named above (including dynamic MCP and
|
|
20
|
+
* skill tools, which cannot be enumerated). */
|
|
21
|
+
const NAMESPACE_KINDS = {
|
|
22
|
+
fs: 'read',
|
|
23
|
+
shell: 'execute',
|
|
24
|
+
web: 'fetch',
|
|
25
|
+
memory: 'other',
|
|
26
|
+
agent: 'think',
|
|
27
|
+
prompd: 'other',
|
|
28
|
+
system: 'other',
|
|
29
|
+
mcp: 'other',
|
|
30
|
+
skill: 'other',
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Describe one of our tools in ACP's `kind` vocabulary.
|
|
34
|
+
*
|
|
35
|
+
* Accepts either a bare wire name (`write_file`) or a policy address
|
|
36
|
+
* (`fs:write_file`), because callers have one or the other depending on where in
|
|
37
|
+
* the loop they sit.
|
|
38
|
+
*
|
|
39
|
+
* The `fs` namespace fallback is `read` rather than `edit` on purpose: a tool we
|
|
40
|
+
* cannot name is more likely a reader, and this value drives only the client's
|
|
41
|
+
* icon and progress display. The AUTHORIZATION for that same call is decided by
|
|
42
|
+
* our own Policy before we ever emit this.
|
|
43
|
+
*/
|
|
44
|
+
export function kindForTool(nameOrAddress) {
|
|
45
|
+
const colon = nameOrAddress.indexOf(':');
|
|
46
|
+
const ns = colon > 0 ? nameOrAddress.slice(0, colon) : '';
|
|
47
|
+
const name = colon > 0 ? nameOrAddress.slice(colon + 1) : nameOrAddress;
|
|
48
|
+
const byName = TOOL_KINDS[name];
|
|
49
|
+
if (byName)
|
|
50
|
+
return byName;
|
|
51
|
+
if (ns)
|
|
52
|
+
return NAMESPACE_KINDS[ns] ?? 'other';
|
|
53
|
+
return 'other';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Render one agent-loop event as an ACP `session/update`, or null when the event
|
|
57
|
+
* has no ACP equivalent worth sending.
|
|
58
|
+
*
|
|
59
|
+
* Returning null rather than a placeholder matters: our loop emits far more than
|
|
60
|
+
* ACP models (budget breaches, heals, policy notes, usage), and forwarding those
|
|
61
|
+
* as text would clutter a client's transcript with our internals. A client that
|
|
62
|
+
* wants them can read the run's RUN.jsonl.
|
|
63
|
+
*/
|
|
64
|
+
export function eventToSessionUpdate(ev) {
|
|
65
|
+
switch (ev.type) {
|
|
66
|
+
case 'assistant-delta':
|
|
67
|
+
return { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: ev.delta } };
|
|
68
|
+
case 'tool-call':
|
|
69
|
+
return {
|
|
70
|
+
sessionUpdate: 'tool_call',
|
|
71
|
+
toolCallId: ev.id,
|
|
72
|
+
title: ev.name,
|
|
73
|
+
kind: kindForTool(ev.name),
|
|
74
|
+
status: 'in_progress',
|
|
75
|
+
rawInput: ev.args,
|
|
76
|
+
};
|
|
77
|
+
case 'tool-result':
|
|
78
|
+
return {
|
|
79
|
+
sessionUpdate: 'tool_call_update',
|
|
80
|
+
toolCallId: ev.id,
|
|
81
|
+
status: ev.isError ? 'failed' : 'completed',
|
|
82
|
+
rawOutput: ev.result,
|
|
83
|
+
};
|
|
84
|
+
case 'error':
|
|
85
|
+
return { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: `Error: ${ev.message}` } };
|
|
86
|
+
default:
|
|
87
|
+
// 'assistant' duplicates the deltas already streamed; 'turn', 'usage',
|
|
88
|
+
// 'heal', 'offload', 'permission*', 'policy-violation', 'budget-*',
|
|
89
|
+
// 'artifact' and 'done' have no ACP shape. 'done' is reported as the
|
|
90
|
+
// prompt's stopReason instead, which is where ACP expects it.
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Map our loop's stop reason onto ACP's. Unknown reasons report `end_turn`
|
|
95
|
+
* rather than inventing a value the client would not recognize. */
|
|
96
|
+
export function stopReasonForAcp(reason) {
|
|
97
|
+
switch (reason) {
|
|
98
|
+
case 'cancelled':
|
|
99
|
+
case 'aborted':
|
|
100
|
+
return 'cancelled';
|
|
101
|
+
case 'max-turns':
|
|
102
|
+
case 'max_turns':
|
|
103
|
+
return 'max_turn_requests';
|
|
104
|
+
case 'refusal':
|
|
105
|
+
return 'refusal';
|
|
106
|
+
default:
|
|
107
|
+
return 'end_turn';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=agentMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentMap.js","sourceRoot":"","sources":["../../acp/agentMap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { acpToSubject, acpDisplayTitle, levelToOptionId, ACP_KIND_ADDRESSES, ALLOW_KINDS, REJECT_KINDS, type AcpPermissionAnswer, } from './policyMap';
|
|
2
|
+
export { kindForTool, eventToSessionUpdate, stopReasonForAcp, type AcpSessionUpdate, } from './agentMap';
|
|
3
|
+
export type { AcpToolKind, AcpToolCallLocation, AcpToolCallUpdate, AcpPermissionOption, AcpPermissionOptionKind, AcpRequestPermissionRequest, } from './types';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../acp/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/* ACP module barrel — the client-side translation between the Agent Client
|
|
2
|
+
* Protocol and this harness's governance layer. Pure: no transport, no node.
|
|
3
|
+
* The wire lives in packages/sidecar/src/acp/. */
|
|
4
|
+
export { acpToSubject, acpDisplayTitle, levelToOptionId, ACP_KIND_ADDRESSES, ALLOW_KINDS, REJECT_KINDS, } from './policyMap';
|
|
5
|
+
export { kindForTool, eventToSessionUpdate, stopReasonForAcp, } from './agentMap';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../acp/index.ts"],"names":[],"mappings":"AAAA;;kDAEkD;AAClD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,YAAY,GAEb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,gBAAgB,GAEjB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PermissionSubject } from '../guardrail/permission-service';
|
|
2
|
+
import type { PermissionDecision } from '@corenel/protocol';
|
|
3
|
+
import type { AcpPermissionOption, AcpPermissionOptionKind, AcpToolCallUpdate } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Translate an ACP tool call into the subject our PermissionService resolves.
|
|
6
|
+
*
|
|
7
|
+
* THE INVARIANT: an unknown, absent, or unrecognized `kind` is MUTATING.
|
|
8
|
+
* ACP marks `kind` optional with a default of 'other', so an agent that does not
|
|
9
|
+
* label its calls reports nothing at all. Were that mapped to non-mutating,
|
|
10
|
+
* STANDARD_POLICY — which sets no defaultPermission and falls through to the
|
|
11
|
+
* read/write heuristic — would resolve it via `defaultForReadOnly` ('allow') and
|
|
12
|
+
* silently auto-approve a destructive call. Absence must never read as
|
|
13
|
+
* permissive.
|
|
14
|
+
*
|
|
15
|
+
* `name` is deliberately the SAME string as `address`, not the human title:
|
|
16
|
+
* PermissionService matches policy globs against a subject's address AND its
|
|
17
|
+
* name (subjectMatches), and matches session pre-approvals against the name
|
|
18
|
+
* alone (decide -> sessionAllow). Letting agent-supplied text be the name would
|
|
19
|
+
* hand an external process a second, softer path into policy matching — a title
|
|
20
|
+
* of `fs:acp_read` could satisfy a rule or a pre-approval written for the real
|
|
21
|
+
* thing. One identity, derived entirely from data we control. The human-readable
|
|
22
|
+
* title is carried separately by acpDisplayTitle() for the approval prompt.
|
|
23
|
+
*/
|
|
24
|
+
export declare function acpToSubject(call: AcpToolCallUpdate): PermissionSubject;
|
|
25
|
+
/** The human-facing label for an approval prompt. Raw agent text — safe to
|
|
26
|
+
* DISPLAY, never safe to match policy against (see acpToSubject). */
|
|
27
|
+
export declare function acpDisplayTitle(call: AcpToolCallUpdate): string;
|
|
28
|
+
/** Our answer, expressed in the agent's own option vocabulary. */
|
|
29
|
+
export type AcpPermissionAnswer = {
|
|
30
|
+
ok: true;
|
|
31
|
+
optionId: string;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
reason: string;
|
|
35
|
+
};
|
|
36
|
+
declare const ALLOW_KINDS: AcpPermissionOptionKind[];
|
|
37
|
+
declare const REJECT_KINDS: AcpPermissionOptionKind[];
|
|
38
|
+
/**
|
|
39
|
+
* Turn a resolved decision into an `optionId` drawn from what the agent OFFERED.
|
|
40
|
+
* We never synthesize an id: the agent defines its own option set, and answering
|
|
41
|
+
* with an unknown id is a protocol error.
|
|
42
|
+
*
|
|
43
|
+
* FAILS CLOSED. If no acceptable option exists we return `ok: false` rather than
|
|
44
|
+
* throwing or falling back to something permissive — an agent that offers only
|
|
45
|
+
* allow-shaped options must not thereby win a denial. The caller reports the
|
|
46
|
+
* refusal and cancels the call.
|
|
47
|
+
*/
|
|
48
|
+
export declare function levelToOptionId(decision: PermissionDecision, options: readonly AcpPermissionOption[], opts?: {
|
|
49
|
+
sessionApproved?: boolean;
|
|
50
|
+
}): AcpPermissionAnswer;
|
|
51
|
+
/** Exported for tests and for a capabilities UI that wants to show what a
|
|
52
|
+
* proxied agent can address. */
|
|
53
|
+
export declare const ACP_KIND_ADDRESSES: readonly string[];
|
|
54
|
+
export { ALLOW_KINDS, REJECT_KINDS };
|
|
55
|
+
//# sourceMappingURL=policyMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyMap.d.ts","sourceRoot":"","sources":["../../acp/policyMap.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EAElB,MAAM,SAAS,CAAC;AAiDjB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,CAQvE;AAED;qEACqE;AACrE,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAI/D;AAED,kEAAkE;AAClE,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,QAAA,MAAM,WAAW,EAAE,uBAAuB,EAAmC,CAAC;AAC9E,QAAA,MAAM,YAAY,EAAE,uBAAuB,EAAqC,CAAC;AAajF;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,SAAS,mBAAmB,EAAE,EACvC,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,mBAAmB,CAarB;AAED;gCACgC;AAChC,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAEJ,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/** How each ACP kind addresses into our policy namespaces, and whether it has
|
|
2
|
+
* side effects. Anything NOT in this table is mutating — see toSafety(). */
|
|
3
|
+
const KIND_MAP = {
|
|
4
|
+
read: { ns: 'fs', mutates: false },
|
|
5
|
+
search: { ns: 'fs', mutates: false },
|
|
6
|
+
think: { ns: 'agent', mutates: false },
|
|
7
|
+
edit: { ns: 'fs', mutates: true },
|
|
8
|
+
delete: { ns: 'fs', mutates: true },
|
|
9
|
+
move: { ns: 'fs', mutates: true },
|
|
10
|
+
execute: { ns: 'shell', mutates: true },
|
|
11
|
+
fetch: { ns: 'web', mutates: true },
|
|
12
|
+
other: { ns: 'other', mutates: true },
|
|
13
|
+
};
|
|
14
|
+
/** Upper bound on the slug derived from an agent-supplied title. Long enough to
|
|
15
|
+
* stay readable in a policy file, short enough that a hostile title cannot bloat
|
|
16
|
+
* an address. */
|
|
17
|
+
const SLUG_MAX = 48;
|
|
18
|
+
/**
|
|
19
|
+
* Reduce untrusted agent-supplied text to a policy-safe token.
|
|
20
|
+
*
|
|
21
|
+
* SECURITY. `title` is free text from an external process, and it reaches a
|
|
22
|
+
* policy ADDRESS. Without this, a title of `fs:read` would forge an address in a
|
|
23
|
+
* privileged namespace, and a title containing `*` would inject a glob wildcard
|
|
24
|
+
* into the very expression being matched. Everything outside [a-z0-9_] is
|
|
25
|
+
* collapsed, so neither `:` nor `*` can survive.
|
|
26
|
+
*/
|
|
27
|
+
function slugify(raw) {
|
|
28
|
+
const slug = (raw ?? '')
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
31
|
+
.replace(/^_+|_+$/g, '')
|
|
32
|
+
.slice(0, SLUG_MAX)
|
|
33
|
+
.replace(/_+$/g, '');
|
|
34
|
+
return slug || 'unknown';
|
|
35
|
+
}
|
|
36
|
+
/** Resolve a wire `kind` to its table entry. An absent, unrecognized, or
|
|
37
|
+
* newer-protocol kind lands on `other`, which is MUTATING. */
|
|
38
|
+
function toSafety(kind) {
|
|
39
|
+
if (kind && Object.prototype.hasOwnProperty.call(KIND_MAP, kind)) {
|
|
40
|
+
return KIND_MAP[kind];
|
|
41
|
+
}
|
|
42
|
+
return KIND_MAP.other;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Translate an ACP tool call into the subject our PermissionService resolves.
|
|
46
|
+
*
|
|
47
|
+
* THE INVARIANT: an unknown, absent, or unrecognized `kind` is MUTATING.
|
|
48
|
+
* ACP marks `kind` optional with a default of 'other', so an agent that does not
|
|
49
|
+
* label its calls reports nothing at all. Were that mapped to non-mutating,
|
|
50
|
+
* STANDARD_POLICY — which sets no defaultPermission and falls through to the
|
|
51
|
+
* read/write heuristic — would resolve it via `defaultForReadOnly` ('allow') and
|
|
52
|
+
* silently auto-approve a destructive call. Absence must never read as
|
|
53
|
+
* permissive.
|
|
54
|
+
*
|
|
55
|
+
* `name` is deliberately the SAME string as `address`, not the human title:
|
|
56
|
+
* PermissionService matches policy globs against a subject's address AND its
|
|
57
|
+
* name (subjectMatches), and matches session pre-approvals against the name
|
|
58
|
+
* alone (decide -> sessionAllow). Letting agent-supplied text be the name would
|
|
59
|
+
* hand an external process a second, softer path into policy matching — a title
|
|
60
|
+
* of `fs:acp_read` could satisfy a rule or a pre-approval written for the real
|
|
61
|
+
* thing. One identity, derived entirely from data we control. The human-readable
|
|
62
|
+
* title is carried separately by acpDisplayTitle() for the approval prompt.
|
|
63
|
+
*/
|
|
64
|
+
export function acpToSubject(call) {
|
|
65
|
+
const { ns, mutates } = toSafety(call.kind);
|
|
66
|
+
// A recognized kind addresses by that kind (fs:acp_edit) so one policy rule
|
|
67
|
+
// covers every edit the agent makes. An unrecognized one has no such handle,
|
|
68
|
+
// so it falls back to the sanitized title to stay at least distinguishable.
|
|
69
|
+
const leaf = ns === 'other' ? slugify(call.title) : slugify(call.kind);
|
|
70
|
+
const address = `${ns}:acp_${leaf}`;
|
|
71
|
+
return { name: address, address, mutates };
|
|
72
|
+
}
|
|
73
|
+
/** The human-facing label for an approval prompt. Raw agent text — safe to
|
|
74
|
+
* DISPLAY, never safe to match policy against (see acpToSubject). */
|
|
75
|
+
export function acpDisplayTitle(call) {
|
|
76
|
+
const t = (call.title ?? '').trim();
|
|
77
|
+
if (t)
|
|
78
|
+
return t;
|
|
79
|
+
return call.kind ? `${call.kind} (untitled)` : 'untitled tool call';
|
|
80
|
+
}
|
|
81
|
+
const ALLOW_KINDS = ['allow_once', 'allow_always'];
|
|
82
|
+
const REJECT_KINDS = ['reject_once', 'reject_always'];
|
|
83
|
+
function pick(options, preferred) {
|
|
84
|
+
for (const kind of preferred) {
|
|
85
|
+
const hit = options.find((o) => o.kind === kind);
|
|
86
|
+
if (hit)
|
|
87
|
+
return hit;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Turn a resolved decision into an `optionId` drawn from what the agent OFFERED.
|
|
93
|
+
* We never synthesize an id: the agent defines its own option set, and answering
|
|
94
|
+
* with an unknown id is a protocol error.
|
|
95
|
+
*
|
|
96
|
+
* FAILS CLOSED. If no acceptable option exists we return `ok: false` rather than
|
|
97
|
+
* throwing or falling back to something permissive — an agent that offers only
|
|
98
|
+
* allow-shaped options must not thereby win a denial. The caller reports the
|
|
99
|
+
* refusal and cancels the call.
|
|
100
|
+
*/
|
|
101
|
+
export function levelToOptionId(decision, options, opts = {}) {
|
|
102
|
+
if (decision === 'allow') {
|
|
103
|
+
// A session pre-approval is durable on our side, so prefer the durable
|
|
104
|
+
// option when the agent offers one — it stops the agent re-asking for
|
|
105
|
+
// something the user already blanket-approved.
|
|
106
|
+
const order = opts.sessionApproved
|
|
107
|
+
? ['allow_always', 'allow_once']
|
|
108
|
+
: ['allow_once', 'allow_always'];
|
|
109
|
+
const hit = pick(options, order);
|
|
110
|
+
return hit ? { ok: true, optionId: hit.optionId } : { ok: false, reason: 'agent offered no allow option' };
|
|
111
|
+
}
|
|
112
|
+
const hit = pick(options, REJECT_KINDS);
|
|
113
|
+
return hit ? { ok: true, optionId: hit.optionId } : { ok: false, reason: 'agent offered no reject option' };
|
|
114
|
+
}
|
|
115
|
+
/** Exported for tests and for a capabilities UI that wants to show what a
|
|
116
|
+
* proxied agent can address. */
|
|
117
|
+
export const ACP_KIND_ADDRESSES = Object.entries(KIND_MAP)
|
|
118
|
+
.filter(([kind]) => kind !== 'other')
|
|
119
|
+
.map(([kind, v]) => `${v.ns}:acp_${kind}`);
|
|
120
|
+
export { ALLOW_KINDS, REJECT_KINDS };
|
|
121
|
+
//# sourceMappingURL=policyMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policyMap.js","sourceRoot":"","sources":["../../acp/policyMap.ts"],"names":[],"mappings":"AAuBA;4EAC4E;AAC5E,MAAM,QAAQ,GAA0D;IACtE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;IAClC,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;IACpC,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;IACtC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;IACjC,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;IACnC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;IACjC,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;IACvC,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;IACnC,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;CACtC,CAAC;AAEF;;iBAEiB;AACjB,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB;;;;;;;;GAQG;AACH,SAAS,OAAO,CAAC,GAAuB;IACtC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;SACrB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;SAClB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvB,OAAO,IAAI,IAAI,SAAS,CAAC;AAC3B,CAAC;AAED;8DAC8D;AAC9D,SAAS,QAAQ,CAAC,IAAwB;IACxC,IAAI,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;QACjE,OAAO,QAAQ,CAAC,IAAmB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY,CAAC,IAAuB;IAClD,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,IAAI,GAAG,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,GAAG,EAAE,QAAQ,IAAI,EAAE,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,eAAe,CAAC,IAAuB;IACrD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAChB,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,oBAAoB,CAAC;AACtE,CAAC;AAOD,MAAM,WAAW,GAA8B,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;AAC9E,MAAM,YAAY,GAA8B,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AAEjF,SAAS,IAAI,CACX,OAAuC,EACvC,SAA6C;IAE7C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACjD,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC;IACtB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,QAA4B,EAC5B,OAAuC,EACvC,OAAsC,EAAE;IAExC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,uEAAuE;QACvE,sEAAsE;QACtE,+CAA+C;QAC/C,MAAM,KAAK,GAA8B,IAAI,CAAC,eAAe;YAC3D,CAAC,CAAC,CAAC,cAAc,EAAE,YAAY,CAAC;YAChC,CAAC,CAAC,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;IAC7G,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACxC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gCAAgC,EAAE,CAAC;AAC9G,CAAC;AAED;gCACgC;AAChC,MAAM,CAAC,MAAM,kBAAkB,GAAsB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;KAC1E,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC;KACpC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** How the agent categorizes a tool call. A UI hint in ACP, and our only signal
|
|
2
|
+
* for whether the call has side effects. OPTIONAL on the wire, defaulting to
|
|
3
|
+
* 'other' — see the ACP_KINDS note in policyMap.ts. */
|
|
4
|
+
export type AcpToolKind = 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'other';
|
|
5
|
+
/** Where a tool call acts. Present only when the agent chooses to report it. */
|
|
6
|
+
export interface AcpToolCallLocation {
|
|
7
|
+
path: string;
|
|
8
|
+
line?: number;
|
|
9
|
+
}
|
|
10
|
+
/** The tool-call description carried by a permission request. ACP models this as
|
|
11
|
+
* a partial update, so everything except the id may be absent. */
|
|
12
|
+
export interface AcpToolCallUpdate {
|
|
13
|
+
toolCallId?: string;
|
|
14
|
+
/** Human-readable description of what the tool is doing. UNTRUSTED text from
|
|
15
|
+
* an external process — never interpolate it into an address without
|
|
16
|
+
* sanitizing (policyMap.ts slugifies it). */
|
|
17
|
+
title?: string;
|
|
18
|
+
kind?: AcpToolKind;
|
|
19
|
+
status?: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
20
|
+
locations?: AcpToolCallLocation[];
|
|
21
|
+
/** The original parameters the agent passed to its tool. Opaque to us; passed
|
|
22
|
+
* through to the permission prompt so a human sees what they are approving. */
|
|
23
|
+
rawInput?: unknown;
|
|
24
|
+
}
|
|
25
|
+
/** Scope of a permission answer, as offered by the agent. */
|
|
26
|
+
export type AcpPermissionOptionKind = 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
|
|
27
|
+
/** One choice the agent is willing to accept back. We must answer with an
|
|
28
|
+
* `optionId` drawn from the offered list — never a synthesized one. */
|
|
29
|
+
export interface AcpPermissionOption {
|
|
30
|
+
optionId: string;
|
|
31
|
+
name: string;
|
|
32
|
+
kind: AcpPermissionOptionKind;
|
|
33
|
+
}
|
|
34
|
+
/** `session/request_permission` — an agent asking the CLIENT to authorize a tool
|
|
35
|
+
* call. We are the client, so this is where our Policy is consulted. */
|
|
36
|
+
export interface AcpRequestPermissionRequest {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
toolCall: AcpToolCallUpdate;
|
|
39
|
+
options: AcpPermissionOption[];
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../acp/types.ts"],"names":[],"mappings":"AAaA;;uDAEuD;AACvD,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,MAAM,GACN,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,SAAS,GACT,OAAO,GACP,OAAO,GACP,OAAO,CAAC;AAEZ,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;kEACkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;iDAE6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC5D,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC;mFAC+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,6DAA6D;AAC7D,MAAM,MAAM,uBAAuB,GAC/B,YAAY,GACZ,cAAc,GACd,aAAa,GACb,eAAe,CAAC;AAEpB;uEACuE;AACvE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,uBAAuB,CAAC;CAC/B;AAED;wEACwE;AACxE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAChC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* The subset of the Agent Client Protocol wire format we consume as an ACP
|
|
2
|
+
* CLIENT (driving an external agent such as Claude Code).
|
|
3
|
+
*
|
|
4
|
+
* Hand-written rather than generated from the published schema.json: we touch a
|
|
5
|
+
* handful of fields on two messages, and a generated multi-thousand-line mirror
|
|
6
|
+
* would be a maintenance liability that hides which parts we actually depend on.
|
|
7
|
+
*
|
|
8
|
+
* Source: agentclientprotocol.com/protocol/{tool-calls,schema}. Every field the
|
|
9
|
+
* spec marks optional is optional here too — that faithfulness is load-bearing,
|
|
10
|
+
* because `kind` being absent is exactly the case the policy mapping must fail
|
|
11
|
+
* closed on (see policyMap.ts).
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../acp/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { CorenelApiClient } from './client';
|
|
2
|
+
/** Capabilities a backend may advertise. Strings, not an enum: an older client
|
|
3
|
+
* must be able to read a newer backend's list without choking on a name it does
|
|
4
|
+
* not know. */
|
|
5
|
+
export declare const CAP_SESSIONS = "sessions";
|
|
6
|
+
export declare const CAP_BLOBS = "blobs";
|
|
7
|
+
export declare const CAP_SNAPSHOTS = "snapshots";
|
|
8
|
+
export interface BackendConfig {
|
|
9
|
+
/** e.g. https://api.corenel.ai/api/v1 — trailing slashes are trimmed. */
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
getToken(): Promise<string> | string;
|
|
12
|
+
/** Injected so a test needs no network; defaults to the ambient fetch. */
|
|
13
|
+
fetch?: typeof fetch;
|
|
14
|
+
}
|
|
15
|
+
export interface Backend {
|
|
16
|
+
readonly baseUrl: string;
|
|
17
|
+
readonly client: CorenelApiClient;
|
|
18
|
+
/** What the last probe found. Empty before probing, and empty is honest: we
|
|
19
|
+
* have not asked, so we do not know. */
|
|
20
|
+
capabilities(): readonly string[];
|
|
21
|
+
supports(capability: string): boolean;
|
|
22
|
+
/** Ask the backend what it is. Safe to call more than once. */
|
|
23
|
+
probe(): Promise<readonly string[]>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Point at a backend, or at none.
|
|
27
|
+
*
|
|
28
|
+
* Called by a HOST — the browser app, the sidecar, a hosted runner — and by
|
|
29
|
+
* nothing else. Passing null detaches, which a config change that removes the URL
|
|
30
|
+
* must do rather than leaving the last one running.
|
|
31
|
+
*/
|
|
32
|
+
export declare function configureBackend(cfg: BackendConfig | null): Backend | null;
|
|
33
|
+
/** The configured backend, or null when this host runs local-only. */
|
|
34
|
+
export declare function backend(): Backend | null;
|
|
35
|
+
/** True when a backend is attached AND says it can do this. Both halves matter:
|
|
36
|
+
* attached-but-incapable is the prompd gateway, and using it for sessions would
|
|
37
|
+
* be a 404 on every push. */
|
|
38
|
+
export declare function backendSupports(capability: string): boolean;
|
|
39
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../api/backend.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,gBAAgB,EAAqB,MAAM,UAAU,CAAC;AAE/D;;eAEe;AACf,eAAO,MAAM,YAAY,aAAa,CAAC;AACvC,eAAO,MAAM,SAAS,UAAU,CAAC;AACjC,eAAO,MAAM,aAAa,cAAc,CAAC;AAEzC,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACrC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC;4CACwC;IACxC,YAAY,IAAI,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IACtC,+DAA+D;IAC/D,KAAK,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;CACrC;AAuDD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAG1E;AAED,sEAAsE;AACtE,wBAAgB,OAAO,IAAI,OAAO,GAAG,IAAI,CAExC;AAED;;6BAE6B;AAC7B,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE3D"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* THE BACKEND: one place that knows where it is and what it can do.
|
|
2
|
+
*
|
|
3
|
+
* Replaces a per-host flag with a per-host CONFIGURATION. A sidecar had grown
|
|
4
|
+
* `--api`, the browser had an env var, and the hosted runner would have grown a
|
|
5
|
+
* third — three mechanisms for one decision, which is how they drift. A host
|
|
6
|
+
* configures a backend; everything else asks this.
|
|
7
|
+
*
|
|
8
|
+
* IT ASKS THE BACKEND WHAT IT IS, rather than being told by a flag. Pointing at
|
|
9
|
+
* `api.prompd.app` and pointing at `api.corenel.ai` are not the same thing —
|
|
10
|
+
* prompd's gateway has no sessions, no blobs and no lease — and a caller should
|
|
11
|
+
* not have to be configured with that fact when the server can simply say so.
|
|
12
|
+
* `probe()` reads `/meta`; a backend that does not answer, or answers without a
|
|
13
|
+
* capability, does not get used for it.
|
|
14
|
+
*
|
|
15
|
+
* WHY A PROBE AND NOT A SECOND FLAG. A flag can be wrong. A machine pointed at
|
|
16
|
+
* the wrong backend with `--api` set would push into a 404 forever and look like
|
|
17
|
+
* a quiet session; asking removes the class of error entirely, and costs one
|
|
18
|
+
* request at startup.
|
|
19
|
+
*
|
|
20
|
+
* Absent is normal. No backend means local-only, which is the correct state for a
|
|
21
|
+
* laptop working alone and never an error to report.
|
|
22
|
+
*/
|
|
23
|
+
import { CorenelApiClient } from './client';
|
|
24
|
+
/** Capabilities a backend may advertise. Strings, not an enum: an older client
|
|
25
|
+
* must be able to read a newer backend's list without choking on a name it does
|
|
26
|
+
* not know. */
|
|
27
|
+
export const CAP_SESSIONS = 'sessions';
|
|
28
|
+
export const CAP_BLOBS = 'blobs';
|
|
29
|
+
export const CAP_SNAPSHOTS = 'snapshots';
|
|
30
|
+
class RemoteBackend {
|
|
31
|
+
constructor(cfg) {
|
|
32
|
+
Object.defineProperty(this, "baseUrl", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "client", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: void 0
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "caps", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: []
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "probed", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: null
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(this, "fetchImpl", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: void 0
|
|
61
|
+
});
|
|
62
|
+
this.baseUrl = cfg.baseUrl.replace(/\/+$/, '');
|
|
63
|
+
this.fetchImpl = cfg.fetch ?? ((...args) => fetch(...args));
|
|
64
|
+
const transport = {
|
|
65
|
+
fetch: this.fetchImpl,
|
|
66
|
+
baseUrl: this.baseUrl,
|
|
67
|
+
getToken: cfg.getToken,
|
|
68
|
+
};
|
|
69
|
+
this.client = new CorenelApiClient(transport);
|
|
70
|
+
}
|
|
71
|
+
capabilities() {
|
|
72
|
+
return this.caps;
|
|
73
|
+
}
|
|
74
|
+
supports(capability) {
|
|
75
|
+
return this.caps.includes(capability);
|
|
76
|
+
}
|
|
77
|
+
probe() {
|
|
78
|
+
if (this.probed)
|
|
79
|
+
return this.probed;
|
|
80
|
+
this.probed = (async () => {
|
|
81
|
+
try {
|
|
82
|
+
// Unauthenticated on purpose: "what are you" must be answerable before a
|
|
83
|
+
// credential is sorted out, or a signed-out machine could never discover
|
|
84
|
+
// that signing in would be worth it.
|
|
85
|
+
const res = await this.fetchImpl(`${this.baseUrl}/meta`);
|
|
86
|
+
if (!res.ok)
|
|
87
|
+
return [];
|
|
88
|
+
const body = await res.json();
|
|
89
|
+
this.caps = Array.isArray(body.capabilities)
|
|
90
|
+
? body.capabilities.filter((c) => typeof c === 'string')
|
|
91
|
+
: [];
|
|
92
|
+
return this.caps;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Unreachable, not JSON, or not a corenel backend at all. All of them
|
|
96
|
+
// mean the same thing: do not use it for anything.
|
|
97
|
+
this.caps = [];
|
|
98
|
+
return this.caps;
|
|
99
|
+
}
|
|
100
|
+
})();
|
|
101
|
+
return this.probed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
let current = null;
|
|
105
|
+
/**
|
|
106
|
+
* Point at a backend, or at none.
|
|
107
|
+
*
|
|
108
|
+
* Called by a HOST — the browser app, the sidecar, a hosted runner — and by
|
|
109
|
+
* nothing else. Passing null detaches, which a config change that removes the URL
|
|
110
|
+
* must do rather than leaving the last one running.
|
|
111
|
+
*/
|
|
112
|
+
export function configureBackend(cfg) {
|
|
113
|
+
current = cfg && cfg.baseUrl ? new RemoteBackend(cfg) : null;
|
|
114
|
+
return current;
|
|
115
|
+
}
|
|
116
|
+
/** The configured backend, or null when this host runs local-only. */
|
|
117
|
+
export function backend() {
|
|
118
|
+
return current;
|
|
119
|
+
}
|
|
120
|
+
/** True when a backend is attached AND says it can do this. Both halves matter:
|
|
121
|
+
* attached-but-incapable is the prompd gateway, and using it for sessions would
|
|
122
|
+
* be a 404 on every push. */
|
|
123
|
+
export function backendSupports(capability) {
|
|
124
|
+
return current?.supports(capability) ?? false;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../../api/backend.ts"],"names":[],"mappings":"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"}
|