@sema-agent/core 7.50.1 → 7.51.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 +275 -0
- package/dist/agents/child-failure.d.ts +5 -16
- package/dist/agents/child-failure.js +4 -24
- package/dist/agents/subagent-steps.js +2 -2
- package/dist/agents/subagent.d.ts +4 -13
- package/dist/agents/subagent.js +94 -204
- package/dist/agents/tool-filter.d.ts +2 -2
- package/dist/brain/anthropic.d.ts +1 -1
- package/dist/brain/anthropic.js +7 -5
- package/dist/brain/open-responses.d.ts +1 -1
- package/dist/brain/open-responses.js +3 -2
- package/dist/brain/openai.d.ts +1 -1
- package/dist/brain/openai.js +3 -2
- package/dist/brain/request-route.d.ts +2 -2
- package/dist/brain/route-adjudicator.d.ts +16 -7
- package/dist/brain/route-adjudicator.js +23 -7
- package/dist/brain/route-conformance.js +23 -5
- package/dist/brain/stream-engine.js +9 -3
- package/dist/core/a2a.js +6 -8
- package/dist/core/allow-entry.d.ts +45 -26
- package/dist/core/allow-entry.js +48 -24
- package/dist/core/arg-summary.d.ts +8 -85
- package/dist/core/arg-summary.js +4 -386
- package/dist/core/ask-origin.d.ts +35 -33
- package/dist/core/ask-origin.js +9 -11
- package/dist/core/checkpoint-requirements.d.ts +6 -0
- package/dist/core/checkpoint-requirements.js +1 -0
- package/dist/core/checkpoint-store.d.ts +15 -34
- package/dist/core/checkpoint-store.js +5 -4
- package/dist/core/classifier-unavailable-arm.d.ts +12 -8
- package/dist/core/classifier-unavailable-arm.js +7 -2
- package/dist/core/credential-vocabulary.d.ts +3 -2
- package/dist/core/credential-vocabulary.js +27 -18
- package/dist/core/delegation-frames.d.ts +40 -15
- package/dist/core/delegation-frames.js +34 -0
- package/dist/core/engine-notice.d.ts +35 -11
- package/dist/core/gate-fold.js +9 -7
- package/dist/core/gate-lanes.js +6 -4
- package/dist/core/gate-outcome.d.ts +87 -11
- package/dist/core/gate-outcome.js +171 -61
- package/dist/core/governance-codes.d.ts +7 -1
- package/dist/core/governance-codes.js +9 -0
- package/dist/core/hooks.d.ts +4 -4
- package/dist/core/hooks.js +4 -3
- package/dist/core/mcp-connection-pool.d.ts +2 -0
- package/dist/core/mcp-connection-pool.js +2 -1
- package/dist/core/mcp-failure.js +2 -1
- package/dist/core/mcp-server-spec.d.ts +27 -5
- package/dist/core/mcp.d.ts +13 -38
- package/dist/core/mcp.js +28 -23
- package/dist/core/media-byte-cap.d.ts +25 -0
- package/dist/core/media-byte-cap.js +18 -2
- package/dist/core/memory-engine/consolidation-driver.js +1 -1
- package/dist/core/mode-policy.d.ts +25 -1
- package/dist/core/mode-policy.js +28 -13
- package/dist/core/own-property-receipt.d.ts +18 -0
- package/dist/core/own-property-receipt.js +39 -0
- package/dist/core/permission-rule-model.d.ts +32 -11
- package/dist/core/permission-rule-model.js +12 -7
- package/dist/core/persisted-rule-arms.d.ts +3 -3
- package/dist/core/prompt-section-seat.d.ts +54 -0
- package/dist/core/prompt-section-seat.js +49 -0
- package/dist/core/protocol-http-auth.d.ts +44 -0
- package/dist/core/protocol-http-auth.js +45 -0
- package/dist/core/read-face-seats.d.ts +30 -0
- package/dist/core/read-face-seats.js +38 -0
- package/dist/core/redaction-passes.d.ts +82 -0
- package/dist/core/redaction-passes.js +378 -0
- package/dist/core/remote-env.d.ts +4 -3
- package/dist/core/resume-startup-cause.d.ts +56 -0
- package/dist/core/resume-startup-cause.js +43 -0
- package/dist/core/rule-denial-sentence.d.ts +2 -2
- package/dist/core/runner/announce-once-ledger.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +17 -13
- package/dist/core/runner/classifier-ladder.js +12 -10
- package/dist/core/runner/construction-doors.js +9 -20
- package/dist/core/runner/contracts.d.ts +4 -4
- package/dist/core/runner/gate-exit.js +1 -1
- package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
- package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
- package/dist/core/runner/person-path-rows.d.ts +4 -3
- package/dist/core/runner/policy-throw.js +4 -4
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
- package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
- package/dist/core/runner/prepare-gate-stations.js +3 -3
- package/dist/core/runner/prepare-hands-readface.js +1 -19
- package/dist/core/runner/prepare-inherited-gate.js +10 -9
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
- package/dist/core/runner/prepare-policy-chain.js +10 -13
- package/dist/core/runner/prepare-project-context.d.ts +4 -4
- package/dist/core/runner/prepare-project-context.js +2 -1
- package/dist/core/runner/prepare-prompt-assembly.js +1 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-inputs.js +4 -4
- package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
- package/dist/core/runner/prepare-protocol-tools.js +3 -2
- package/dist/core/runner/prepare-task.js +6 -4
- package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
- package/dist/core/runner/prepare-turn-wiring.js +8 -7
- package/dist/core/runner/run-harness-handlers.js +2 -2
- package/dist/core/runner/run-leg.js +10 -9
- package/dist/core/runner/run-settle-and-teardown.js +5 -5
- package/dist/core/runner/run-turn-boundary.js +3 -2
- package/dist/core/runner/runtask.d.ts +1 -2
- package/dist/core/runner/runtask.js +8 -12
- package/dist/core/runner/session-rule-policy.d.ts +15 -11
- package/dist/core/runner/session-rule-policy.js +43 -33
- package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
- package/dist/core/runner/stream-settle-backstop.js +2 -1
- package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
- package/dist/core/runner/sub-agent-deny-lists.js +23 -12
- package/dist/core/runner/terminal-account.js +3 -3
- package/dist/core/runner/tool-output-projection.js +3 -8
- package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
- package/dist/core/runner/tool-result-media-seat.js +13 -0
- package/dist/core/runner/turn-attachments.d.ts +13 -12
- package/dist/core/runner/turn-attachments.js +11 -7
- package/dist/core/runner-deps.d.ts +4 -3
- package/dist/core/safety-merge-corpus.js +21 -21
- package/dist/core/secret-env.js +1 -1
- package/dist/core/session-policy-store.d.ts +89 -72
- package/dist/core/session-policy-store.js +100 -93
- package/dist/core/shell-read-silence.d.ts +38 -4
- package/dist/core/shell-read-silence.js +102 -0
- package/dist/core/side-query.d.ts +7 -8
- package/dist/core/skill-grant-binding.d.ts +32 -11
- package/dist/core/skill-grant-binding.js +11 -3
- package/dist/core/skill-grant.d.ts +4 -3
- package/dist/core/skill-grant.js +5 -4
- package/dist/core/skills-directory.d.ts +12 -1
- package/dist/core/skills-directory.js +17 -8
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
- package/dist/core/swappable-deps.d.ts +15 -1
- package/dist/core/swappable-deps.js +25 -1
- package/dist/core/synthetic-read-verdict.d.ts +16 -14
- package/dist/core/synthetic-read-verdict.js +28 -8
- package/dist/core/task-notification.d.ts +13 -3
- package/dist/core/task-notification.js +12 -1
- package/dist/core/task-spec.d.ts +14 -15
- package/dist/core/tool-name-readings.d.ts +0 -4
- package/dist/core/tool-name-readings.js +0 -1
- package/dist/core/tool-policy.d.ts +40 -9
- package/dist/core/tool-policy.js +25 -7
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-egress.d.ts +60 -11
- package/dist/core/untrusted-egress.js +120 -9
- package/dist/core/untrusted-text.js +16 -0
- package/dist/core/wiring-manifest.d.ts +22 -8
- package/dist/core/wiring-manifest.js +12 -4
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +4 -1
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/llm/types.d.ts +7 -9
- package/dist/engine/loop/agent-loop.d.ts +8 -3
- package/dist/engine/loop/agent-loop.js +26 -9
- package/dist/engine/loop/types.d.ts +10 -0
- package/dist/index.d.ts +17 -5
- package/dist/index.js +14 -5
- package/dist/orchestration/run-workflow-tool.js +8 -8
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompt-assembly/assemble.d.ts +3 -1
- package/dist/prompt-assembly/assemble.js +6 -0
- package/dist/prompts/cowork.d.ts +42 -0
- package/dist/prompts/cowork.js +529 -0
- package/dist/prompts/default.d.ts +14 -1
- package/dist/prompts/default.js +3 -0
- package/dist/stores/file/session-policy-store.d.ts +6 -3
- package/dist/stores/file/session-policy-store.js +5 -16
- package/dist/tools/artifact/artifact-tool.js +2 -0
- package/dist/tools/fs/image-admission.d.ts +57 -4
- package/dist/tools/fs/image-admission.js +85 -0
- package/dist/tools/fs/path-containment.d.ts +2 -0
- package/dist/tools/fs/path-containment.js +5 -0
- package/dist/tools/fs/shell-read-check.d.ts +31 -6
- package/dist/tools/fs/shell-read-check.js +2 -4
- package/dist/tools/fs/shell-read-environment.d.ts +10 -25
- package/dist/tools/fs/shell-read-environment.js +6 -18
- package/dist/tools/fs/shell-read-establish.d.ts +3 -2
- package/dist/tools/fs/shell-read-establish.js +5 -5
- package/dist/tools/fs/shell-read-judge.d.ts +38 -8
- package/dist/tools/fs/shell-read-judge.js +12 -10
- package/dist/tools/web.js +2 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +94 -10
- package/wire-changes/core@7.50.2.json +42 -0
- package/wire-changes/core@7.51.0.json +2421 -0
|
@@ -53,8 +53,8 @@ import type { ContainmentFace } from "../tools/fs/containment-face.js";
|
|
|
53
53
|
* reads a word, an unterminated quote, a syntax error — the answer's `reason`): whether one of those
|
|
54
54
|
* rows forbids this call is unknown, so an allow tightens to an ask a person must clear and no automatic
|
|
55
55
|
* lane may answer. A store (or a partition of it) that could not be READ is this word too — a row inside it
|
|
56
|
-
* may forbid the call and nobody can tell
|
|
57
|
-
*
|
|
56
|
+
* may forbid the call and nobody can tell. Both carry the class `rule_unreadable` (bypass-immune); the run also
|
|
57
|
+
* carries one `permission.rule_store_unavailable` notice.
|
|
58
58
|
* - `hook` — a deployment's PreToolUse screening face raised or co-raised the ask; the word is
|
|
59
59
|
* engine-stamped at the fold and is the deployment's own per-call adjudication.
|
|
60
60
|
* - `ask_rule` — an EXPLICIT `ask` permission rule matched: a person's standing "ask me each time",
|
|
@@ -103,13 +103,12 @@ export interface AskOriginFacts {
|
|
|
103
103
|
readonly org: "rule" | "unavailable" | undefined;
|
|
104
104
|
/** The gate's own record of its persisted-rule lane's READ: the store was read, but THIS CALL could not be read
|
|
105
105
|
* against the person's deny/ask rows (the lane's answer carried the lexer's reason) — the origin
|
|
106
|
-
* `rule_store_unavailable`, the class `rule_unreadable
|
|
106
|
+
* `rule_store_unavailable`, the class `rule_unreadable`. Absent: the lane read the call, or the store
|
|
107
107
|
* could not be read (see {@link ruleStoreUnreadable}). Written only by the gate. */
|
|
108
108
|
readonly ruleCallUnreadable?: boolean;
|
|
109
109
|
/** The gate's own record of the same read when a PARTITION of the store (or all of it) could not be read: a deny / ask
|
|
110
|
-
* row inside it may forbid this call and nobody can tell — the same origin
|
|
111
|
-
*
|
|
112
|
-
* Exclusive with {@link ruleCallUnreadable}. Written only by the gate. */
|
|
110
|
+
* row inside it may forbid this call and nobody can tell — the same origin and the same class as {@link ruleCallUnreadable}.
|
|
111
|
+
* Exclusive with it. Written only by the gate. */
|
|
113
112
|
readonly ruleStoreUnreadable?: boolean;
|
|
114
113
|
/** Which of the gate's OWN post-fold stations minted the standing ask, if one did — again the gate's
|
|
115
114
|
* record of its own layer (a station replaces an `allow` wholesale, so at most one fires per pass),
|
|
@@ -156,6 +155,15 @@ export declare function stampAskOrigin<T extends Extract<PermissionResult, {
|
|
|
156
155
|
}>>(decision: T, facts: AskOriginFacts, classification: AskClassification): T & {
|
|
157
156
|
origin: AskOrigin;
|
|
158
157
|
} & AskClassMembers;
|
|
158
|
+
/**
|
|
159
|
+
* WHY an approval card offers no persisted allow rule, one word per reason — the closed set of `ruleOffersAbsence`
|
|
160
|
+
* on the ask and its park twin, written by the one offer factory: `mandated` (no persisted rule may clear the ask),
|
|
161
|
+
* `allow_outranked` (an allow-layer member already matched this call and the question outranks it), `ask_rule_matched`
|
|
162
|
+
* (an explicit `ask` rule matched: it keeps asking), `lane_cannot_speak` (a rule could clear the ask and the lane has no
|
|
163
|
+
* text for this call). The first three are {@link ruleClearanceOf}'s doors.
|
|
164
|
+
*/
|
|
165
|
+
export declare const RULE_OFFERS_ABSENCES: readonly ["mandated", "allow_outranked", "ask_rule_matched", "lane_cannot_speak"];
|
|
166
|
+
export type RuleOffersAbsence = (typeof RULE_OFFERS_ABSENCES)[number];
|
|
159
167
|
/**
|
|
160
168
|
* CAN a persisted allow rule clear this ask — and, if not, which door closed? ONE derivation with TWO
|
|
161
169
|
* readers, which is the whole point of the shape: the approval card's `ruleOffersAbsence` word and the
|
|
@@ -177,20 +185,20 @@ export declare function stampAskOrigin<T extends Extract<PermissionResult, {
|
|
|
177
185
|
* approver and this task's rule lane never adjudicates it;
|
|
178
186
|
* · the bit already stamped on the ask, which no layer below the mint may clear (tightening-only).
|
|
179
187
|
*
|
|
180
|
-
* And
|
|
181
|
-
* (`allowRuleShadowed`), or the person's explicit "ask me each time" rule matched
|
|
182
|
-
* (`matchedAskRule`). A rule minted from such a card silences nothing, so the lane offers none — but
|
|
188
|
+
* And two doors that are not mandates, each its own word: an allow-layer member already matched and is outranked
|
|
189
|
+
* (`allowRuleShadowed` ⇒ `allow_outranked`), or the person's explicit "ask me each time" rule matched
|
|
190
|
+
* (`matchedAskRule` ⇒ `ask_rule_matched`). A rule minted from such a card silences nothing, so the lane offers none — but
|
|
183
191
|
* the reason is that a RULE is already speaking, not that the question is beyond rules, and a surface
|
|
184
192
|
* that auto-answers by posture is not crossing a mandate when it answers one.
|
|
185
193
|
*
|
|
186
194
|
* FOUR named readings of the one pass, because four callers need four different things and taking
|
|
187
195
|
* the wrong one is how this boundary drifts:
|
|
188
|
-
* · `word` — the DISCLOSED reason, first door wins, in the display order the card has always
|
|
189
|
-
* (governance, the
|
|
196
|
+
* · `word` — the DISCLOSED reason ({@link RuleOffersAbsence}), first door wins, in the display order the card has always
|
|
197
|
+
* used (governance, the outranked allow, the hook, the ask rule, the two ancestor doors, then the marks). Display
|
|
190
198
|
* only. A structural door the offer factory owns (no lane wired, a tool it does not speak for, a
|
|
191
199
|
* task that cannot hold a rule) is decided BEFORE this and is deliberately silent;
|
|
192
200
|
* · `mandated` — the FACT the ask carries as its own member. Read off the whole mandate set, so which
|
|
193
|
-
* door is DISPLAYED cannot change it: an ask that is both
|
|
201
|
+
* door is DISPLAYED cannot change it: an ask that is both outranked and mandated is mandated;
|
|
194
202
|
* · `ruleMayClear` — the ENFORCEMENT reading the gate's clearing arms take. It is `mandated` plus the
|
|
195
203
|
* person's explicit `ask` rule, and deliberately NOT `word !== undefined`: `allowRuleShadowed`
|
|
196
204
|
* is an ACCOUNT of a refusal that already happened, display-only by contract, and reading it as a
|
|
@@ -201,7 +209,7 @@ export declare function stampAskOrigin<T extends Extract<PermissionResult, {
|
|
|
201
209
|
* not one word.
|
|
202
210
|
*/
|
|
203
211
|
export declare function ruleClearanceOf(ask: RuleClearanceFacts | undefined, marks: Omit<MandateMarks, "probeMandated">): {
|
|
204
|
-
word?:
|
|
212
|
+
word?: Exclude<RuleOffersAbsence, "lane_cannot_speak">;
|
|
205
213
|
mandated: boolean;
|
|
206
214
|
ruleMayClear: boolean;
|
|
207
215
|
mandate?: PersistedRuleMandate;
|
|
@@ -347,7 +355,7 @@ export declare function mandateDisclosureOf(mandate: PersistedRuleMandate | unde
|
|
|
347
355
|
* in {@link irreversibilityMandateOf} which {@link safetyAxisOf} shares: such a write is not an irreversible action, and
|
|
348
356
|
* its park stays budgetable.
|
|
349
357
|
*
|
|
350
|
-
* Two consumers, one derivation: the gate's silencing arm (a matching rule is disclosed as
|
|
358
|
+
* Two consumers, one derivation: the gate's silencing arm (a matching rule is disclosed as outranked
|
|
351
359
|
* instead of clearing the ask) and the runner's suggestion factory (a mandated ask offers no
|
|
352
360
|
* "stop asking me this" option — a rule minted from it would never clear it). A drift between the
|
|
353
361
|
* two would let a card offer a rule the lane then refuses to honor. The per-call member reaches both
|
|
@@ -379,7 +387,7 @@ export interface MandateMarks {
|
|
|
379
387
|
targetUnresolved?: boolean;
|
|
380
388
|
/** The pass's own record that the probe was consulted and did not answer (`GatePass.probeUnanswered`):
|
|
381
389
|
* the boundary is unknown, which no standing yes may stand in for. The runner's suggestion factory does
|
|
382
|
-
* not see the pass and never passes it — a card may offer a rule the lane then discloses as
|
|
390
|
+
* not see the pass and never passes it — a card may offer a rule the lane then discloses as outranked
|
|
383
391
|
* (loud, never a silent override), the same drift window the `matchedAskRule` conjunct already has. */
|
|
384
392
|
probeUnanswered?: boolean;
|
|
385
393
|
}
|
|
@@ -467,11 +475,12 @@ export type ToolMarkSource = "deployment" | "mcp_annotation" | "a2a_default";
|
|
|
467
475
|
* working directory — or an ancestor of one): the reference implementation's dangerous-removal breaker, which no mode,
|
|
468
476
|
* allow rule or classifier answers. `unverifiable_delete` is its sibling for a target the command text cannot resolve;
|
|
469
477
|
* `unverifiable_shell_text` is a shell handed text the command does not spell (a program's output, a substitution, a
|
|
470
|
-
* file's content, an argv a stream fills in): the engine cannot read what that shell will execute.
|
|
471
|
-
*
|
|
472
|
-
* and the
|
|
478
|
+
* file's content, an argv a stream fills in): the engine cannot read what that shell will execute. The three read one
|
|
479
|
+
* row ({@link REMOVAL_BREAKER_ROW}).
|
|
480
|
+
* `rule_unreadable` is a rule that may govern the call and cannot be read for it: the organization's snapshot, a partition of
|
|
481
|
+
* the person's store, or this call against a deny / ask row (the lexer's word, a content row, the read station's rows).
|
|
473
482
|
*/
|
|
474
|
-
export declare const ASK_CLASSES: readonly ["content_question", "hook", "ancestor", "dangerous_delete", "unverifiable_delete", "unverifiable_shell_text", "transcript_write", "org_rule", "
|
|
483
|
+
export declare const ASK_CLASSES: readonly ["content_question", "hook", "ancestor", "dangerous_delete", "unverifiable_delete", "unverifiable_shell_text", "transcript_write", "org_rule", "rule_unreadable", "ask_rule", "denial_limit_fallback", "classifier_no_ruling", "requires_user_interaction", "operator_forced", "shell_floor", "target_unresolved", "probe", "write_protection", "tool_marks_mcp", "tool_marks_a2a", "policy_rra", "artifact_shared_read", "mode", "unstated", "ordinary"];
|
|
475
484
|
export type AskClass = (typeof ASK_CLASSES)[number];
|
|
476
485
|
/** Membership test for {@link AskClass} — the word crosses the wire on `tool_end.gate.disposition`. */
|
|
477
486
|
export declare function isAskClass(v: unknown): v is AskClass;
|
|
@@ -531,27 +540,27 @@ export declare const MODE_ARBITRATION: {
|
|
|
531
540
|
readonly ruleMayClear: false;
|
|
532
541
|
readonly hookAskFloor: false;
|
|
533
542
|
};
|
|
534
|
-
readonly dangerous_delete: {
|
|
543
|
+
readonly dangerous_delete: Readonly<{
|
|
535
544
|
readonly bypassImmune: true;
|
|
536
545
|
readonly classifierEligible: false;
|
|
537
546
|
readonly sandboxAdmission: false;
|
|
538
547
|
readonly ruleMayClear: false;
|
|
539
548
|
readonly hookAskFloor: true;
|
|
540
|
-
}
|
|
541
|
-
readonly unverifiable_delete: {
|
|
549
|
+
}>;
|
|
550
|
+
readonly unverifiable_delete: Readonly<{
|
|
542
551
|
readonly bypassImmune: true;
|
|
543
|
-
readonly classifierEligible:
|
|
552
|
+
readonly classifierEligible: false;
|
|
544
553
|
readonly sandboxAdmission: false;
|
|
545
554
|
readonly ruleMayClear: false;
|
|
546
555
|
readonly hookAskFloor: true;
|
|
547
|
-
}
|
|
548
|
-
readonly unverifiable_shell_text: {
|
|
556
|
+
}>;
|
|
557
|
+
readonly unverifiable_shell_text: Readonly<{
|
|
549
558
|
readonly bypassImmune: true;
|
|
550
559
|
readonly classifierEligible: false;
|
|
551
560
|
readonly sandboxAdmission: false;
|
|
552
561
|
readonly ruleMayClear: false;
|
|
553
562
|
readonly hookAskFloor: true;
|
|
554
|
-
}
|
|
563
|
+
}>;
|
|
555
564
|
readonly transcript_write: {
|
|
556
565
|
readonly bypassImmune: false;
|
|
557
566
|
readonly classifierEligible: true;
|
|
@@ -566,13 +575,6 @@ export declare const MODE_ARBITRATION: {
|
|
|
566
575
|
readonly ruleMayClear: false;
|
|
567
576
|
readonly hookAskFloor: false;
|
|
568
577
|
};
|
|
569
|
-
readonly governance_unavailable: {
|
|
570
|
-
readonly bypassImmune: true;
|
|
571
|
-
readonly classifierEligible: false;
|
|
572
|
-
readonly sandboxAdmission: false;
|
|
573
|
-
readonly ruleMayClear: false;
|
|
574
|
-
readonly hookAskFloor: false;
|
|
575
|
-
};
|
|
576
578
|
readonly rule_unreadable: {
|
|
577
579
|
readonly bypassImmune: true;
|
|
578
580
|
readonly classifierEligible: false;
|
package/dist/core/ask-origin.js
CHANGED
|
@@ -23,6 +23,7 @@ export function stampAskOrigin(decision, facts, classification) {
|
|
|
23
23
|
const { probeReadRootCandidate: _withdrawn, ...kept } = mandated;
|
|
24
24
|
return { ...kept, origin, ...classed };
|
|
25
25
|
}
|
|
26
|
+
export const RULE_OFFERS_ABSENCES = Object.freeze(["mandated", "allow_outranked", "ask_rule_matched", "lane_cannot_speak"]);
|
|
26
27
|
export function ruleClearanceOf(ask, marks) {
|
|
27
28
|
const marksWord = persistedRuleMandateOf({ ...marks, probeMandated: ask?.probeMandated === true });
|
|
28
29
|
const foldedDoor = (ask?.foldedAskClasses ?? []).some((c) => !MODE_ARBITRATION[c].ruleMayClear);
|
|
@@ -34,7 +35,7 @@ export function ruleClearanceOf(ask, marks) {
|
|
|
34
35
|
if (ask?.requiresRealApproval === true)
|
|
35
36
|
return "mandated";
|
|
36
37
|
if (Array.isArray(ask?.allowRuleShadowed) && ask.allowRuleShadowed.length > 0)
|
|
37
|
-
return "
|
|
38
|
+
return "allow_outranked";
|
|
38
39
|
if (ask?.decisionReason === "hook")
|
|
39
40
|
return "mandated";
|
|
40
41
|
if (ask?.decisionReason === "mode" || foldedDoor)
|
|
@@ -42,7 +43,7 @@ export function ruleClearanceOf(ask, marks) {
|
|
|
42
43
|
if (ask?.authoredBy === "deployment")
|
|
43
44
|
return "mandated";
|
|
44
45
|
if (ask?.matchedAskRule !== undefined)
|
|
45
|
-
return "
|
|
46
|
+
return "ask_rule_matched";
|
|
46
47
|
if (ask?.inheritedUnresolved === true)
|
|
47
48
|
return "mandated";
|
|
48
49
|
if (ask?.ancestorResolved === true)
|
|
@@ -177,7 +178,6 @@ export const ASK_CLASSES = Object.freeze([
|
|
|
177
178
|
"unverifiable_shell_text",
|
|
178
179
|
"transcript_write",
|
|
179
180
|
"org_rule",
|
|
180
|
-
"governance_unavailable",
|
|
181
181
|
"rule_unreadable",
|
|
182
182
|
"ask_rule",
|
|
183
183
|
"denial_limit_fallback",
|
|
@@ -200,16 +200,16 @@ const ASK_CLASS_SET = new Set(ASK_CLASSES);
|
|
|
200
200
|
export function isAskClass(v) {
|
|
201
201
|
return ASK_CLASS_SET.has(v);
|
|
202
202
|
}
|
|
203
|
+
const REMOVAL_BREAKER_ROW = Object.freeze({ bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true });
|
|
203
204
|
export const MODE_ARBITRATION = {
|
|
204
205
|
content_question: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true },
|
|
205
206
|
hook: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
206
207
|
ancestor: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
207
|
-
dangerous_delete:
|
|
208
|
-
unverifiable_delete:
|
|
209
|
-
unverifiable_shell_text:
|
|
208
|
+
dangerous_delete: REMOVAL_BREAKER_ROW,
|
|
209
|
+
unverifiable_delete: REMOVAL_BREAKER_ROW,
|
|
210
|
+
unverifiable_shell_text: REMOVAL_BREAKER_ROW,
|
|
210
211
|
transcript_write: { bypassImmune: false, classifierEligible: true, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true },
|
|
211
212
|
org_rule: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
212
|
-
governance_unavailable: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
213
213
|
rule_unreadable: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
214
214
|
ask_rule: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true },
|
|
215
215
|
denial_limit_fallback: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false },
|
|
@@ -237,7 +237,7 @@ function bypassImmuneOn(c, face) {
|
|
|
237
237
|
return MODE_ARBITRATION[c].bypassImmune && !(face === "shell" && RELEASED_ON_SHELL_FACE.has(c));
|
|
238
238
|
}
|
|
239
239
|
const CLASSIFIER_WITHHELD_ON_ROOTS = new Set(["probe"]);
|
|
240
|
-
export const RRA_EXPLAINERS = new Set(["dangerous_delete", "unverifiable_delete", "unverifiable_shell_text", "transcript_write", "org_rule", "
|
|
240
|
+
export const RRA_EXPLAINERS = new Set(["dangerous_delete", "unverifiable_delete", "unverifiable_shell_text", "transcript_write", "org_rule", "rule_unreadable", "denial_limit_fallback", "classifier_no_ruling", "ancestor", "artifact_shared_read", "requires_user_interaction"]);
|
|
241
241
|
const RELEASABLE_BY_FACE = Object.freeze({
|
|
242
242
|
shell: Object.freeze(ASK_CLASSES.filter((c) => !bypassImmuneOn(c, "shell"))),
|
|
243
243
|
other: Object.freeze(ASK_CLASSES.filter((c) => !bypassImmuneOn(c, "other"))),
|
|
@@ -316,9 +316,7 @@ export function classifyAsk(d, facts) {
|
|
|
316
316
|
classes.add("ancestor");
|
|
317
317
|
if (facts.org === "rule")
|
|
318
318
|
classes.add("org_rule");
|
|
319
|
-
if (facts.org === "unavailable" || facts.ruleStoreUnreadable === true)
|
|
320
|
-
classes.add("governance_unavailable");
|
|
321
|
-
if (facts.ruleCallUnreadable === true)
|
|
319
|
+
if (facts.org === "unavailable" || facts.ruleStoreUnreadable === true || facts.ruleCallUnreadable === true)
|
|
322
320
|
classes.add("rule_unreadable");
|
|
323
321
|
const marks = facts.mandateMarks;
|
|
324
322
|
if (marks.requiresUserInteraction === true)
|
|
@@ -290,6 +290,9 @@ export declare const PENDING_ACTION_REQUIREMENTS: {
|
|
|
290
290
|
readonly boundGrants: {
|
|
291
291
|
readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
|
|
292
292
|
};
|
|
293
|
+
readonly boundGrantAbsence: {
|
|
294
|
+
readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
|
|
295
|
+
};
|
|
293
296
|
readonly capabilityId: {
|
|
294
297
|
readonly digest: "decision-read";
|
|
295
298
|
};
|
|
@@ -784,6 +787,9 @@ export declare const CHECKPOINT_REQUIREMENTS: {
|
|
|
784
787
|
readonly boundGrants: {
|
|
785
788
|
readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
|
|
786
789
|
};
|
|
790
|
+
readonly boundGrantAbsence: {
|
|
791
|
+
readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
|
|
792
|
+
};
|
|
787
793
|
readonly capabilityId: {
|
|
788
794
|
readonly digest: "decision-read";
|
|
789
795
|
};
|
|
@@ -114,6 +114,7 @@ export const PENDING_ACTION_REQUIREMENTS = {
|
|
|
114
114
|
boundInputHash: DECISION_READ,
|
|
115
115
|
boundGrantDigest: { word: "skill_card_binding" },
|
|
116
116
|
boundGrants: SHOWN,
|
|
117
|
+
boundGrantAbsence: SHOWN,
|
|
117
118
|
capabilityId: DECISION_READ,
|
|
118
119
|
contractId: DECISION_READ,
|
|
119
120
|
shapeDigest: DECISION_READ,
|
|
@@ -10,6 +10,7 @@ import type { WorkspaceHandle } from "./remote-env.js";
|
|
|
10
10
|
import type { SessionPermissionRules } from "./session-policy-store.js";
|
|
11
11
|
import type { NestedUsageAccum, PermissionMode, ShellGateDoctrine, TaskResult } from "./types.js";
|
|
12
12
|
import type { ActiveWorktreeSession } from "../internal/harness-types.js";
|
|
13
|
+
import type { ReopenReason } from "./resume-startup-cause.js";
|
|
13
14
|
/**
|
|
14
15
|
* design/45 — the **durable-checkpoint** primitive: cross-process / resumable `suspend`/`resume`.
|
|
15
16
|
*
|
|
@@ -768,23 +769,13 @@ export type PendingAction = {
|
|
|
768
769
|
* — the PARK twin of `AskRequest.ruleOffersAbsence`, minted by the SAME one factory as
|
|
769
770
|
* {@link ruleOffers} so the parked card and the live card cannot answer differently. Present ⇔
|
|
770
771
|
* the rule-offer lane is in play for this call and has nothing to give, naming which door
|
|
771
|
-
* closed
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
*
|
|
777
|
-
|
|
778
|
-
* never take effect where it was minted; confirming the call is the whole of what a person can
|
|
779
|
-
* do about it),
|
|
780
|
-
* `"shadowed"` (a rule the person already wrote is
|
|
781
|
-
* speaking and does not clear it), `"lane_cannot_speak"` (this card has no rule to offer — the
|
|
782
|
-
* grammar has no text for the command, or the card's array-order contract declines to present
|
|
783
|
-
* the one text that exists; it is NOT a claim that no rule could ever be written).
|
|
784
|
-
* Mutually exclusive with {@link ruleOffers}. ABSENCE is not a claim — it also covers
|
|
785
|
-
* "there are offers" and the structural doors (no lane wired, another tool, a task that cannot
|
|
786
|
-
* hold a rule) — so read presence. See the synchronous seat's doc for the full contract. */
|
|
787
|
-
ruleOffersAbsence?: "mandated" | "lane_cannot_speak" | "shadowed";
|
|
772
|
+
* closed, one word of {@link import("./ask-origin.js").RULE_OFFERS_ABSENCES} per reason (`"mandated"`:
|
|
773
|
+
* an inbox must NOT point at rule-writing nor at a directory grant on this arm — confirming the call
|
|
774
|
+
* is the whole of what a person can do about it). Mutually exclusive with {@link ruleOffers}.
|
|
775
|
+
* ABSENCE is not a claim — it also covers "there are offers" and the structural doors (no lane
|
|
776
|
+
* wired, another tool, a task that cannot hold a rule) — so read presence. See the synchronous
|
|
777
|
+
* seat's doc for the full contract. */
|
|
778
|
+
ruleOffersAbsence?: import("./ask-origin.js").RuleOffersAbsence;
|
|
788
779
|
/** design/382 §2.4 (adversarial-review r3; additive — no checkpoint-version bump, the
|
|
789
780
|
* `previewWithheld` precedent: an optional field an older reader ignores and the resume
|
|
790
781
|
* path never reads) — the PARK twin of `AskRequest.execCwd`, minted by the SAME factory as
|
|
@@ -807,7 +798,7 @@ export type PendingAction = {
|
|
|
807
798
|
* Minted from the SAME facts object the row's rule-offer factory reads, through
|
|
808
799
|
* {@link import("./ask-origin.js").askMandateMember} (the live card's derivation). Three independent
|
|
809
800
|
* facts: {@link requiresRealApproval} (only a person — implies this one), this one (no rule), and
|
|
810
|
-
* {@link ruleOffersAbsence} (what the offer lane has to give: silent off its tool and wiring, `"
|
|
801
|
+
* {@link ruleOffersAbsence} (what the offer lane has to give: silent off its tool and wiring, `"ask_rule_matched"`
|
|
811
802
|
* when a person's rule also speaks — never a substitute for this one). Display metadata: no gate, CAS or
|
|
812
803
|
* resume path reads it. */
|
|
813
804
|
mandated?: true;
|
|
@@ -856,6 +847,8 @@ export type PendingAction = {
|
|
|
856
847
|
boundGrantDigest?: string;
|
|
857
848
|
/** The value {@link boundGrantDigest} hashes — the card's twin of `AskRequest.boundGrants`, minted by the same factory; written exactly when the digest is. Display metadata (additive, no version bump): the resume judges the digest alone. */
|
|
858
849
|
boundGrants?: import("./skill-grant-binding.js").BoundSkillGrants;
|
|
850
|
+
/** The PARK twin of `AskRequest.boundGrantAbsence`, minted by the same factory: written exactly when the digest is not. Display metadata. */
|
|
851
|
+
boundGrantAbsence?: import("./skill-grant-binding.js").BoundGrantAbsence;
|
|
859
852
|
/**
|
|
860
853
|
* design/388 §2.5 boundary ② (B18; additive — no checkpoint-version bump, the `previewWithheld`
|
|
861
854
|
* precedent: an optional group an older reader ignores): the IDENTITY of the row the park minted
|
|
@@ -1602,22 +1595,8 @@ export interface ResolvedOutcome {
|
|
|
1602
1595
|
* (`when` differs per replay; the record is a consequence of the decision, not its substance). */
|
|
1603
1596
|
gateOutcome?: import("./gate-outcome.js").GateOutcome;
|
|
1604
1597
|
}
|
|
1605
|
-
/**
|
|
1606
|
-
|
|
1607
|
-
* pending). The reason drives re-resume validation (§3 invariant #1): an `env_failed` reopen is a SYSTEM
|
|
1608
|
-
* RETRY of the ALREADY-APPROVED action — the re-resume MUST replay the persisted {@link ResolvedOutcome}
|
|
1609
|
-
* winner, never a new vote — while a `tool_unavailable` reopen (the action could not run, may now be
|
|
1610
|
-
* invalid — P-7) lets a human RE-DECIDE with the tool present, so a fresh operator decision IS allowed.
|
|
1611
|
-
*
|
|
1612
|
-
* `constraint_rejected` (F-012 L3) carries the same FRESH-REDECISION semantics as `tool_unavailable`:
|
|
1613
|
-
* a resolve was retracted because its decided INPUT failed a constraint re-adjudication (a frozen
|
|
1614
|
-
* ancestor projection / resume-edit policy denied the approver's edit) — the ask itself is unconsumed,
|
|
1615
|
-
* so the returned-to-pending row takes a fresh operator decision, never a forced replay of the
|
|
1616
|
-
* rejected one. Core's own edit re-adjudication runs PRE-CAS (nothing to reopen there); this reason
|
|
1617
|
-
* exists for a deployment lane that discovers the same rejection only after its own resolve landed
|
|
1618
|
-
* and must return the row to pending without inventing an out-of-vocabulary reason.
|
|
1619
|
-
*/
|
|
1620
|
-
export type ReopenReason = "env_failed" | "tool_unavailable" | "constraint_rejected";
|
|
1598
|
+
/** Why a consumed checkpoint was reopened — declared beside the one table that picks it. */
|
|
1599
|
+
export { REOPEN_REASONS, type ReopenReason } from "./resume-startup-cause.js";
|
|
1621
1600
|
/**
|
|
1622
1601
|
* design/80 D-1 (atomicity fix): the optimistic-concurrency precondition a {@link CheckpointStore.resolve}
|
|
1623
1602
|
* caller passes so its CAS is atomic with the validation it did against an earlier `get()` snapshot. The
|
|
@@ -2013,6 +1992,8 @@ export interface CheckpointSummary {
|
|
|
2013
1992
|
boundGrantDigest?: string;
|
|
2014
1993
|
/** The row's `boundGrants` — a fresh copy of the skill and its two lists, published only when it hashes to {@link boundGrantDigest} (never a list beside a digest that binds another). Deployment text: render with contextual escaping. ECHO-ONLY. */
|
|
2015
1994
|
boundGrants?: import("./skill-grant-binding.js").BoundSkillGrants;
|
|
1995
|
+
/** Every `tool_approval` summary carries {@link boundGrantDigest} or this, never both: the row's word when it is a member and the row has no digest, else `unreadable` (the row does not say what it binds). ECHO-ONLY. */
|
|
1996
|
+
boundGrantAbsence?: import("./skill-grant-binding.js").BoundGrantAbsence;
|
|
2016
1997
|
/** The row's `PendingAction.tool_approval.origin`, projected for the same one-call reason: a card
|
|
2017
1998
|
* rendered by the ask's origin word must not re-open the row to learn it. Echoed ONLY when the stored
|
|
2018
1999
|
* value is a member of the closed set ({@link import("./ask-origin.js").isAskOrigin}) — a bad value
|
|
@@ -7,7 +7,7 @@ import { isAskOrigin, isPersistedRuleMandate } from "./ask-origin.js";
|
|
|
7
7
|
import { readBoundBinding } from "./skill-grant-binding.js";
|
|
8
8
|
import { checkpointExecutionRecorded, executionRecordDisposition, ExecutionOutcomeConflictError, } from "./checkpoint-execution-record.js";
|
|
9
9
|
import { renderUntrustedCommandText, stripFormatCharacters } from "./permission-rule-model.js";
|
|
10
|
-
import {
|
|
10
|
+
import { egressText } from "./untrusted-egress.js";
|
|
11
11
|
import { steerReplayRefusal } from "./steer-replay.js";
|
|
12
12
|
import { isToolApprovalCard } from "./tool-face.js";
|
|
13
13
|
import { isPermissionMode } from "./ask-origin.js";
|
|
@@ -131,7 +131,7 @@ export function buildRiskDescriptor(input) {
|
|
|
131
131
|
if (shell || toolName === "Bash") {
|
|
132
132
|
const cmd = isPlainRecord(args) ? safeDataValue(args, "command") : undefined;
|
|
133
133
|
if (typeof cmd === "string" && cmd.length > 0)
|
|
134
|
-
summary = renderUntrustedCommandText(stripFormatCharacters(
|
|
134
|
+
summary = renderUntrustedCommandText(stripFormatCharacters(egressText("same-principal", cmd)), SUMMARY_CMD_MAX);
|
|
135
135
|
const bg = isPlainRecord(args) ? safeDataValue(args, "run_in_background") : undefined;
|
|
136
136
|
if (bg === true)
|
|
137
137
|
summary = `[background persistent process — no per-step recheck] ${summary ?? ""}`.trimEnd();
|
|
@@ -146,7 +146,7 @@ export function buildRiskDescriptor(input) {
|
|
|
146
146
|
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
147
147
|
const encDigest = (s) => s.replace(/%/g, "%25").replace(/=/g, "%3D").replace(/ /g, "%20");
|
|
148
148
|
const k = encDigest(renderUntrustedCommandText(key, 40));
|
|
149
|
-
const val = encDigest(renderUntrustedCommandText(stripFormatCharacters(
|
|
149
|
+
const val = encDigest(renderUntrustedCommandText(stripFormatCharacters(egressText("same-principal", String(v))), SUMMARY_VALUE_MAX));
|
|
150
150
|
parts.push(`${k}=${val}`);
|
|
151
151
|
}
|
|
152
152
|
}
|
|
@@ -247,6 +247,7 @@ export function remainingTokens(ledger) {
|
|
|
247
247
|
return 0;
|
|
248
248
|
return Math.max(0, total - (spent ?? 0));
|
|
249
249
|
}
|
|
250
|
+
export { REOPEN_REASONS } from "./resume-startup-cause.js";
|
|
250
251
|
export function summarizeCheckpoint(cp) {
|
|
251
252
|
const riskDescriptor = cp.gate.kind === "human" || cp.gate.kind === "irreversible_ask" ? cp.gate.riskDescriptor : undefined;
|
|
252
253
|
const severity = riskDescriptor !== undefined ? riskSeverity(riskDescriptor.axes) : undefined;
|
|
@@ -300,7 +301,7 @@ export function summarizeCheckpoint(cp) {
|
|
|
300
301
|
const why = tool?.classifierNoRuling;
|
|
301
302
|
return { ...(isAskOrigin(origin) ? { origin } : {}), ...(isPersistedRuleMandate(mandate) ? { mandate } : {}), ...(isAutoModeFallbackCause(why) ? { classifierNoRuling: why } : {}) };
|
|
302
303
|
})(),
|
|
303
|
-
...readBoundBinding(tool),
|
|
304
|
+
...(tool !== undefined ? readBoundBinding(tool) : {}),
|
|
304
305
|
...(hasBidi ? { hasBidiControls: true } : {}),
|
|
305
306
|
};
|
|
306
307
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* budget ran out — `error` / `timeout` — or it replied with no verdict at all — `parse_error`) — shared by both classifier
|
|
4
4
|
* stations, the leg's gate and an ancestor's frozen classifier in the policy fold, so the rule is written once.
|
|
5
5
|
*
|
|
6
|
-
* ONE fork, on the APPROVAL
|
|
7
|
-
* with
|
|
6
|
+
* ONE fork, on the APPROVAL ROUTE the station states (`ApprovalRoute`, the one seat decision — `live_approver`,
|
|
7
|
+
* `durable_park` or `none`): with a person on the route, the ask goes on carrying why (`classifierNoRuling`, its text saying it too), and a person decides — never
|
|
8
8
|
* counted (a person's refusal of it is a person's); without one, an unavailable classification is refused with CC's `x1t`
|
|
9
9
|
* sentence (its FORM, `unavailable`, attested on the deny) and is never counted, while a reply with no verdict goes back to
|
|
10
10
|
* the station's ruling path (`counted`): refused with its own sentence and counted on the denial limit, so the bound falls
|
|
@@ -18,22 +18,26 @@
|
|
|
18
18
|
import { type AutoModeClassifierRound, type AutoModeDenialTracker, type AutoModeFallbackCause, type UnarmedDenialLimitFallback } from "./auto-mode.js";
|
|
19
19
|
import { type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "./tool-policy.js";
|
|
20
20
|
import type { Settlement } from "./gate-outcome.js";
|
|
21
|
-
|
|
21
|
+
import type { ApprovalRoute } from "./wiring-manifest.js";
|
|
22
|
+
/** Where a classification that did not rule sent the call: `asked` = on to the approval seat, `denied` = refused — the
|
|
23
|
+
* projection of the route ({@link classifierUnavailableDispositionOf}), never a second reading of it. */
|
|
22
24
|
export type ClassifierUnavailableDisposition = "asked" | "denied";
|
|
25
|
+
/** The disposition a route projects: a route with a person on it is `asked`, `none` is `denied`. */
|
|
26
|
+
export declare function classifierUnavailableDispositionOf(route: ApprovalRoute): ClassifierUnavailableDisposition;
|
|
23
27
|
/** What the stations' observer receives — the facts the `classifier.unavailable` notice is composed from. `cause` is why
|
|
24
28
|
* the classification did not rule ({@link AutoModeFallbackCause}: `error` / `timeout`, or `parse_error` for a reply with no
|
|
25
|
-
* verdict). */
|
|
29
|
+
* verdict); `route` is where the station sent the call (the disposition the notice's detail carries is its projection). */
|
|
26
30
|
export interface ClassifierUnavailableInfo {
|
|
27
31
|
toolName: string;
|
|
28
32
|
toolCallId: string;
|
|
29
33
|
cause: AutoModeFallbackCause;
|
|
30
|
-
|
|
34
|
+
route: ApprovalRoute;
|
|
31
35
|
}
|
|
32
36
|
export type ClassifierUnavailableObserver = (info: ClassifierUnavailableInfo) => void;
|
|
33
|
-
/** The station's two facts for the fork:
|
|
34
|
-
*
|
|
37
|
+
/** The station's two facts for the fork: the route an ask it hands on takes (`approvalRouteOf` / `frozenApproverRoute` —
|
|
38
|
+
* a value outside the two person routes, or none stated, is `none`), and who hears it. */
|
|
35
39
|
export interface ClassifierUnavailableSeat {
|
|
36
|
-
|
|
40
|
+
route?: ApprovalRoute;
|
|
37
41
|
onUnavailable?: ClassifierUnavailableObserver;
|
|
38
42
|
}
|
|
39
43
|
/** The arm the fork chose: the ask to hand on, the refusal, or — a reply with no verdict and no seat — the station's own
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { classifierNoRulingAskMessage, classifierUnavailableDenyMessage } from "./auto-mode.js";
|
|
2
2
|
import { attest } from "./tool-policy.js";
|
|
3
|
+
export function classifierUnavailableDispositionOf(route) {
|
|
4
|
+
return route === "none" ? "denied" : "asked";
|
|
5
|
+
}
|
|
6
|
+
const routeOfSeat = (seat) => (seat?.route === "live_approver" || seat?.route === "durable_park" ? seat.route : "none");
|
|
3
7
|
export function classifierNoRulingArm(args) {
|
|
4
8
|
const { ask, req, cause } = args;
|
|
5
|
-
const
|
|
9
|
+
const route = routeOfSeat(args.seat);
|
|
10
|
+
const seated = route !== "none";
|
|
6
11
|
if (!args.aborted) {
|
|
7
12
|
try {
|
|
8
|
-
args.seat?.onUnavailable?.({ toolName: req.toolName, toolCallId: req.toolCallId, cause,
|
|
13
|
+
args.seat?.onUnavailable?.({ toolName: req.toolName, toolCallId: req.toolCallId, cause, route });
|
|
9
14
|
}
|
|
10
15
|
catch {
|
|
11
16
|
}
|
|
@@ -115,8 +115,9 @@ export interface EnvNameView {
|
|
|
115
115
|
/** A credential word in its position, case-insensitive. */
|
|
116
116
|
readonly credentialWord: RegExp;
|
|
117
117
|
readonly connectionStringName: RegExp;
|
|
118
|
-
/**
|
|
119
|
-
|
|
118
|
+
/** A credential word that is a whole `_` segment of a name (every position but `embedded`; a counted plural no count
|
|
119
|
+
* word touches), as a regex source. */
|
|
120
|
+
readonly segmentWord: string;
|
|
120
121
|
readonly tokenPrefix: RegExp;
|
|
121
122
|
}
|
|
122
123
|
export declare function envNameView(vocabulary: CredentialVocabulary): EnvNameView;
|
|
@@ -8,8 +8,10 @@ const PLAIN_QUOTES = [`"`, `'`, `"`, `'`, `"`, `'`, `%22`, `%2
|
|
|
8
8
|
const ESCAPED_QUOTES = [`"`, `'`, `x22`, `x27`, `u0022`, `u0027`, `042`, `047`];
|
|
9
9
|
const BACKSLASHES = String.raw `\\{1,${ESCAPE_DEPTH}}`;
|
|
10
10
|
const QUOTE = `(?:${PLAIN_QUOTES.join("|")}|${BACKSLASHES}(?:${ESCAPED_QUOTES.join("|")}))`;
|
|
11
|
+
const WORD_START = `(?:(?<![A-Za-z0-9])|(?<=${QUOTE}))`;
|
|
11
12
|
const AFTER_CLOSING_QUOTE = String.raw `(?:[\s,}\]:]|["']|$)`;
|
|
12
|
-
const
|
|
13
|
+
const VALUE_RUN_END = `(?:(?<!\\\\)|(?!\\\\{0,${ESCAPE_DEPTH}}["']))`;
|
|
14
|
+
const GLUED_RUN = `${VALUE_CHAR}*${VALUE_RUN_END}`;
|
|
13
15
|
const pairedValue = (open, close) => `${open}(?!${AFTER_CLOSING_QUOTE})[^\\n]{0,${QUOTED_VALUE_MAX}}?${close}`;
|
|
14
16
|
const QUOTED_VALUE = [
|
|
15
17
|
...PLAIN_QUOTES.map((q) => pairedValue(q, q)),
|
|
@@ -17,29 +19,35 @@ const QUOTED_VALUE = [
|
|
|
17
19
|
`${QUOTE}(?!${AFTER_CLOSING_QUOTE})${GLUED_RUN}`,
|
|
18
20
|
].join("|");
|
|
19
21
|
const BEARER_TAIL = `(?:(?!${QUOTE})\\S${GLUED_RUN}|(?<==)(?:${QUOTED_VALUE}))`;
|
|
22
|
+
const HEADER_PARAM = String.raw `=[ \t]*(\\{0,${ESCAPE_DEPTH}})(["'])(?:(?!(?<!\\)\2\3)[^\r\n])*(?:(?<!\\)\2\3(?=[\s,;]|\\{0,${ESCAPE_DEPTH}}["']|$)|(?=[\r\n]|$))`;
|
|
23
|
+
const HEADER_VALUE = String.raw `(?:[A-Za-z][A-Za-z0-9!#$%&*+.^_|~-]*[ \t]+)?(?:${HEADER_PARAM}|[^\s"',]|[ \t]*,[ \t]*(?=[^\s"',])|[ \t]+(?==)|(?<==)[ \t]+(?=[^\s"',]))+${VALUE_RUN_END}`;
|
|
24
|
+
const headed = (body, flags = "g") => new RegExp(`${WORD_START}${body}`, flags);
|
|
20
25
|
export const CREDENTIAL_VOCABULARY = {
|
|
21
26
|
values: [
|
|
22
|
-
{ id: "github-token", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match:
|
|
23
|
-
{ id: "github-fine-grained-pat", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match:
|
|
24
|
-
{ id: "openai-api-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match:
|
|
25
|
-
{ id: "stripe-secret-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match:
|
|
26
|
-
{ id: "stripe-publishable-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match:
|
|
27
|
-
{ id: "gitlab-pat", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match:
|
|
28
|
-
{ id: "
|
|
29
|
-
{ id: "
|
|
30
|
-
{ id: "
|
|
31
|
-
{ id: "
|
|
32
|
-
{ id: "
|
|
27
|
+
{ id: "github-token", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed(`gh[opsur][-_]${VENDOR_BODY}`), strict: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/ },
|
|
28
|
+
{ id: "github-fine-grained-pat", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed(`github_pat[-_]${VENDOR_BODY}`), strict: /\bgithub_pat_[A-Za-z0-9_]{60,}\b/ },
|
|
29
|
+
{ id: "openai-api-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed(`sk-${VENDOR_BODY}`), strict: /\bsk-(?:ant-(?:api03-)?|proj-)?[A-Za-z0-9_]{20,}\b/ },
|
|
30
|
+
{ id: "stripe-secret-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed(`(?:sk_|rk[-_])${VENDOR_BODY}`), strict: /\b[sr]k_(?:live|test)_[A-Za-z0-9]{24,}\b/ },
|
|
31
|
+
{ id: "stripe-publishable-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed(`pk[-_]${VENDOR_BODY}`) },
|
|
32
|
+
{ id: "gitlab-pat", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed("glpat-[A-Za-z0-9_-]{20,}") },
|
|
33
|
+
{ id: "npm-access-token", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed("npm_[A-Za-z0-9]{36,}") },
|
|
34
|
+
{ id: "google-oauth-access-token", kind: "prefixed-token", source: "arg-summary", confidence: "high", pass: "vendor-prefixed", match: headed(String.raw `ya29\.[A-Za-z0-9_.-]{20,}`) },
|
|
35
|
+
{ id: "google-api-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", match: headed("AIza[A-Za-z0-9_-]{20,}"), strict: /\bAIza[0-9A-Za-z_-]{35}\b/ },
|
|
36
|
+
{ id: "slack-token", kind: "prefixed-token", source: "arg-summary", confidence: "high", match: headed("xox[baprs]-[A-Za-z0-9-]{10,}"), strict: /\bxox[baprs]-(?:[A-Za-z0-9]+-){1,3}[A-Za-z0-9]{16,}\b/ },
|
|
37
|
+
{ id: "aws-access-key-id", kind: "prefixed-token", source: "arg-summary", confidence: "high", match: headed("(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{12,}"), strict: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
38
|
+
{ id: "huggingface-token", kind: "prefixed-token", source: "arg-summary", confidence: "high", match: headed("hf_[A-Za-z0-9]{20,}"), strict: /\bhf_(?=[A-Za-z0-9]*[0-9])[A-Za-z0-9]{20,}\b/ },
|
|
39
|
+
{ id: "jwt", kind: "jwt", source: "arg-summary", confidence: "high", match: headed("eyJ[A-Za-z0-9._-]{20,}"), strict: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]{16,}\b/ },
|
|
33
40
|
{ id: "private-key", kind: "private-key-block", source: "arg-summary", confidence: "high", match: /-----BEGIN[ A-Z0-9]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END[ A-Z0-9]*PRIVATE KEY(?: BLOCK)?-----|$)/g, strict: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/ },
|
|
34
|
-
{ id: "bearer-token", kind: "bearer-token", source: "arg-summary", confidence: "high", match:
|
|
41
|
+
{ id: "bearer-token", kind: "bearer-token", source: "arg-summary", confidence: "high", match: headed(String.raw `()[Bb][Ee][Aa][Rr][Ee][Rr]\s+[A-Za-z0-9._~+/-]{8,}=*(?:${BEARER_TAIL})?`), strict: /\bAuthorization:\s*Bearer\s+(?=[A-Za-z0-9._~+/-]*[0-9])[A-Za-z0-9._~+/-]{20,}=*/ },
|
|
42
|
+
{ id: "authorization-header", kind: "authorization-header", source: "arg-summary", confidence: "high", match: headed(String.raw `(authorization(?:\\{0,${ESCAPE_DEPTH}}["'])?[ \t]*[=:][ \t]*(?:\\{0,${ESCAPE_DEPTH}}["'])?)${HEADER_VALUE}`, "gi") },
|
|
35
43
|
{ id: "azure-storage-account-key", kind: "prefixed-token", source: "arg-summary", confidence: "high", match: /(AccountKey=)[A-Za-z0-9+/]{40,}={0,2}/g, strict: /AccountKey=[A-Za-z0-9+/]{40,}={0,2}/ },
|
|
36
|
-
{ id: "keyword-value", kind: "keyword-value", source: "arg-summary", confidence: "medium", match:
|
|
44
|
+
{ id: "keyword-value", kind: "keyword-value", source: "arg-summary", confidence: "medium", match: headed(String.raw `((?:token|secret|key|password|passwd|credential)["']?\s*[=:]\s*["']?)${VALUE_CHAR}{4,}${VALUE_RUN_END}`, "gi") },
|
|
37
45
|
{ id: "url-userinfo", kind: "url-credentials", source: "untrusted-egress", confidence: "medium", match: new RegExp(String.raw `(${URI_SCHEME_SOURCE}:\/\/)((?:[^/\s"'@]*@)+)`, "gi") },
|
|
38
46
|
{ id: "signed-url-param", kind: "signed-url-param", source: "untrusted-egress", confidence: "high", match: /([?&#](?:sig|signature|sas|code)=)[^&\s"'#]+/gi },
|
|
39
47
|
],
|
|
40
48
|
words: [
|
|
41
49
|
...["TOKEN", "SECRET", "PASSWORD", "PASSWD", "PASSPHRASE", "KEY"].map((word) => ({ word, position: "leading" })),
|
|
42
|
-
{ word: "AUTH", position: "leading", recordKeys: ["AUTHORIZATION"] },
|
|
50
|
+
{ word: "AUTH", position: "leading", recordKeys: ["AUTHORIZATION", "AUTHENTICATION", "OAUTH", "AUTHN", "AUTHZ"] },
|
|
43
51
|
...["COOKIE", "PAT", "DSN", "WEBHOOK", "CREDENTIAL", "CREDENTIALS", "CREDS", "APIKEY", "ACCESSKEY", "SECRETKEY", "ACCOUNTKEY", "PRIVATEKEY", "AUTHKEY", "SSHKEY", "SIGNINGKEY", "MASTERKEY", "DEPLOYKEY", "ENCRYPTIONKEY", "PGPASSWORD", "SSHPASS"].map((word) => ({ word, position: "leading" })),
|
|
44
52
|
...["KEY", "SECRET", "PASSWORD", "CREDENTIAL"].map((word) => ({ word, position: "plural" })),
|
|
45
53
|
{ word: "TOKEN", position: "counted-plural" },
|
|
@@ -109,10 +117,11 @@ function credentialWordPattern(countWords, rows) {
|
|
|
109
117
|
...(separated.length > 0 ? [`_(${separated.join("|")})`] : []),
|
|
110
118
|
...(embedded.length > 0 ? [`(${embedded.join("|")})`] : []),
|
|
111
119
|
];
|
|
112
|
-
|
|
120
|
+
const segmentWord = `(?:${[...at("leading"), ...at("plural").map((w) => `${w}S`), ...separated, countedPlural].join("|")})`;
|
|
121
|
+
return { credentialWord: new RegExp(`(${positions.join("|")})(?=$|[_0-9])`, "i"), segmentWord };
|
|
113
122
|
}
|
|
114
123
|
export function envNameView(vocabulary) {
|
|
115
|
-
const { credentialWord,
|
|
124
|
+
const { credentialWord, segmentWord } = credentialWordPattern(vocabulary.countWords, vocabulary.words);
|
|
116
125
|
return {
|
|
117
126
|
exactNames: new Set(vocabulary.exactEnvNames.flatMap((name) => [name, `INPUT_${name}`])),
|
|
118
127
|
nonSecretExactNames: new Set(vocabulary.nonSecretExactNames),
|
|
@@ -120,7 +129,7 @@ export function envNameView(vocabulary) {
|
|
|
120
129
|
nonCredentialPrefix: vocabulary.nonCredentialPrefix,
|
|
121
130
|
credentialWord,
|
|
122
131
|
connectionStringName: vocabulary.connectionStringName,
|
|
123
|
-
|
|
132
|
+
segmentWord,
|
|
124
133
|
tokenPrefix: vocabulary.tokenPrefix,
|
|
125
134
|
};
|
|
126
135
|
}
|