@sema-agent/core 7.16.0 → 7.17.0
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/CHANGELOG.md +173 -0
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/cross-session-envelope.d.ts +0 -1
- package/dist/agents/cross-session-envelope.js +0 -1
- package/dist/agents/peer-notices.d.ts +0 -9
- package/dist/agents/peer-notices.js +0 -63
- package/dist/agents/subagent.js +7 -15
- package/dist/brain/request-params.d.ts +2 -2
- package/dist/config/defaults.d.ts +0 -7
- package/dist/config/defaults.js +0 -2
- package/dist/core/artifact-host.d.ts +218 -0
- package/dist/core/artifact-host.js +92 -0
- package/dist/core/ask-origin.d.ts +29 -3
- package/dist/core/ask-origin.js +17 -9
- package/dist/core/ask-question.d.ts +25 -0
- package/dist/core/ask-question.js +4 -0
- package/dist/core/ask-unresolvable-notice.d.ts +30 -21
- package/dist/core/ask-unresolvable-notice.js +16 -8
- package/dist/core/auto-compaction.d.ts +10 -10
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/background-agent-store.d.ts +5 -5
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-execution-record.d.ts +0 -3
- package/dist/core/checkpoint-execution-record.js +0 -4
- package/dist/core/checkpoint-store.d.ts +71 -99
- package/dist/core/checkpoint-store.js +9 -16
- package/dist/core/context-edit.d.ts +36 -104
- package/dist/core/context-edit.js +37 -60
- package/dist/core/effective-path-target.d.ts +68 -8
- package/dist/core/effective-path-target.js +42 -30
- package/dist/core/engine-notice.d.ts +46 -32
- package/dist/core/env-budget.d.ts +31 -0
- package/dist/core/env-budget.js +39 -0
- package/dist/core/exec-output-tail.d.ts +5 -5
- package/dist/core/file-history-store.js +3 -6
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.js +3 -3
- package/dist/core/gate-lanes.js +9 -15
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +18 -17
- package/dist/core/lsp-session.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/lsp.js +5 -4
- package/dist/core/mcp.d.ts +5 -5
- package/dist/core/mcp.js +4 -48
- package/dist/core/memory-recall.d.ts +0 -8
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/oracle-isolation.js +2 -2
- package/dist/core/parent-spec-seat.d.ts +66 -0
- package/dist/core/parent-spec-seat.js +47 -0
- package/dist/core/permission-rule-model.d.ts +11 -6
- package/dist/core/permission-rule-model.js +53 -37
- package/dist/core/permission-rules.js +8 -3
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +19 -0
- package/dist/core/retired-keys.js +15 -0
- package/dist/core/runner/active-skill-scope.js +3 -3
- package/dist/core/runner/assemble-result.d.ts +15 -13
- package/dist/core/runner/assemble-result.js +5 -4
- package/dist/core/runner/compaction-call-options.d.ts +25 -89
- package/dist/core/runner/contracts.d.ts +10 -9
- package/dist/core/runner/gate-exit.js +15 -1
- package/dist/core/runner/halt-attribution.d.ts +35 -0
- package/dist/core/runner/halt-attribution.js +16 -0
- package/dist/core/runner/memory-consolidation.d.ts +0 -2
- package/dist/core/runner/memory-consolidation.js +0 -1
- package/dist/core/runner/prepare-artifact.d.ts +53 -0
- package/dist/core/runner/prepare-artifact.js +77 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
- package/dist/core/runner/prepare-config-doors.d.ts +3 -3
- package/dist/core/runner/prepare-config-doors.js +5 -2
- package/dist/core/runner/prepare-context-lane.d.ts +2 -2
- package/dist/core/runner/prepare-context-lane.js +2 -13
- package/dist/core/runner/prepare-file-history.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.js +3 -52
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
- package/dist/core/runner/prepare-inherited-gate.js +7 -8
- package/dist/core/runner/prepare-memory.d.ts +4 -6
- package/dist/core/runner/prepare-memory.js +4 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
- package/dist/core/runner/prepare-policy-chain.js +15 -30
- package/dist/core/runner/prepare-prompt-assembly.js +1 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
- package/dist/core/runner/prepare-suspend-saga.js +0 -2
- package/dist/core/runner/prepare-task.js +10 -9
- package/dist/core/runner/prepare-wiring-manifest.js +9 -7
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-internals-and-config.js +4 -0
- package/dist/core/runner/retired-runner-deps.d.ts +3 -0
- package/dist/core/runner/retired-runner-deps.js +7 -0
- package/dist/core/runner/run-harness-handlers.js +2 -3
- package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
- package/dist/core/runner/run-settle-and-teardown.js +3 -1
- package/dist/core/runner/session-file-state-replay.js +3 -3
- package/dist/core/runner/session-rule-policy.js +3 -4
- package/dist/core/runner/stream-halt-verbs.js +1 -1
- package/dist/core/runner/stream-steer-verb.js +1 -4
- package/dist/core/runner/workspace-path.js +3 -0
- package/dist/core/runner-deps.d.ts +30 -49
- package/dist/core/safe-notify.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session.d.ts +2 -2
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/strategy-store.d.ts +8 -11
- package/dist/core/task-event.d.ts +20 -17
- package/dist/core/task-limits.d.ts +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +5 -5
- package/dist/core/task-registry-monitor.d.ts +2 -2
- package/dist/core/task-registry-shared.d.ts +27 -27
- package/dist/core/task-registry-shared.js +3 -13
- package/dist/core/task-registry.d.ts +8 -8
- package/dist/core/task-result.d.ts +37 -37
- package/dist/core/task-spec.d.ts +5 -5
- package/dist/core/task-stream.d.ts +16 -11
- package/dist/core/tool-catalog-entries.d.ts +1 -0
- package/dist/core/tool-catalog-entries.js +5 -0
- package/dist/core/tool-conformance.d.ts +0 -2
- package/dist/core/tool-conformance.js +1 -3
- package/dist/core/tool-errors.d.ts +5 -0
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +51 -22
- package/dist/core/tool-policy.js +28 -5
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +11 -11
- package/dist/core/tool-spec.d.ts +34 -38
- package/dist/core/tools.d.ts +2 -2
- package/dist/core/trace.d.ts +12 -15
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +4 -3
- package/dist/core/workflow-journal-store.d.ts +11 -11
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
- package/dist/engine/execution-env/node-execution-env.js +4 -2
- package/dist/engine/harness/agent-harness.d.ts +47 -4
- package/dist/engine/harness/agent-harness.js +52 -9
- package/dist/engine/loop/agent-loop.js +15 -8
- package/dist/engine/loop/types.d.ts +5 -0
- package/dist/index.d.ts +19 -14
- package/dist/index.js +15 -10
- package/dist/internal/harness.d.ts +1 -0
- package/dist/orchestration/run-spec.d.ts +3 -0
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +12 -6
- package/dist/orchestration/run-workflow-tool.js +23 -5
- package/dist/orchestration/workflow-primitives.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +1 -4
- package/dist/orchestration/workflow-types.d.ts +34 -4
- package/dist/orchestration/workflow-types.js +2 -0
- package/dist/orchestration/workflow.d.ts +39 -7
- package/dist/orchestration/workflow.js +234 -113
- package/dist/prompt-assembly/assemble.d.ts +2 -2
- package/dist/prompt-assembly/assemble.js +9 -9
- package/dist/prompt-assembly/composer.d.ts +6 -7
- package/dist/prompt-assembly/composer.js +10 -10
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.d.ts +0 -21
- package/dist/prompt-assembly/packs/sema-default.js +30 -30
- package/dist/prompt-assembly/types.d.ts +14 -14
- package/dist/prompts/coordinator.d.ts +0 -13
- package/dist/prompts/coordinator.js +0 -3
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/permission-rule-store.d.ts +4 -35
- package/dist/stores/file/permission-rule-store.js +4 -148
- package/dist/tools/artifact/artifact-text.d.ts +62 -0
- package/dist/tools/artifact/artifact-text.js +68 -0
- package/dist/tools/artifact/artifact-tool.d.ts +52 -0
- package/dist/tools/artifact/artifact-tool.js +351 -0
- package/dist/tools/artifact/local-stub.d.ts +26 -0
- package/dist/tools/artifact/local-stub.js +296 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +74 -8
- package/dist/tools/fs/bash-readonly-classifier.js +171 -60
- package/dist/tools/fs/fs-bash.d.ts +14 -10
- package/dist/tools/fs/fs-bash.js +19 -27
- package/dist/tools/fs/fs-search-tools.js +5 -2
- package/dist/tools/fs/fs-shared.d.ts +2 -0
- package/dist/tools/fs/fs-shared.js +3 -11
- package/dist/tools/fs/index.d.ts +4 -0
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/safety.d.ts +117 -1
- package/dist/tools/fs/safety.js +131 -31
- package/dist/tools/fs/search.js +8 -6
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +115 -39
package/dist/config/defaults.js
CHANGED
|
@@ -2,8 +2,6 @@ export const RETAIN_DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
|
2
2
|
export const RETAIN_DEFAULT_MAX = 16;
|
|
3
3
|
export const DELEGATION_MAX_CONCURRENT_DEFAULT = 20;
|
|
4
4
|
export const DELEGATION_MAX_PER_SESSION_DEFAULT = 200;
|
|
5
|
-
export const ORPHAN_ADOPT_WINDOW_MS_DEFAULT = 48 * 60 * 60 * 1000;
|
|
6
|
-
export const ORPHAN_ADOPT_MAX_DEFAULT = 20;
|
|
7
5
|
export const SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT = 30;
|
|
8
6
|
export const SESSION_DEFAULT_TTL_DAYS = 7;
|
|
9
7
|
export const RUNNING_AGENT_OBSERVE_EVERY_BEATS = 4;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
2
|
+
/** The model-facing action words. `publish` is the default action and the one every host must declare. */
|
|
3
|
+
export declare const ARTIFACT_ACTIONS: readonly ["publish", "list", "read", "list_files", "read_file"];
|
|
4
|
+
export type ArtifactAction = (typeof ARTIFACT_ACTIONS)[number];
|
|
5
|
+
/** Which face the host serves. `"local-stub"` = publish-only local storage with `eval-stub://` links;
|
|
6
|
+
* `"hosted"` = a hosting plane that mints its own URLs. Declared by the host, never detected. */
|
|
7
|
+
export declare const ARTIFACT_PLANES: readonly ["hosted", "local-stub"];
|
|
8
|
+
export type ArtifactPlane = (typeof ARTIFACT_PLANES)[number];
|
|
9
|
+
/** The `publishContext` word a publish carries (default `"plain"`). */
|
|
10
|
+
export declare const ARTIFACT_PUBLISH_CONTEXTS: readonly ["pr_review", "workshop", "whiteboard", "prototype", "plain"];
|
|
11
|
+
export type ArtifactPublishContext = (typeof ARTIFACT_PUBLISH_CONTEXTS)[number];
|
|
12
|
+
/** The `list` scope words. */
|
|
13
|
+
export declare const ARTIFACT_LIST_SCOPES: readonly ["mine", "shared", "all"];
|
|
14
|
+
export type ArtifactListScope = (typeof ARTIFACT_LIST_SCOPES)[number];
|
|
15
|
+
/**
|
|
16
|
+
* The content-origin class of the mounted tool, derived from the declared plane: a hosted plane hands
|
|
17
|
+
* back host-controlled bytes (URLs, titles, page content) and is classified external; the local stub
|
|
18
|
+
* reads nothing back from outside the process. Plane-keyed, never implementation-keyed.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ARTIFACT_PLANE_CONTENT_ORIGIN: {
|
|
21
|
+
readonly hosted: "external";
|
|
22
|
+
readonly "local-stub": "local";
|
|
23
|
+
};
|
|
24
|
+
export type ContentOriginTableCoversEveryPlane = AssertAllKeysHandled<Exclude<ArtifactPlane, keyof typeof ARTIFACT_PLANE_CONTENT_ORIGIN>>;
|
|
25
|
+
/** The host method each declared action is served by; the mount-time validator probes the method's presence. */
|
|
26
|
+
export declare const ARTIFACT_ACTION_HOST_METHOD: {
|
|
27
|
+
readonly publish: "publish";
|
|
28
|
+
readonly list: "list";
|
|
29
|
+
readonly read: "read";
|
|
30
|
+
readonly list_files: "listFiles";
|
|
31
|
+
readonly read_file: "readFile";
|
|
32
|
+
};
|
|
33
|
+
export type HostMethodTableCoversEveryAction = AssertAllKeysHandled<Exclude<ArtifactAction, keyof typeof ARTIFACT_ACTION_HOST_METHOD>>;
|
|
34
|
+
/** The typed failure words a host outcome may carry (`ok: false`). */
|
|
35
|
+
export declare const ARTIFACT_HOST_FAILURE_CODES: readonly ["conflict", "url_foreign", "forbidden", "payload_too_large", "not_found", "force_refused", "refused"];
|
|
36
|
+
export type ArtifactHostFailureCode = (typeof ARTIFACT_HOST_FAILURE_CODES)[number];
|
|
37
|
+
/** The tool-result `details.code` (and `tool_end.errorCode`) each host failure word is projected to. */
|
|
38
|
+
export declare const ARTIFACT_FAILURE_RESULT_CODE: {
|
|
39
|
+
readonly conflict: "artifact.publish_conflict";
|
|
40
|
+
readonly url_foreign: "artifact.url_foreign";
|
|
41
|
+
readonly forbidden: "artifact.forbidden";
|
|
42
|
+
readonly payload_too_large: "artifact.payload_too_large";
|
|
43
|
+
readonly not_found: "artifact.not_found";
|
|
44
|
+
readonly force_refused: "artifact.force_refused";
|
|
45
|
+
readonly refused: "artifact.refused";
|
|
46
|
+
};
|
|
47
|
+
export type ResultCodeTableCoversEveryHostFailure = AssertAllKeysHandled<Exclude<ArtifactHostFailureCode, keyof typeof ARTIFACT_FAILURE_RESULT_CODE>>;
|
|
48
|
+
/** The one result code that is not a host word: the host threw, timed out, or answered outside its contract. */
|
|
49
|
+
export declare const ARTIFACT_HOST_ERROR_CODE = "artifact.host_error";
|
|
50
|
+
export type ArtifactResultCode = (typeof ARTIFACT_FAILURE_RESULT_CODE)[ArtifactHostFailureCode] | typeof ARTIFACT_HOST_ERROR_CODE;
|
|
51
|
+
/** The `details.type` card the Artifact tool mints on every result. */
|
|
52
|
+
export declare const ARTIFACT_CARD = "artifact";
|
|
53
|
+
/**
|
|
54
|
+
* What a host declares at mount time. Invariants (a violation refuses the mount and announces
|
|
55
|
+
* `config.artifact_host_invalid`): `actions` non-empty, duplicate-free, within {@link ARTIFACT_ACTIONS},
|
|
56
|
+
* containing `"publish"`; `plane: "local-stub"` ⇒ `actions` is exactly `["publish"]`;
|
|
57
|
+
* `defaultVisibility` is `"private"`; both byte limits are positive finite integers; `multiFile` is a
|
|
58
|
+
* boolean; every declared action's host method exists; `plane: "local-stub"` ⇒ `storageDir` is a non-empty
|
|
59
|
+
* absolute path.
|
|
60
|
+
*/
|
|
61
|
+
export interface ArtifactHostCapabilities {
|
|
62
|
+
plane: ArtifactPlane;
|
|
63
|
+
actions: ReadonlyArray<ArtifactAction>;
|
|
64
|
+
/** The on-disk store of a `"local-stub"` plane (required there; ignored on `"hosted"`). Contract: the store
|
|
65
|
+
* must lie OUTSIDE every root the run may write or read (the task root, `additionalDirectories`,
|
|
66
|
+
* `additionalReadDirectories`) — a store the model can reach through the fs tools is not a trust boundary;
|
|
67
|
+
* the engine refuses the mount (`config.artifact_host_invalid`) when it is inside one. */
|
|
68
|
+
storageDir?: string;
|
|
69
|
+
/** The only value this version mounts: a page is private until its owner shares it. */
|
|
70
|
+
defaultVisibility: "private";
|
|
71
|
+
maxFileBytes: number;
|
|
72
|
+
maxTotalBytes: number;
|
|
73
|
+
/** `false` ⇒ the publish arm takes one `file_path` only (`files[]` leaves the schema). */
|
|
74
|
+
multiFile: boolean;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Engine-minted call attribution and idempotency keys — closure-supplied, never a tool parameter.
|
|
78
|
+
* `taskId` / `sessionId` / `agentName` are audit attribution, never an authorization subject: a host
|
|
79
|
+
* instance is bound to one principal at construction. `runId` and `attemptId` are engine-minted trusted
|
|
80
|
+
* keys: `runId` is the same value `onMount` received; `attemptId` is one opaque value per tool call,
|
|
81
|
+
* stable across any host retry of that call, so a host MUST replay the first outcome for a repeated
|
|
82
|
+
* `attemptId` (a fresh publish never mints a second page, an update never advances twice).
|
|
83
|
+
*/
|
|
84
|
+
export interface ArtifactCallContext {
|
|
85
|
+
taskId: string;
|
|
86
|
+
sessionId?: string;
|
|
87
|
+
agentName?: string;
|
|
88
|
+
runId: string;
|
|
89
|
+
attemptId: string;
|
|
90
|
+
}
|
|
91
|
+
export interface ArtifactFileContent {
|
|
92
|
+
path: string;
|
|
93
|
+
content: Uint8Array;
|
|
94
|
+
mediaType?: string;
|
|
95
|
+
}
|
|
96
|
+
export interface ArtifactPublishRequest {
|
|
97
|
+
slug?: string;
|
|
98
|
+
/** Update locator. Present without `baseVersion` ⇒ the host applies last-write-wins. */
|
|
99
|
+
url?: string;
|
|
100
|
+
files: ReadonlyArray<ArtifactFileContent>;
|
|
101
|
+
/** Reserved on the seam; the model face does not expose it in this version. A host MAY refuse it. */
|
|
102
|
+
removeFiles?: ReadonlyArray<string>;
|
|
103
|
+
title?: string;
|
|
104
|
+
description?: string;
|
|
105
|
+
favicon?: string;
|
|
106
|
+
label?: string;
|
|
107
|
+
baseVersion?: string;
|
|
108
|
+
force?: boolean;
|
|
109
|
+
publishContext: ArtifactPublishContext;
|
|
110
|
+
}
|
|
111
|
+
/** The reference shape every publish / list / read result carries. */
|
|
112
|
+
export interface ArtifactRef {
|
|
113
|
+
url: string;
|
|
114
|
+
slug: string;
|
|
115
|
+
version: string;
|
|
116
|
+
title?: string;
|
|
117
|
+
}
|
|
118
|
+
export interface ArtifactListRow extends ArtifactRef {
|
|
119
|
+
owner: "mine" | "shared";
|
|
120
|
+
updatedAt: string;
|
|
121
|
+
}
|
|
122
|
+
export interface ArtifactConflictSnapshot {
|
|
123
|
+
files: ReadonlyArray<ArtifactFileContent>;
|
|
124
|
+
title?: string;
|
|
125
|
+
description?: string;
|
|
126
|
+
favicon?: string;
|
|
127
|
+
label?: string;
|
|
128
|
+
}
|
|
129
|
+
export type ArtifactPublishOutcome = {
|
|
130
|
+
ok: true;
|
|
131
|
+
ref: ArtifactRef;
|
|
132
|
+
} | {
|
|
133
|
+
ok: false;
|
|
134
|
+
code: "conflict";
|
|
135
|
+
currentVersion: string;
|
|
136
|
+
current: ArtifactConflictSnapshot | "reread";
|
|
137
|
+
} | {
|
|
138
|
+
ok: false;
|
|
139
|
+
code: Exclude<ArtifactHostFailureCode, "conflict">;
|
|
140
|
+
message: string;
|
|
141
|
+
};
|
|
142
|
+
export type ArtifactListOutcome = {
|
|
143
|
+
ok: true;
|
|
144
|
+
rows: ReadonlyArray<ArtifactListRow>;
|
|
145
|
+
truncated: boolean;
|
|
146
|
+
} | {
|
|
147
|
+
ok: false;
|
|
148
|
+
code: "refused";
|
|
149
|
+
message: string;
|
|
150
|
+
};
|
|
151
|
+
export type ArtifactReadOutcome = {
|
|
152
|
+
ok: true;
|
|
153
|
+
ref: ArtifactRef;
|
|
154
|
+
content: string;
|
|
155
|
+
isolatedSummary: boolean;
|
|
156
|
+
} | {
|
|
157
|
+
ok: false;
|
|
158
|
+
code: "not_found" | "forbidden";
|
|
159
|
+
message: string;
|
|
160
|
+
};
|
|
161
|
+
export type ArtifactFilesOutcome = {
|
|
162
|
+
ok: true;
|
|
163
|
+
entries: ReadonlyArray<{
|
|
164
|
+
path: string;
|
|
165
|
+
bytes: number;
|
|
166
|
+
mediaType?: string;
|
|
167
|
+
}>;
|
|
168
|
+
} | {
|
|
169
|
+
ok: false;
|
|
170
|
+
code: "not_found" | "forbidden";
|
|
171
|
+
message: string;
|
|
172
|
+
};
|
|
173
|
+
export type ArtifactFileOutcome = {
|
|
174
|
+
ok: true;
|
|
175
|
+
file: ArtifactFileContent;
|
|
176
|
+
} | {
|
|
177
|
+
ok: false;
|
|
178
|
+
code: "not_found" | "forbidden";
|
|
179
|
+
message: string;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* The hosting seat. Atomicity contract: no half version is ever observable — a reader sees the old
|
|
183
|
+
* complete version or the new complete version, never a mix of files / metadata / version number.
|
|
184
|
+
* The commit itself may be uncertain from the engine's side (the answer was lost after the host
|
|
185
|
+
* committed); the `attemptId` replay rule above is what makes a retry of that call converge.
|
|
186
|
+
*/
|
|
187
|
+
export interface ArtifactHost {
|
|
188
|
+
capabilities(): ArtifactHostCapabilities;
|
|
189
|
+
/** Called at every materialization of a run (prepare and resume) with the engine-minted run id. */
|
|
190
|
+
onMount?(runId: string): void;
|
|
191
|
+
publish(req: ArtifactPublishRequest, ctx: ArtifactCallContext): Promise<ArtifactPublishOutcome>;
|
|
192
|
+
list?(req: {
|
|
193
|
+
scope: ArtifactListScope;
|
|
194
|
+
limit: number;
|
|
195
|
+
}, ctx: ArtifactCallContext): Promise<ArtifactListOutcome>;
|
|
196
|
+
read?(req: {
|
|
197
|
+
url: string;
|
|
198
|
+
}, ctx: ArtifactCallContext): Promise<ArtifactReadOutcome>;
|
|
199
|
+
listFiles?(req: {
|
|
200
|
+
url: string;
|
|
201
|
+
}, ctx: ArtifactCallContext): Promise<ArtifactFilesOutcome>;
|
|
202
|
+
readFile?(req: {
|
|
203
|
+
url: string;
|
|
204
|
+
path: string;
|
|
205
|
+
}, ctx: ArtifactCallContext): Promise<ArtifactFileOutcome>;
|
|
206
|
+
}
|
|
207
|
+
export type ArtifactHostCapabilitiesVerdict = {
|
|
208
|
+
ok: true;
|
|
209
|
+
capabilities: ArtifactHostCapabilities;
|
|
210
|
+
} | {
|
|
211
|
+
ok: false;
|
|
212
|
+
problem: string;
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Read and validate a host's declaration. Every invariant of {@link ArtifactHostCapabilities} is judged
|
|
216
|
+
* here and nowhere else; a `problem` names the first violated one in operator-facing words.
|
|
217
|
+
*/
|
|
218
|
+
export declare function readArtifactHostCapabilities(host: unknown): ArtifactHostCapabilitiesVerdict;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { isAbsolute } from "node:path";
|
|
2
|
+
export const ARTIFACT_ACTIONS = ["publish", "list", "read", "list_files", "read_file"];
|
|
3
|
+
export const ARTIFACT_PLANES = ["hosted", "local-stub"];
|
|
4
|
+
export const ARTIFACT_PUBLISH_CONTEXTS = ["pr_review", "workshop", "whiteboard", "prototype", "plain"];
|
|
5
|
+
export const ARTIFACT_LIST_SCOPES = ["mine", "shared", "all"];
|
|
6
|
+
export const ARTIFACT_PLANE_CONTENT_ORIGIN = {
|
|
7
|
+
hosted: "external",
|
|
8
|
+
"local-stub": "local",
|
|
9
|
+
};
|
|
10
|
+
export const ARTIFACT_ACTION_HOST_METHOD = {
|
|
11
|
+
publish: "publish",
|
|
12
|
+
list: "list",
|
|
13
|
+
read: "read",
|
|
14
|
+
list_files: "listFiles",
|
|
15
|
+
read_file: "readFile",
|
|
16
|
+
};
|
|
17
|
+
export const ARTIFACT_HOST_FAILURE_CODES = ["conflict", "url_foreign", "forbidden", "payload_too_large", "not_found", "force_refused", "refused"];
|
|
18
|
+
export const ARTIFACT_FAILURE_RESULT_CODE = {
|
|
19
|
+
conflict: "artifact.publish_conflict",
|
|
20
|
+
url_foreign: "artifact.url_foreign",
|
|
21
|
+
forbidden: "artifact.forbidden",
|
|
22
|
+
payload_too_large: "artifact.payload_too_large",
|
|
23
|
+
not_found: "artifact.not_found",
|
|
24
|
+
force_refused: "artifact.force_refused",
|
|
25
|
+
refused: "artifact.refused",
|
|
26
|
+
};
|
|
27
|
+
export const ARTIFACT_HOST_ERROR_CODE = "artifact.host_error";
|
|
28
|
+
export const ARTIFACT_CARD = "artifact";
|
|
29
|
+
const isPositiveInt = (v) => typeof v === "number" && Number.isSafeInteger(v) && v > 0;
|
|
30
|
+
const wordOf = (set, v) => typeof v === "string" && set.includes(v);
|
|
31
|
+
export function readArtifactHostCapabilities(host) {
|
|
32
|
+
if (host === null || typeof host !== "object")
|
|
33
|
+
return { ok: false, problem: "artifactHost is not an object" };
|
|
34
|
+
const h = host;
|
|
35
|
+
if (typeof h.capabilities !== "function")
|
|
36
|
+
return { ok: false, problem: "artifactHost.capabilities is not a function" };
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = h.capabilities.call(host);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return { ok: false, problem: `artifactHost.capabilities() threw: ${err instanceof Error ? err.message : String(err)}` };
|
|
43
|
+
}
|
|
44
|
+
if (raw === null || typeof raw !== "object")
|
|
45
|
+
return { ok: false, problem: "artifactHost.capabilities() did not return an object" };
|
|
46
|
+
const c = raw;
|
|
47
|
+
const plane = c["plane"];
|
|
48
|
+
if (!wordOf(ARTIFACT_PLANES, plane))
|
|
49
|
+
return { ok: false, problem: `capabilities.plane must be one of ${ARTIFACT_PLANES.join(" | ")}` };
|
|
50
|
+
const actions = c["actions"];
|
|
51
|
+
if (!Array.isArray(actions) || actions.length === 0)
|
|
52
|
+
return { ok: false, problem: "capabilities.actions must be a non-empty array" };
|
|
53
|
+
for (const a of actions)
|
|
54
|
+
if (!wordOf(ARTIFACT_ACTIONS, a))
|
|
55
|
+
return { ok: false, problem: `capabilities.actions carries a word outside ${ARTIFACT_ACTIONS.join(" | ")}` };
|
|
56
|
+
if (new Set(actions).size !== actions.length)
|
|
57
|
+
return { ok: false, problem: "capabilities.actions carries a duplicate" };
|
|
58
|
+
if (!actions.includes("publish"))
|
|
59
|
+
return { ok: false, problem: 'capabilities.actions must include "publish"' };
|
|
60
|
+
if (plane === "local-stub" && (actions.length !== 1 || actions[0] !== "publish"))
|
|
61
|
+
return { ok: false, problem: 'a "local-stub" plane must declare exactly ["publish"]' };
|
|
62
|
+
if (c["defaultVisibility"] !== "private")
|
|
63
|
+
return { ok: false, problem: 'capabilities.defaultVisibility must be "private"' };
|
|
64
|
+
if (!isPositiveInt(c["maxFileBytes"]))
|
|
65
|
+
return { ok: false, problem: "capabilities.maxFileBytes must be a positive integer" };
|
|
66
|
+
if (!isPositiveInt(c["maxTotalBytes"]))
|
|
67
|
+
return { ok: false, problem: "capabilities.maxTotalBytes must be a positive integer" };
|
|
68
|
+
if (typeof c["multiFile"] !== "boolean")
|
|
69
|
+
return { ok: false, problem: "capabilities.multiFile must be a boolean" };
|
|
70
|
+
const storageDir = c["storageDir"];
|
|
71
|
+
if (plane === "local-stub" && (typeof storageDir !== "string" || storageDir.length === 0 || !isAbsolute(storageDir)))
|
|
72
|
+
return { ok: false, problem: 'a "local-stub" plane must declare its absolute storageDir' };
|
|
73
|
+
if (storageDir !== undefined && typeof storageDir !== "string")
|
|
74
|
+
return { ok: false, problem: "capabilities.storageDir must be a string when present" };
|
|
75
|
+
for (const a of actions) {
|
|
76
|
+
const method = ARTIFACT_ACTION_HOST_METHOD[a];
|
|
77
|
+
if (typeof host[method] !== "function")
|
|
78
|
+
return { ok: false, problem: `capabilities.actions declares "${a}" but artifactHost.${method} is not a function` };
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
ok: true,
|
|
82
|
+
capabilities: Object.freeze({
|
|
83
|
+
plane,
|
|
84
|
+
actions: Object.freeze([...actions]),
|
|
85
|
+
defaultVisibility: "private",
|
|
86
|
+
maxFileBytes: c["maxFileBytes"],
|
|
87
|
+
maxTotalBytes: c["maxTotalBytes"],
|
|
88
|
+
multiFile: c["multiFile"],
|
|
89
|
+
...(typeof storageDir === "string" ? { storageDir } : {}),
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -246,11 +246,14 @@ export declare function classifierMayAnswer(origin: AskOrigin): boolean;
|
|
|
246
246
|
* the same way every other per-call fact does: the gate stamps it on the surviving ask, the
|
|
247
247
|
* synchronous sites read it off the decision they spread, and the park leg threads its own parameter.
|
|
248
248
|
*/
|
|
249
|
-
export declare function persistedRuleMandateOf(marks:
|
|
249
|
+
export declare function persistedRuleMandateOf(marks: MandateMarks): "operator_always" | "tool_marks" | "probe_mandate" | "probe_unanswered" | undefined;
|
|
250
|
+
/** The mark inputs of the two mandate derivations — properties of the tool SEAT (`egress`, `shellGated`,
|
|
251
|
+
* `irreversibility`) beside the two per-call facts the gate stamps. */
|
|
252
|
+
export interface MandateMarks {
|
|
250
253
|
egress?: boolean;
|
|
251
254
|
shellGated?: boolean;
|
|
252
255
|
irreversibility?: "never" | "maybe" | "always";
|
|
253
|
-
/**
|
|
256
|
+
/** The surviving ask's engine-stamped `probeMandated` — see
|
|
254
257
|
* {@link import("./types.js").ReversibilityVerdict.mandated}. */
|
|
255
258
|
probeMandated?: boolean;
|
|
256
259
|
/** The pass's own record that the probe was consulted and did not answer (`GatePass.probeUnanswered`):
|
|
@@ -258,7 +261,30 @@ export declare function persistedRuleMandateOf(marks: {
|
|
|
258
261
|
* not see the pass and never passes it — a card may offer a rule the lane then discloses as shadowed
|
|
259
262
|
* (loud, never a silent override), the same drift window the `matchedAskRule` conjunct already has. */
|
|
260
263
|
probeUnanswered?: boolean;
|
|
261
|
-
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* The SAFETY AXIS of one ask — what makes its durable park NON-BUDGETABLE (`irreversible_ask`), judged
|
|
267
|
+
* from the same mark inputs as the rule-lane mandate plus the gate's own record of which tighten minted the
|
|
268
|
+
* ask. `egress` is the tool's static external-write mark, as it always was. `irreversible` is a mandate of
|
|
269
|
+
* THIS call: the tool's own tier (`tool_marks` — the deployment declared the tool may be irreversible,
|
|
270
|
+
* and a policy or hook asking first does not un-declare it), the operator's per-call doctrine
|
|
271
|
+
* (`operator_always`), the probe's structural verdict (`probe_mandate` / `probe_unanswered`), OR — under a
|
|
272
|
+
* shell-seat-installed `"maybe"` tier — the shell gate's own tighten having minted this ask (the doctrine's
|
|
273
|
+
* probe judged THIS command not reversible). A policy ask on a shell-gated tool that the doctrine's probe
|
|
274
|
+
* did not tighten and did not mandate is a plain ask: the tier was the probe's instrument, not a risk
|
|
275
|
+
* label, and the park stays the budgetable kind. Before this derivation the static tier alone set the bit,
|
|
276
|
+
* so every policy ask on Bash under `shellGate:"classify"` parked non-budgetable.
|
|
277
|
+
*
|
|
278
|
+
* Deliberately NOT the rule-lane predicate verbatim: a shell tighten's ask is rule-clearable (a person's
|
|
279
|
+
* past yes to this command shape stands) yet non-budgetable (a budget's mechanical yes is not a person's).
|
|
280
|
+
* The two derivations share every other row through {@link irreversibilityMandateOf}.
|
|
281
|
+
*/
|
|
282
|
+
export declare function safetyAxisOf(marks: MandateMarks & {
|
|
283
|
+
readonly tightened: AskOriginFacts["tightened"];
|
|
284
|
+
}): {
|
|
285
|
+
egress: boolean;
|
|
286
|
+
irreversible: boolean;
|
|
287
|
+
} | undefined;
|
|
262
288
|
/** The PROBE half of the mandated family (the deployment's read boundary, judged per call): neither a standing rule
|
|
263
289
|
* nor the classifier clears these two words; `tool_marks`/`operator_always` bind the rule lane only (`safety_tighten`). */
|
|
264
290
|
export declare function probeBoundaryMandate(word: ReturnType<typeof persistedRuleMandateOf>): boolean;
|
package/dist/core/ask-origin.js
CHANGED
|
@@ -68,19 +68,27 @@ export function classifierMayAnswer(origin) {
|
|
|
68
68
|
return CLASSIFIER_MAY_ANSWER[origin];
|
|
69
69
|
}
|
|
70
70
|
export function persistedRuleMandateOf(marks) {
|
|
71
|
+
const word = irreversibilityMandateOf(marks);
|
|
72
|
+
return probeBoundaryMandate(word) ? word : marks.egress === true ? "tool_marks" : word;
|
|
73
|
+
}
|
|
74
|
+
function irreversibilityMandateOf(marks) {
|
|
71
75
|
return marks.probeMandated === true
|
|
72
76
|
? "probe_mandate"
|
|
73
77
|
: marks.probeUnanswered === true
|
|
74
78
|
? "probe_unanswered"
|
|
75
|
-
: marks.
|
|
76
|
-
? "
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
:
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
: marks.shellGated === true
|
|
80
|
+
? marks.irreversibility === "always"
|
|
81
|
+
? "operator_always"
|
|
82
|
+
: undefined
|
|
83
|
+
: marks.irreversibility === "always" || marks.irreversibility === "maybe"
|
|
84
|
+
? "tool_marks"
|
|
85
|
+
: undefined;
|
|
86
|
+
}
|
|
87
|
+
export function safetyAxisOf(marks) {
|
|
88
|
+
const egress = marks.egress === true;
|
|
89
|
+
const irreversible = irreversibilityMandateOf(marks) !== undefined ||
|
|
90
|
+
(marks.shellGated === true && marks.irreversibility === "maybe" && marks.tightened === "shell_gate");
|
|
91
|
+
return egress || irreversible ? { egress, irreversible } : undefined;
|
|
84
92
|
}
|
|
85
93
|
export function probeBoundaryMandate(word) {
|
|
86
94
|
return word === "probe_mandate" || word === "probe_unanswered";
|
|
@@ -348,3 +348,28 @@ source?: {
|
|
|
348
348
|
principal?: string;
|
|
349
349
|
sourceTaskId?: string;
|
|
350
350
|
}, opts?: AskUserQuestionToolOptions): AgentTool;
|
|
351
|
+
/**
|
|
352
|
+
* The ONE producer of the "engine stripped the spawn turn's question face" fact. The strip (removing `onQuestion`
|
|
353
|
+
* from a child spec — or from the parent SPEC seat a child is built from — when the child OUTLIVES the request that
|
|
354
|
+
* spawned it) and the disclosure flag (`RunInternals.questionFaceStripped` → the wiring manifest's
|
|
355
|
+
* `question.wired: "stripped_bg_lane"` + the posture door's exemption) are PAIR-PRODUCED from this single expression —
|
|
356
|
+
* never inferred from spec shape, never minted anywhere else. Three families ride it: the Agent tool's session-scoped
|
|
357
|
+
* background lanes (spec-override form), its retain snapshots (destructure-drop form; the flag then rides
|
|
358
|
+
* `internalsSnapshot`, so a revived leg keeps the exemption), and the Workflow tool (submit-and-return: every agent it
|
|
359
|
+
* spawns outlives the launching request, so the host's seat is stripped before it reaches the launch site). WHY:
|
|
360
|
+
* `spec.onQuestion` is by contract a per-REQUEST lane torn down with its turn; handing it to a leg that runs hours later
|
|
361
|
+
* would strand the child asking into a closed surface (`AskUserQuestion` awaits the answer). A key REMOVED and a key set
|
|
362
|
+
* `undefined` resolve identically through the engine's `spec.onQuestion ?? deps.onQuestion` reads — a process-level
|
|
363
|
+
* `RunnerDeps.onQuestion` still reaches the child; only the turn-bound value drops. Trust posture: RunInternals is a
|
|
364
|
+
* PUBLIC trusted-caller parameter, so the flag is an HONESTY device (the engine disclosing its own strip), not a
|
|
365
|
+
* security gate. The flag states "a face WAS removed here" — minted only when one actually existed (an inherently
|
|
366
|
+
* faceless lane reads `absent`, not `stripped_bg_lane`, and collects no exemption).
|
|
367
|
+
*/
|
|
368
|
+
export declare function stripSpawnTurnQuestionFace<S extends {
|
|
369
|
+
onQuestion?: OnQuestion;
|
|
370
|
+
}>(spec: S): {
|
|
371
|
+
spec: Omit<S, "onQuestion">;
|
|
372
|
+
internalsFlag: {
|
|
373
|
+
questionFaceStripped?: true;
|
|
374
|
+
};
|
|
375
|
+
};
|
|
@@ -378,3 +378,7 @@ export function createAskUserQuestionTool(onQuestion, source, opts) {
|
|
|
378
378
|
},
|
|
379
379
|
});
|
|
380
380
|
}
|
|
381
|
+
export function stripSpawnTurnQuestionFace(spec) {
|
|
382
|
+
const { onQuestion: spawnTurnQuestionFace, ...rest } = spec;
|
|
383
|
+
return { spec: rest, internalsFlag: spawnTurnQuestionFace !== undefined ? { questionFaceStripped: true } : {} };
|
|
384
|
+
}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `delegation.ask_unresolvable` — the one mint of the notice the gate raises when an `ask` reached its
|
|
3
|
-
* final deny with NOBODY having judged it
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
3
|
+
* final deny with NOBODY having judged it. Three ways that happens, one event class ("a call was denied
|
|
4
|
+
* fail-closed because its approval could not be resolved"), one closed cause set:
|
|
5
|
+
* · `approver_unavailable` — the approver consulted for the call (the run's own seat, or an ancestor's
|
|
6
|
+
* frozen seat on a delegated child) answered the ROUTING question `unavailable` and no durable park
|
|
7
|
+
* took the call afterwards (`tool_end.gate.settlement.kind:"approver_unavailable"`);
|
|
8
|
+
* · `no_approver_wired` — no approver (or, for a question, no question face) is wired for this run, or
|
|
9
|
+
* the seat is the deny posture string: the headless auto-deny (`settlement.kind:"no_approver"`);
|
|
10
|
+
* · `mandate_unreconstructible` — an inherited durable-approval mandate binds the ask and THIS leg has no
|
|
11
|
+
* park facility to honor it, so the delegated child refuses in-fold rather than let a synchronous layer
|
|
12
|
+
* resolve what its ancestor bound to a park (an in-fold deny; no settlement rides the frame).
|
|
13
|
+
* The deny itself is older than this notice and stands unchanged (the model reads the sentence as its
|
|
14
|
+
* tool result); what was missing was any face a PERSON watching notices could see — an operator reading
|
|
15
|
+
* notices alone saw nothing, and the person whose work was refused learned it only from a transcript.
|
|
10
16
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* this factory: one call, one notice, once per tool call.
|
|
17
|
+
* ONE exit composes it: the gate's single deny exit (`exitGate`), which reads the settlement the deny
|
|
18
|
+
* carries or the cause an in-fold arm attested, after every park re-route has had its attempt. That exit is
|
|
19
|
+
* the only caller of this factory: one call, one notice, once per tool call.
|
|
15
20
|
*
|
|
16
21
|
* Audience `"user"`: the refused work is the user's own (their run, or a delegation their run made), and
|
|
17
22
|
* the recovery — a checkpoint store with a durable approval opt-in so such asks can wait for a person, or
|
|
@@ -22,17 +27,21 @@
|
|
|
22
27
|
* is pure.
|
|
23
28
|
*/
|
|
24
29
|
import type { EngineNotice } from "./engine-notice.js";
|
|
25
|
-
|
|
30
|
+
/** WHY the ask could not be resolved — the closed set (see the module note for each word). */
|
|
31
|
+
export declare const ASK_UNRESOLVABLE_CAUSES: readonly ["approver_unavailable", "no_approver_wired", "mandate_unreconstructible"];
|
|
32
|
+
export type AskUnresolvableCause = (typeof ASK_UNRESOLVABLE_CAUSES)[number];
|
|
33
|
+
/** Membership test for {@link AskUnresolvableCause} — the word rides a notice detail across process boundaries. */
|
|
34
|
+
export declare function isAskUnresolvableCause(v: unknown): v is AskUnresolvableCause;
|
|
26
35
|
/** What the gate knows at the deny exit — facts only, no words of the engine's vocabulary composed. */
|
|
27
36
|
export interface AskUnresolvableFacts {
|
|
28
37
|
/** The session whose call was refused — the notice's routing key, required (see module note). */
|
|
29
38
|
readonly sessionId: string;
|
|
30
39
|
/** The tool the ask was for (the message quotes it verbatim). */
|
|
31
40
|
readonly toolName: string;
|
|
32
|
-
/** The refused call's id — the correlation key to the `tool_end` frame carrying the same
|
|
41
|
+
/** The refused call's id — the correlation key to the `tool_end` frame carrying the same deny. */
|
|
33
42
|
readonly toolCallId: string;
|
|
34
|
-
/**
|
|
35
|
-
readonly
|
|
43
|
+
/** Why the ask could not be resolved — carried as a fact from the exit, not re-derived here. */
|
|
44
|
+
readonly cause: AskUnresolvableCause;
|
|
36
45
|
/** Whether a durable approval park lane was ARMED for this run (`true`: the park had its one attempt and
|
|
37
46
|
* did not take the call — it declined or failed; `false`: no park lane at all, the usual shape). */
|
|
38
47
|
readonly parkLaneExisted: boolean;
|
|
@@ -41,12 +50,12 @@ export interface AskUnresolvableFacts {
|
|
|
41
50
|
* Compose the `delegation.ask_unresolvable` notice from the gate's deny-exit facts.
|
|
42
51
|
*
|
|
43
52
|
* @contract delegation.ask_unresolvable.single_mint — the `delegation.ask_unresolvable` notice is composed
|
|
44
|
-
* by CORE from the gate's deny-exit FACTS (session, tool, call id, the
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* shaped into a notice that would route nowhere.
|
|
53
|
+
* by CORE from the gate's deny-exit FACTS (session, tool, call id, the cause, whether a park lane existed)
|
|
54
|
+
* at the ONE deny exit every unresolvable arm reaches; no other site spells the code or the sentence, and
|
|
55
|
+
* an empty session or tool name, or a cause outside the closed set, is refused at the mint with a
|
|
56
|
+
* `TypeError` rather than shaped into a notice that would route nowhere or say nothing.
|
|
48
57
|
*
|
|
49
|
-
* `detail: { sessionId, toolName, toolCallId,
|
|
50
|
-
*
|
|
58
|
+
* `detail: { sessionId, toolName, toolCallId, cause, parkLaneExisted }` — `sessionId` is lifted onto the
|
|
59
|
+
* typed key by {@link import("./engine-notice.js").deliverEngineNotice}, the one throat.
|
|
51
60
|
*/
|
|
52
61
|
export declare function askUnresolvableNotice(facts: AskUnresolvableFacts): EngineNotice;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
export const ASK_UNRESOLVABLE_CAUSES = ["approver_unavailable", "no_approver_wired", "mandate_unreconstructible"];
|
|
2
|
+
const ASK_UNRESOLVABLE_CAUSE_SET = new Set(ASK_UNRESOLVABLE_CAUSES);
|
|
3
|
+
export function isAskUnresolvableCause(v) {
|
|
4
|
+
return ASK_UNRESOLVABLE_CAUSE_SET.has(v);
|
|
5
|
+
}
|
|
6
|
+
const CAUSE_CLAUSE = {
|
|
7
|
+
approver_unavailable: "the approver reported that nobody is reachable for this ask",
|
|
8
|
+
no_approver_wired: "no approver (or question face) is wired for this run — the headless posture",
|
|
9
|
+
mandate_unreconstructible: "an inherited durable-approval mandate binds this ask and this leg has no park facility to honor it",
|
|
10
|
+
};
|
|
1
11
|
export function askUnresolvableNotice(facts) {
|
|
2
|
-
const { sessionId, toolName, toolCallId,
|
|
12
|
+
const { sessionId, toolName, toolCallId, cause, parkLaneExisted } = facts;
|
|
3
13
|
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
4
14
|
throw new TypeError("delegation.ask_unresolvable: sessionId is required — a user-audience notice with no session routes nowhere");
|
|
5
15
|
}
|
|
@@ -9,17 +19,15 @@ export function askUnresolvableNotice(facts) {
|
|
|
9
19
|
if (typeof toolCallId !== "string" || toolCallId.length === 0) {
|
|
10
20
|
throw new TypeError("delegation.ask_unresolvable: toolCallId is required");
|
|
11
21
|
}
|
|
12
|
-
if (
|
|
13
|
-
throw new TypeError(`delegation.ask_unresolvable:
|
|
22
|
+
if (!isAskUnresolvableCause(cause)) {
|
|
23
|
+
throw new TypeError(`delegation.ask_unresolvable: cause ${JSON.stringify(cause)} is not one of ${ASK_UNRESOLVABLE_CAUSES.join("/")}`);
|
|
14
24
|
}
|
|
15
|
-
const park = parkLaneExisted
|
|
16
|
-
? "the durable approval park did not take it"
|
|
17
|
-
: "no durable approval park is armed for this run";
|
|
25
|
+
const park = parkLaneExisted ? "the durable approval park did not take it" : "no durable approval park is armed for this run";
|
|
18
26
|
return {
|
|
19
27
|
code: "delegation.ask_unresolvable",
|
|
20
|
-
message: `approval for "${toolName}" could not be resolved:
|
|
28
|
+
message: `approval for "${toolName}" could not be resolved: ${CAUSE_CLAUSE[cause]} and ${park} — ` +
|
|
21
29
|
`the call was denied fail-closed and did not run. To let such approvals wait for a person, run with a checkpoint store and a ` +
|
|
22
30
|
`durable approval opt-in (or a live approver reachable for delegated children).`,
|
|
23
|
-
detail: { sessionId, toolName, toolCallId,
|
|
31
|
+
detail: { sessionId, toolName, toolCallId, cause, parkLaneExisted: parkLaneExisted === true },
|
|
24
32
|
};
|
|
25
33
|
}
|