@sema-agent/core 7.52.0 → 7.54.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 +470 -0
- package/LICENSE +3 -1
- package/README.md +3 -1
- package/dist/agents/cross-session-envelope.d.ts +8 -0
- package/dist/agents/cross-session-envelope.js +6 -0
- package/dist/agents/cross-session-judge.d.ts +21 -1
- package/dist/agents/cross-session-judge.js +8 -0
- package/dist/agents/cross-session-ref.d.ts +5 -0
- package/dist/agents/cross-session-ref.js +1 -0
- package/dist/agents/org-delivery-gate.d.ts +122 -0
- package/dist/agents/org-delivery-gate.js +69 -0
- package/dist/agents/peer-held-queue.d.ts +9 -4
- package/dist/agents/peer-held-queue.js +6 -5
- package/dist/agents/peer-session-drain.d.ts +35 -6
- package/dist/agents/peer-session-drain.js +271 -182
- package/dist/agents/read-inbox-tool.d.ts +7 -0
- package/dist/agents/read-inbox-tool.js +5 -1
- package/dist/agents/send-message-tool.js +3 -4
- package/dist/agents/subagent.d.ts +6 -5
- package/dist/agents/subagent.js +117 -83
- package/dist/agents/tool-filter.d.ts +25 -4
- package/dist/agents/tool-filter.js +13 -3
- package/dist/brain/anthropic.d.ts +6 -1
- package/dist/brain/anthropic.js +4 -4
- package/dist/brain/model-presets.js +2 -1
- package/dist/brain/route-adjudicator.d.ts +6 -0
- package/dist/brain/routing.d.ts +24 -1
- package/dist/brain/routing.js +54 -19
- package/dist/brain/timeout.js +5 -4
- package/dist/core/a2a.d.ts +5 -2
- package/dist/core/a2a.js +33 -14
- package/dist/core/allow-entry.d.ts +3 -1
- package/dist/core/allow-entry.js +3 -2
- package/dist/core/approval-object.d.ts +87 -0
- package/dist/core/approval-object.js +85 -0
- package/dist/core/arg-summary.d.ts +3 -2
- package/dist/core/arg-summary.js +17 -11
- package/dist/core/ask-origin.d.ts +43 -27
- package/dist/core/ask-origin.js +20 -11
- package/dist/core/ask-question.d.ts +0 -1
- package/dist/core/ask-question.js +4 -67
- package/dist/core/ask-unresolvable-notice.d.ts +3 -2
- package/dist/core/auto-compaction.d.ts +106 -50
- package/dist/core/auto-compaction.js +69 -53
- package/dist/core/auto-mode-prompt.d.ts +4 -2
- package/dist/core/auto-mode-prompt.js +4 -3
- package/dist/core/canonical-json.d.ts +69 -0
- package/dist/core/canonical-json.js +130 -8
- package/dist/core/checkpoint-requirements.d.ts +13 -7
- package/dist/core/checkpoint-requirements.js +4 -1
- package/dist/core/checkpoint-store.d.ts +60 -36
- package/dist/core/checkpoint-store.js +7 -6
- package/dist/core/child-clamp-seats.d.ts +45 -6
- package/dist/core/child-clamp-seats.js +56 -4
- package/dist/core/compliance.js +3 -1
- package/dist/core/consolidate-scope.d.ts +1 -1
- package/dist/core/cross-boundary-taint.d.ts +213 -0
- package/dist/core/cross-boundary-taint.js +196 -0
- package/dist/core/decision-receipt.d.ts +44 -1
- package/dist/core/decision-receipt.js +16 -0
- package/dist/core/effective-path-target.d.ts +28 -6
- package/dist/core/effective-path-target.js +28 -2
- package/dist/core/engine-notice.d.ts +47 -4
- package/dist/core/engine-notice.js +17 -1
- package/dist/core/engine-storage-backend.d.ts +73 -0
- package/dist/core/engine-storage-backend.js +1 -0
- package/dist/core/file-history-store.d.ts +6 -2
- package/dist/core/file-history-store.js +1 -0
- package/dist/core/gate-fold.js +13 -4
- package/dist/core/gate-lanes.js +41 -34
- package/dist/core/gate-outcome.d.ts +18 -9
- package/dist/core/governance-codes.d.ts +9 -2
- package/dist/core/governance-codes.js +9 -0
- package/dist/core/hands-band-screen.d.ts +7 -0
- package/dist/core/hands-band-screen.js +32 -0
- package/dist/core/hooks.d.ts +21 -19
- package/dist/core/hooks.js +6 -5
- package/dist/core/locked-config.d.ts +110 -31
- package/dist/core/locked-config.js +155 -42
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/mcp-probe.js +1 -1
- package/dist/core/mcp-server-spec.d.ts +152 -17
- package/dist/core/mcp-server-spec.js +223 -40
- package/dist/core/mcp.d.ts +21 -14
- package/dist/core/mcp.js +59 -54
- package/dist/core/memory-engine/engine.d.ts +14 -19
- package/dist/core/memory-engine/engine.js +9 -6
- package/dist/core/memory-engine/export-bundle.d.ts +1 -1
- package/dist/core/memory-engine/export-bundle.js +1 -1
- package/dist/core/memory-engine/file-backend.d.ts +2 -6
- package/dist/core/memory-engine/file-backend.js +1 -12
- package/dist/core/memory.d.ts +3 -3
- package/dist/core/mode-policy.d.ts +16 -26
- package/dist/core/mode-policy.js +5 -18
- package/dist/core/mods/kernel/dispatch.js +13 -15
- package/dist/core/mods/kernel/events.d.ts +9 -6
- package/dist/core/mods/kernel/events.js +2 -2
- package/dist/core/mods/kernel/receive.d.ts +41 -11
- package/dist/core/mods/kernel/receive.js +124 -54
- package/dist/core/mods/kernel/site-rules.d.ts +80 -15
- package/dist/core/mods/kernel/site-rules.js +232 -40
- package/dist/core/mods/stations.d.ts +0 -6
- package/dist/core/mods/stations.js +0 -7
- package/dist/core/mounted-mcp.d.ts +57 -0
- package/dist/core/mounted-mcp.js +71 -2
- package/dist/core/own-property-receipt.d.ts +13 -0
- package/dist/core/own-property-receipt.js +3 -1
- package/dist/core/parent-spec-seat.d.ts +12 -15
- package/dist/core/parent-spec-seat.js +14 -6
- package/dist/core/pause-registry.d.ts +35 -1
- package/dist/core/pause-registry.js +10 -0
- package/dist/core/permission-rule-consent.d.ts +18 -5
- package/dist/core/permission-rule-consent.js +13 -8
- package/dist/core/permission-rule-model.d.ts +87 -15
- package/dist/core/permission-rule-model.js +87 -8
- package/dist/core/permission-rule-org.d.ts +200 -33
- package/dist/core/permission-rule-org.js +217 -91
- package/dist/core/permission-rule-session.js +2 -2
- package/dist/core/permission-rule-store.d.ts +8 -0
- package/dist/core/permission-rule-store.js +1 -0
- package/dist/core/permission-rule-sync.js +3 -2
- package/dist/core/permission-rules.d.ts +7 -1
- package/dist/core/permission-rules.js +37 -8
- package/dist/core/read-only-shell.d.ts +1 -1
- package/dist/core/read-only-shell.js +30 -9
- package/dist/core/remote-env.d.ts +41 -1
- package/dist/core/remote-env.js +18 -0
- package/dist/core/resume-startup-cause.d.ts +18 -0
- package/dist/core/resume-startup-cause.js +12 -0
- package/dist/core/retention.js +7 -2
- package/dist/core/retired-keys.d.ts +8 -2
- package/dist/core/retired-keys.js +12 -1
- package/dist/core/roles.js +60 -10
- package/dist/core/rule-lane-reads.js +1 -1
- package/dist/core/runner/approval-feedback.d.ts +46 -0
- package/dist/core/runner/approval-feedback.js +41 -0
- package/dist/core/runner/compaction-call-options.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.js +2 -2
- package/dist/core/runner/contracts.d.ts +34 -41
- package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
- package/dist/core/runner/cross-boundary-lane.js +190 -0
- package/dist/core/runner/decide-continuation.d.ts +4 -4
- package/dist/core/runner/decide-continuation.js +3 -1
- package/dist/core/runner/gate-exit.d.ts +32 -12
- package/dist/core/runner/gate-exit.js +77 -34
- package/dist/core/runner/inherited-ask-grants.js +2 -2
- package/dist/core/runner/mcp-redial.d.ts +52 -10
- package/dist/core/runner/mcp-redial.js +38 -9
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
- package/dist/core/runner/permission-rule-lanes.js +22 -10
- package/dist/core/runner/person-path-rows.d.ts +5 -3
- package/dist/core/runner/person-path-rows.js +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
- package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
- package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
- package/dist/core/runner/prepare-config-doors.d.ts +58 -15
- package/dist/core/runner/prepare-config-doors.js +167 -146
- package/dist/core/runner/prepare-context-lane.js +6 -3
- package/dist/core/runner/prepare-defer-classify.js +4 -7
- package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
- package/dist/core/runner/prepare-delegation-surface.js +8 -7
- package/dist/core/runner/prepare-execution-env.d.ts +1 -2
- package/dist/core/runner/prepare-execution-env.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
- package/dist/core/runner/prepare-gate-stations.js +4 -3
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -3
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
- package/dist/core/runner/prepare-inherited-gate.js +3 -2
- package/dist/core/runner/prepare-listings.d.ts +1 -1
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
- package/dist/core/runner/prepare-memory-engine-session.js +19 -6
- package/dist/core/runner/prepare-memory.d.ts +3 -2
- package/dist/core/runner/prepare-memory.js +6 -2
- package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
- package/dist/core/runner/prepare-offload-wrappers.js +2 -1
- package/dist/core/runner/prepare-park-ask.d.ts +1 -1
- package/dist/core/runner/prepare-park-ask.js +9 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
- package/dist/core/runner/prepare-policy-chain.js +14 -19
- package/dist/core/runner/prepare-project-context.d.ts +1 -1
- package/dist/core/runner/prepare-project-context.js +6 -4
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-prompt-assembly.js +20 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
- package/dist/core/runner/prepare-prompt-inputs.js +6 -3
- package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
- package/dist/core/runner/prepare-protocol-tools.js +19 -12
- package/dist/core/runner/prepare-question-face.d.ts +4 -2
- package/dist/core/runner/prepare-question-face.js +4 -3
- package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
- package/dist/core/runner/prepare-safety-scan.js +34 -27
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
- package/dist/core/runner/prepare-suspend-saga.js +6 -2
- package/dist/core/runner/prepare-task.js +22 -19
- package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
- package/dist/core/runner/prepare-turn-wiring.js +14 -49
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -5
- package/dist/core/runner/prepare-wiring-manifest.js +9 -6
- package/dist/core/runner/prepare-workspace-restore.js +2 -2
- package/dist/core/runner/resume-admission.d.ts +4 -1
- package/dist/core/runner/resume-admission.js +16 -14
- package/dist/core/runner/resume-apply.js +4 -4
- package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
- package/dist/core/runner/resume-internals-and-config.js +3 -8
- package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
- package/dist/core/runner/resume-policy-outcome.js +18 -15
- package/dist/core/runner/resume-preflight.js +2 -2
- package/dist/core/runner/resume-review-outcome.js +3 -3
- package/dist/core/runner/retired-runner-deps.d.ts +5 -6
- package/dist/core/runner/retired-runner-deps.js +1 -4
- package/dist/core/runner/run-leg.js +97 -43
- package/dist/core/runner/run-recovery-lanes.js +5 -3
- package/dist/core/runner/run-settle-and-teardown.js +2 -0
- package/dist/core/runner/run-terminal-adoption.js +21 -9
- package/dist/core/runner/run-turn-boundary.js +9 -3
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/session-file-state-replay.d.ts +5 -2
- package/dist/core/runner/session-file-state-replay.js +3 -2
- package/dist/core/runner/skills-seat.d.ts +15 -0
- package/dist/core/runner/skills-seat.js +57 -0
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
- package/dist/core/runner/sub-agent-deny-lists.js +7 -5
- package/dist/core/runner/synthetic-tools.d.ts +5 -2
- package/dist/core/runner/synthetic-tools.js +2 -12
- package/dist/core/runner/tool-face-overlay.d.ts +24 -6
- package/dist/core/runner/tool-face-overlay.js +24 -15
- package/dist/core/runner-deps.d.ts +37 -22
- package/dist/core/session-policy-store.d.ts +19 -0
- package/dist/core/session-policy-store.js +22 -2
- package/dist/core/session-store.d.ts +35 -16
- package/dist/core/session-store.js +20 -12
- package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
- package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
- package/dist/core/shell-wrapper-table.d.ts +5 -3
- package/dist/core/shell-wrapper-table.js +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
- package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
- package/dist/core/store-contracts/session-store-contract.js +639 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
- package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
- package/dist/core/task-event.d.ts +44 -6
- package/dist/core/task-notification.js +2 -2
- package/dist/core/task-registry-agent.js +1 -0
- package/dist/core/task-registry-shared.d.ts +5 -0
- package/dist/core/task-registry.d.ts +7 -0
- package/dist/core/task-registry.js +17 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +7 -7
- package/dist/core/tighten-task-spec.d.ts +6 -4
- package/dist/core/tighten-task-spec.js +2 -2
- package/dist/core/tool-catalog-entries.d.ts +1 -1
- package/dist/core/tool-catalog-entries.js +52 -52
- package/dist/core/tool-face-reader.d.ts +49 -0
- package/dist/core/tool-face-reader.js +94 -0
- package/dist/core/tool-face.d.ts +17 -8
- package/dist/core/tool-face.js +1 -0
- package/dist/core/tool-policy.d.ts +67 -23
- package/dist/core/tool-policy.js +72 -60
- package/dist/core/tool-roster.d.ts +46 -28
- package/dist/core/tool-roster.js +91 -14
- package/dist/core/tool-spec-members.js +2 -2
- package/dist/core/tool-spec.d.ts +20 -4
- package/dist/core/tools.js +2 -0
- package/dist/core/trace.d.ts +24 -2
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +23 -0
- package/dist/core/untrusted-text.js +10 -10
- package/dist/core/user-attachment.d.ts +56 -2
- package/dist/core/user-attachment.js +139 -56
- package/dist/core/wiring-manifest.d.ts +4 -2
- package/dist/engine/compaction/compaction.d.ts +2 -2
- package/dist/engine/compaction/utils.d.ts +2 -2
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +10 -4
- package/dist/engine/llm/types.d.ts +7 -2
- package/dist/engine/llm/types.js +2 -1
- package/dist/engine/loop/agent-loop.d.ts +12 -1
- package/dist/engine/loop/agent-loop.js +17 -5
- package/dist/engine/loop/types.d.ts +7 -1
- package/dist/engine/session/repo-utils.d.ts +5 -0
- package/dist/engine/session/repo-utils.js +9 -0
- package/dist/engine/session/storage-base.js +2 -2
- package/dist/index.d.ts +37 -17
- package/dist/index.js +31 -14
- package/dist/internal/harness.d.ts +1 -0
- package/dist/internal/harness.js +1 -0
- package/dist/internal/llm.d.ts +1 -1
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/goal.d.ts +24 -1
- package/dist/orchestration/goal.js +3 -2
- package/dist/orchestration/run-workflow-tool.d.ts +3 -0
- package/dist/orchestration/run-workflow-tool.js +1 -0
- package/dist/orchestration/workflow-governance.d.ts +1 -1
- package/dist/orchestration/workflow.d.ts +3 -0
- package/dist/orchestration/workflow.js +2 -1
- package/dist/prompt-assembly/artifact-store.d.ts +44 -9
- package/dist/prompt-assembly/artifact-store.js +38 -12
- package/dist/prompt-assembly/artifact.d.ts +12 -11
- package/dist/prompt-assembly/artifact.js +52 -21
- package/dist/prompt-assembly/assemble.d.ts +2 -0
- package/dist/prompt-assembly/assemble.js +2 -1
- package/dist/prompt-assembly/catalog/index.js +2 -1
- package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
- package/dist/prompt-assembly/catalog/memory.js +6 -0
- package/dist/prompt-assembly/catalog/schema.d.ts +19 -2
- package/dist/prompt-assembly/catalog/schema.js +3 -0
- package/dist/prompt-assembly/composer.d.ts +2 -0
- package/dist/prompt-assembly/composer.js +1 -1
- package/dist/prompt-assembly/overlay.d.ts +21 -11
- package/dist/prompt-assembly/overlay.js +57 -19
- package/dist/prompt-assembly/select.d.ts +21 -6
- package/dist/prompt-assembly/select.js +41 -4
- package/dist/prompts/default.d.ts +1 -12
- package/dist/prompts/default.js +0 -3
- package/dist/shell/lex.d.ts +87 -8
- package/dist/shell/lex.js +283 -49
- package/dist/stores/file/index.d.ts +8 -6
- package/dist/stores/file/permission-rule-store.d.ts +1 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/tools/artifact/artifact-tool.js +14 -3
- package/dist/tools/fs/bash-program-position.d.ts +3 -15
- package/dist/tools/fs/bash-program-position.js +3 -10
- package/dist/tools/fs/bash-readonly-classifier.js +2 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-bash.js +72 -101
- package/dist/tools/fs/fs-read.d.ts +12 -0
- package/dist/tools/fs/fs-read.js +10 -9
- package/dist/tools/fs/fs-shared.d.ts +27 -31
- package/dist/tools/fs/fs-shared.js +12 -15
- package/dist/tools/fs/fs-write.js +1 -1
- package/dist/tools/fs/index.js +2 -2
- package/dist/tools/fs/read-deny-home.d.ts +1 -1
- package/dist/tools/fs/read-deny-home.js +5 -4
- package/dist/tools/fs/read-deny.d.ts +16 -3
- package/dist/tools/fs/read-deny.js +12 -9
- package/dist/tools/fs/safety.d.ts +7 -6
- package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
- package/dist/tools/fs/shell-read-bindings.js +15 -16
- package/dist/tools/fs/shell-read-device.js +1 -1
- package/dist/tools/fs/shell-read-plan.js +36 -47
- package/dist/tools/fs/shell-read-word.d.ts +1 -6
- package/dist/tools/fs/shell-read-word.js +2 -20
- package/dist/tools/fs/shell-reentry-text.js +1 -1
- package/dist/tools/web.d.ts +2 -2
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +5651 -5457
- package/wire-changes/core@7.53.0.json +1968 -0
- package/wire-changes/core@7.54.0.json +2870 -0
- package/dist/core/shell-scan.d.ts +0 -60
- package/dist/core/shell-scan.js +0 -183
|
@@ -8,17 +8,20 @@ import { formatHookFeedback } from "../hooks.js";
|
|
|
8
8
|
import { classicEventBase, failedHandlerClause, screenUserPrompt } from "../mods/stations.js";
|
|
9
9
|
import { buildAppliedHumanInputEvent, buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
|
|
10
10
|
import { humanInputAttachmentsOf, readObjectiveAttachments, userAttachmentDeclarations } from "../user-attachment.js";
|
|
11
|
-
import { uuidv7 } from "../../internal/harness.js";
|
|
11
|
+
import { readToolCallArguments, uuidv7 } from "../../internal/harness.js";
|
|
12
12
|
import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
13
13
|
import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
|
|
14
|
-
import {
|
|
14
|
+
import { argsReadBy, stampArgsReading } from "../effective-path-target.js";
|
|
15
|
+
import { skillRowRefusal } from "../skill-grant.js";
|
|
16
|
+
import { judgeApprovalObject } from "../approval-object.js";
|
|
15
17
|
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
16
18
|
import { computeCostMicroUsd } from "../pricing.js";
|
|
17
19
|
import { mintSystemReminder } from "../reminder-mint.js";
|
|
18
20
|
import { hasDestroy } from "../remote-env.js";
|
|
19
|
-
import { advertisedWritableRootsSeat, attestationOf, exemptRootsSeat, humanRefusalSentence, policyCannotAnswer, policyRefusalMessage, refuseOutOfContractDecision } from "../tool-policy.js";
|
|
21
|
+
import { advertisedWritableRootsSeat, attestationOf, exemptRootsSeat, humanRefusalSentence, licenseCovers, ownedReading, policyCannotAnswer, policyRefusalMessage, refuseOutOfContractDecision } from "../tool-policy.js";
|
|
22
|
+
import { failedJudgmentDeny } from "./policy-throw.js";
|
|
20
23
|
import { pathDenialCardOf } from "../../tools/fs/safety.js";
|
|
21
|
-
import { callFaceSeat,
|
|
24
|
+
import { callFaceSeat, parkedRowLiveFace, rosterEntryNamed } from "../tool-roster.js";
|
|
22
25
|
import { deliverEngineNotice } from "../types.js";
|
|
23
26
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
24
27
|
import { errorCodeOf } from "./terminal-account.js";
|
|
@@ -26,6 +29,7 @@ import { DEFAULT_PRECALL_OUTPUT_TOKENS, platformLimitTerminal } from "./clock-an
|
|
|
26
29
|
import { legContextOverlayOption } from "./compaction-call-options.js";
|
|
27
30
|
import { mintOutputStyleReminder, owedPostureText } from "./compaction-restate.js";
|
|
28
31
|
import { deepJsonEqual, resumeContinuation } from "./decide-continuation.js";
|
|
32
|
+
import { approvalWordsOf, owedApprovalWords, withApprovalWords } from "./approval-feedback.js";
|
|
29
33
|
import { chainCannotJudge } from "./call-hook-stations.js";
|
|
30
34
|
import { settleExecutionRecord } from "./execution-record.js";
|
|
31
35
|
import { holdPrompt } from "./prompt-block.js";
|
|
@@ -62,6 +66,10 @@ export async function runLeg(input) {
|
|
|
62
66
|
}
|
|
63
67
|
if (resume) {
|
|
64
68
|
const walltimeExhaustedResume = effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0;
|
|
69
|
+
const approvalWords = approvalWordsOf(resume.outcome);
|
|
70
|
+
const approvalFrames = owedApprovalWords(approvalWords, uuidv7());
|
|
71
|
+
let approvalCommitted = false;
|
|
72
|
+
prepared.undeliveredContinuationRef.current.parkedSteers.push(...approvalFrames);
|
|
65
73
|
const resumeWindowRetryAfterMs = !walltimeExhaustedResume && prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
|
|
66
74
|
if (resumeWindowRetryAfterMs !== undefined) {
|
|
67
75
|
const owesNothing = resume.outcome.gate === "resource_limit" &&
|
|
@@ -234,13 +242,15 @@ export async function runLeg(input) {
|
|
|
234
242
|
...ident(),
|
|
235
243
|
});
|
|
236
244
|
}
|
|
245
|
+
if (approvalWords !== undefined)
|
|
246
|
+
continuation = withApprovalWords(continuation, engineSegments, approvalWords);
|
|
237
247
|
const pending = resume.cp.pendingAction;
|
|
238
248
|
const rejectionHalt = pending.kind === "tool_approval" && resume.outcome.gate === "policy_ask"
|
|
239
249
|
? maybeHumanRejectionHalt({ gate: resume.outcome.gateOutcome, isDelegatedChild: prepared.hookIdentity.isDelegatedChild, toolName: pending.toolName, toolCallId: pending.toolCallId })
|
|
240
250
|
: undefined;
|
|
241
251
|
if (rejectionHalt !== undefined && pending.kind === "tool_approval")
|
|
242
252
|
prepared.batchHaltRef.current = { rejectedToolCallId: pending.toolCallId };
|
|
243
|
-
prepared.undeliveredContinuationRef.current = { parkedSteers: resumeFrames.map(({ entry }) => entry).filter((e) => !withheldSteerIds.has(e.inputId)), orphanTasks };
|
|
253
|
+
prepared.undeliveredContinuationRef.current = { parkedSteers: [...resumeFrames.map(({ entry }) => entry).filter((e) => !withheldSteerIds.has(e.inputId)), ...approvalFrames], orphanTasks };
|
|
244
254
|
if (!prepared.abortController.signal.aborted) {
|
|
245
255
|
if (resume !== undefined)
|
|
246
256
|
resume.decisionDelivered = true;
|
|
@@ -262,11 +272,22 @@ export async function runLeg(input) {
|
|
|
262
272
|
const preview = redactThenCut(prepared.outputStyleFrame, 220);
|
|
263
273
|
prepared.frameReceipts.pending.push({ source: "output_style", text: continuation, commit: () => void (queue.push({ type: "steering_injected", source: "output_style", preview, ...ident() }), (rs.attach.attachmentsInjected += 1)) });
|
|
264
274
|
}
|
|
275
|
+
if (approvalFrames.length > 0) {
|
|
276
|
+
prepared.frameReceipts.pending.push({
|
|
277
|
+
source: "approval_feedback",
|
|
278
|
+
text: continuation,
|
|
279
|
+
commit: () => {
|
|
280
|
+
approvalCommitted = true;
|
|
281
|
+
const seat = prepared.undeliveredContinuationRef.current;
|
|
282
|
+
seat.parkedSteers = seat.parkedSteers.filter((f) => !approvalFrames.includes(f));
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
}
|
|
265
286
|
final = await withBrainSinks(() => prepared.harness.prompt(continuation, {
|
|
266
287
|
engineSegments,
|
|
267
288
|
settleUnansweredToolCalls: rejectionHalt !== undefined ? { halt: rejectionHalt } : {},
|
|
268
289
|
onDelivered: () => {
|
|
269
|
-
prepared.undeliveredContinuationRef.current = { parkedSteers: [], orphanTasks: [] };
|
|
290
|
+
prepared.undeliveredContinuationRef.current = { parkedSteers: approvalCommitted ? [] : [...approvalFrames], orphanTasks: [] };
|
|
270
291
|
for (const e of appliedEvents)
|
|
271
292
|
queue.push(e);
|
|
272
293
|
},
|
|
@@ -529,19 +550,6 @@ async function applyResumeDecision(runner, prepared, resume, emit, emitCommitted
|
|
|
529
550
|
return;
|
|
530
551
|
if (pendingAction.kind !== "tool_approval")
|
|
531
552
|
return;
|
|
532
|
-
if (resume.outcome.gate === "policy_ask" &&
|
|
533
|
-
resume.outcome.decision === "allow" &&
|
|
534
|
-
!prepared.tools.some((t) => t.name === pendingAction.toolName)) {
|
|
535
|
-
const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume — refusing to continue as if it ran; the checkpoint is reopened for a retry with the tool present`);
|
|
536
|
-
e.code = "resume.tool_unavailable";
|
|
537
|
-
throw e;
|
|
538
|
-
}
|
|
539
|
-
const liveRoster = prepared.toolRosterDeltas.current;
|
|
540
|
-
if (pendingAction.kind === "tool_approval" && liveRoster !== undefined && judgeParkedToolIdentity(pendingAction, rosterEntryNamed(liveRoster, pendingAction.toolName)) === "mismatch") {
|
|
541
|
-
const e = new Error(`the approved tool "${pendingAction.toolName}" on this leg is not the tool the approval was minted against (its contract / shape / revision differ) — refusing to execute a replacement wearing the name; the checkpoint is reopened for a retry with the original tool present`);
|
|
542
|
-
e.code = "resume.tool_contract_mismatch";
|
|
543
|
-
throw e;
|
|
544
|
-
}
|
|
545
553
|
await resolvePendingCall(runner, prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart);
|
|
546
554
|
}
|
|
547
555
|
async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
|
|
@@ -549,13 +557,29 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
549
557
|
const { outcome } = resume;
|
|
550
558
|
if (pendingAction.kind !== "tool_approval" || outcome.gate !== "policy_ask")
|
|
551
559
|
return;
|
|
560
|
+
const mounted = () => { const tool = prepared.tools.find((x) => x.name === pendingAction.toolName); const roster = prepared.toolRosterDeltas.current; return tool === undefined || roster === undefined ? {} : { tool, entry: rosterEntryNamed(roster, pendingAction.toolName) }; };
|
|
561
|
+
const reopen = (miss) => { throw Object.assign(new Error(miss.message), { code: miss.code }); };
|
|
562
|
+
const liveTool = mounted().tool;
|
|
563
|
+
const atSession = outcome.decision === "allow" ? judgeApprovalObject(pendingAction, { tool: mounted().entry }, "session") : "match";
|
|
564
|
+
if (atSession !== "match" && atSession.disposition === "reopen")
|
|
565
|
+
reopen(atSession);
|
|
552
566
|
const decidedGate = outcome.gateOutcome;
|
|
553
567
|
if (decidedGate === undefined) {
|
|
554
568
|
throw new CheckpointError("checkpoint.invalid_outcome", "pending action reached the resolver with no settlement record on its decision (gateOutcome) — refusing to execute or settle a decision that carries no record");
|
|
555
569
|
}
|
|
556
570
|
const humanRewrite = wireOf("resume.questionArgsShown", pendingAction) === ASK_USER_QUESTION_TOOL_NAME ? undefined : outcome.updatedInput;
|
|
557
|
-
const
|
|
558
|
-
const
|
|
571
|
+
const reading = liveTool === undefined ? undefined : ownedReading((a) => readToolCallArguments(liveTool, { type: "toolCall", id: pendingAction.toolCallId, name: pendingAction.toolName, arguments: {} }, a));
|
|
572
|
+
const beltFace = () => { const f = parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction); if (f !== undefined && reading !== undefined)
|
|
573
|
+
stampArgsReading(f, reading); return f; };
|
|
574
|
+
const tryRead = (v) => { try {
|
|
575
|
+
return { args: reading === undefined ? v : argsReadBy(reading, v) };
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
return { args: v, unread: { error } };
|
|
579
|
+
} };
|
|
580
|
+
const decided = outcome.decision === "allow" ? tryRead(humanRewrite !== undefined ? humanRewrite : pendingAction.args) : { args: humanRewrite !== undefined ? humanRewrite : pendingAction.args };
|
|
581
|
+
const resolvedArgs = decided.args;
|
|
582
|
+
const pendingLabel = (() => { const l = liveTool?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
|
|
559
583
|
const spelledName = pendingAction.names?.[0] ?? pendingAction.toolName;
|
|
560
584
|
const occurrences = prepared.toolOccurrences;
|
|
561
585
|
const callStart = { toolCallId: pendingAction.toolCallId, spelledName, wireName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs };
|
|
@@ -563,15 +587,26 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
563
587
|
occurrences.gateRound(pendingAction.toolCallId, decidedGate, frozenInputSnapshot(resolvedArgs));
|
|
564
588
|
let notExecuted = false;
|
|
565
589
|
const resultRow = (text, isError) => withRowNotExecuted(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, text, isError), notExecuted);
|
|
566
|
-
const settleEnd = async (isError, result, refusedBy) => {
|
|
590
|
+
const settleEnd = async (isError, result, refusedBy, neverRan) => {
|
|
567
591
|
if (refusedBy !== undefined)
|
|
568
592
|
occurrences.refuse(pendingAction.toolCallId, refusedBy);
|
|
569
|
-
const settled = emitSettled(occurrences, callStart, { result, isError }, { push: emit, structured: prepared.structuredProjector, mediaByteCapBytes: runner.deps.mediaByteCapBytes });
|
|
593
|
+
const settled = emitSettled(occurrences, callStart, { result, isError, ...(neverRan ? { notExecuted: true } : {}) }, { push: emit, structured: prepared.structuredProjector, mediaByteCapBytes: runner.deps.mediaByteCapBytes });
|
|
570
594
|
notExecuted = settled.notExecuted === true;
|
|
571
595
|
const gate = settled.gate ?? decidedGate;
|
|
572
596
|
const word = await resume.recordExecutionOutcome(gate);
|
|
573
597
|
settleExecutionRecord(runner.deps.onNotice, word, { toolName: pendingAction.toolName, gate, sessionId: prepared.sessionId, runId: prepared.runId, scope: resume.cp.scope, ...(resume.cp.checkpointId !== undefined ? { checkpointId: resume.cp.checkpointId } : {}) });
|
|
574
598
|
};
|
|
599
|
+
const settleUnreadable = async (error, which) => {
|
|
600
|
+
const marks = egressValue("same-principal", thrownMarksOf(error));
|
|
601
|
+
const text = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: the tool could not read ${which} — its argument preparation failed (${egressThrownText("same-principal", error)}). This approval is spent — the call has to be re-issued.`, prepared.reminderMark);
|
|
602
|
+
await settleEnd(true, { content: text, ...(marks !== undefined ? { details: marks } : {}) }, undefined, true);
|
|
603
|
+
emitCommitted(await prepared.session.appendMessage(resultRow(text, true)), "toolResult", pendingAction.toolCallId);
|
|
604
|
+
};
|
|
605
|
+
const vetoApproval = async (miss) => {
|
|
606
|
+
const text = formatHookFeedback(miss.message, prepared.reminderMark);
|
|
607
|
+
await settleEnd(true, { content: text, ...(miss.code !== undefined ? { details: { code: miss.code } } : {}) }, "policy");
|
|
608
|
+
emitCommitted(await prepared.session.appendMessage(resultRow(text, true)), "toolResult", pendingAction.toolCallId);
|
|
609
|
+
};
|
|
575
610
|
if (outcome.decision === "deny") {
|
|
576
611
|
const settlementKind = outcome.gateOutcome?.settlement?.kind;
|
|
577
612
|
const reason = settlementKind === "policy_refused"
|
|
@@ -593,6 +628,10 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
593
628
|
if (outcome.decision !== "allow") {
|
|
594
629
|
throw new CheckpointError("checkpoint.invalid_outcome", `pending action reached the resolver with decision "${String(outcome.decision)}" — only "allow" executes and only "deny" injects a denial; refusing to execute`);
|
|
595
630
|
}
|
|
631
|
+
if (atSession !== "match")
|
|
632
|
+
return vetoApproval(atSession);
|
|
633
|
+
if (decided.unread !== undefined)
|
|
634
|
+
return settleUnreadable(decided.unread.error, humanRewrite !== undefined ? "the arguments the decision edited it to" : "its arguments");
|
|
596
635
|
const screened = await prepared.approvedCallScreen?.({ toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, args: structuredClone(resolvedArgs), decidedBy: outcome.hostDecision.decidedBy });
|
|
597
636
|
if (screened !== undefined && "refused" in screened) {
|
|
598
637
|
const hookDenial = formatHookFeedback(screened.refused, prepared.reminderMark);
|
|
@@ -600,10 +639,13 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
600
639
|
emitCommitted(await prepared.session.appendMessage(resultRow(hookDenial, true)), "toolResult", pendingAction.toolCallId);
|
|
601
640
|
return;
|
|
602
641
|
}
|
|
603
|
-
const
|
|
604
|
-
|
|
642
|
+
const rescreened = humanRewrite === undefined && screened !== undefined ? tryRead(screened.args) : undefined;
|
|
643
|
+
if (rescreened?.unread !== undefined)
|
|
644
|
+
return settleUnreadable(rescreened.unread.error, "the arguments a PreToolUse hook rewrote it to");
|
|
645
|
+
const executedArgs = rescreened !== undefined ? rescreened.args : resolvedArgs;
|
|
646
|
+
const unseen = !licenseCovers(pendingAction.boundInputHash, executedArgs);
|
|
605
647
|
let beltReading;
|
|
606
|
-
const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(
|
|
648
|
+
const beltReq = () => ({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(beltFace()), budget: beltBudget, ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}), ...(prepared.handsRoot !== undefined ? { root: prepared.handsRoot } : {}), ...advertisedWritableRootsSeat(prepared.advertisedWritableDirs), ...exemptRootsSeat(prepared.exemptRoots), ...(beltReading !== undefined ? { targetReading: beltReading } : {}) });
|
|
607
649
|
const unjudgeable = unseen ? chainCannotJudge(resume.cp.state.inheritedGate) : undefined;
|
|
608
650
|
if (unjudgeable !== undefined) {
|
|
609
651
|
const chainDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its input changed after the approval and the inherited ancestor chain cannot judge the changed input (${unjudgeable}). This approval is spent — the call has to be re-issued.`, prepared.reminderMark);
|
|
@@ -620,14 +662,20 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
620
662
|
const card = pathDenialCardOf(attestationOf(d, { toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName })?.pathDenial);
|
|
621
663
|
return card !== undefined ? { details: card } : {};
|
|
622
664
|
};
|
|
665
|
+
const beltCheck = async (judge, seat) => { const signal = prepared.abortController.signal; try {
|
|
666
|
+
return await judge(signal);
|
|
667
|
+
}
|
|
668
|
+
catch (err) {
|
|
669
|
+
return failedJudgmentDeny(err, signal, (e) => runner.deps.onError?.(e, { phase: "hook", sessionId: prepared.sessionId }), seat, pendingAction);
|
|
670
|
+
} };
|
|
623
671
|
const beltStamped = prepared.stampCallTarget?.(beltReq(), prepared.abortController.signal);
|
|
624
672
|
if (beltStamped !== undefined)
|
|
625
673
|
beltReading = (await beltStamped).targetReading;
|
|
626
674
|
if (unseen && prepared.basePolicyForResumeEdit) {
|
|
627
|
-
const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check(beltReq(),
|
|
675
|
+
const rechecked = refuseOutOfContractDecision(await beltCheck((signal) => prepared.basePolicyForResumeEdit.check(beltReq(), signal), "the deployment's tool policy re-checking a resumed call"));
|
|
628
676
|
const personUnanswered = policyCannotAnswer(rechecked, outcome.hostDecision.decidedBy);
|
|
629
677
|
const beltUnsafe = rechecked.action === "deny" ||
|
|
630
|
-
(rechecked.updatedInput !== undefined && !deepJsonEqual(
|
|
678
|
+
(rechecked.updatedInput !== undefined && ((read) => read.unread !== undefined || !deepJsonEqual(read.args, executedArgs))(tryRead(rechecked.updatedInput))) ||
|
|
631
679
|
personUnanswered;
|
|
632
680
|
if (beltUnsafe) {
|
|
633
681
|
const why = rechecked.action === "deny"
|
|
@@ -653,7 +701,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
653
701
|
}
|
|
654
702
|
if (prepared.permissionRuleOrg !== undefined) {
|
|
655
703
|
const orgVerdict = prepared.permissionRuleOrg
|
|
656
|
-
.adjudicate({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(
|
|
704
|
+
.adjudicate({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(beltFace()) })
|
|
657
705
|
.catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
|
|
658
706
|
let orgWaitCancelled = false;
|
|
659
707
|
const org = await settleOrgVerdictWithin(orgVerdict, { status: "unavailable", disclosures: [`the org adjudication face did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms (or the task ended first)`] }, {
|
|
@@ -687,7 +735,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
687
735
|
}
|
|
688
736
|
}
|
|
689
737
|
if (prepared.denyNarrowingPolicy) {
|
|
690
|
-
const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check(beltReq(),
|
|
738
|
+
const narrowed = refuseOutOfContractDecision(await beltCheck((signal) => prepared.denyNarrowingPolicy.check(beltReq(), signal), "the narrowing rules re-checking a resumed call"));
|
|
691
739
|
if (narrowed.action === "deny") {
|
|
692
740
|
const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by ${ruleSourcePhrase("narrowing")} and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
|
|
693
741
|
await settleEnd(true, { content: narrowedDenial, ...denialCard(narrowed) }, "policy");
|
|
@@ -729,7 +777,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
729
777
|
}
|
|
730
778
|
}
|
|
731
779
|
if (prepared.shellReadCheck !== undefined) {
|
|
732
|
-
const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(
|
|
780
|
+
const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(beltFace()) };
|
|
733
781
|
const judged = prepared.shellReadCheck.judge(readCall, executedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
|
|
734
782
|
toolCallId: pendingAction.toolCallId,
|
|
735
783
|
...(unseen ? { seat: "rejudge" } : {}),
|
|
@@ -753,18 +801,24 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
753
801
|
reading.verdict.admit?.();
|
|
754
802
|
}
|
|
755
803
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
804
|
+
let tool;
|
|
805
|
+
for (;;) {
|
|
806
|
+
const { targetReading: _belts, ...unread } = beltReq();
|
|
807
|
+
const targetReading = (await prepared.stampCallTarget?.(unread, prepared.abortController.signal))?.targetReading;
|
|
808
|
+
const atExecution = judgeApprovalObject(pendingAction, { tool: mounted().entry, args: executedArgs, skills: prepared.skillSpecs, skillToolName: SKILL_TOOL_NAME, ...(humanRewrite !== undefined ? { decided: resolvedArgs } : {}), targetReading }, "execution");
|
|
809
|
+
if (atExecution !== "match")
|
|
810
|
+
return atExecution.disposition === "reopen" ? reopen(atExecution) : vetoApproval(atExecution);
|
|
811
|
+
tool = mounted().tool;
|
|
812
|
+
const waited = prepared.crossBoundary?.storeWaits() ?? 0;
|
|
813
|
+
const crossBoundaryRefusal = await prepared.crossBoundary?.check?.exit({ toolName: pendingAction.toolName, args: executedArgs, toolCallId: pendingAction.toolCallId }, outcome.hostDecision.decidedBy);
|
|
814
|
+
if (crossBoundaryRefusal !== undefined) {
|
|
815
|
+
const text = formatHookFeedback(crossBoundaryRefusal.message, prepared.reminderMark);
|
|
816
|
+
await settleEnd(true, { content: text }, "policy");
|
|
817
|
+
emitCommitted(await prepared.session.appendMessage(resultRow(text, true)), "toolResult", pendingAction.toolCallId);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if ((prepared.crossBoundary?.storeWaits() ?? 0) === waited)
|
|
821
|
+
break;
|
|
768
822
|
}
|
|
769
823
|
const args = executedArgs;
|
|
770
824
|
screened?.admit();
|
|
@@ -2,7 +2,7 @@ import { compactionKindOf } from "../task-notification.js";
|
|
|
2
2
|
import { planRejectionClears } from "../context-edit.js";
|
|
3
3
|
import { rosterToolName } from "../tool-roster.js";
|
|
4
4
|
import { landStaleResultClears } from "./prepare-context-lane.js";
|
|
5
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent } from "../auto-compaction.js";
|
|
5
|
+
import { compactionPassReport, DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent } from "../auto-compaction.js";
|
|
6
6
|
import { emitTrace } from "../trace.js";
|
|
7
7
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
8
8
|
import { rebaseCadenceWindows } from "./turn-attachments.js";
|
|
@@ -22,6 +22,7 @@ export function runRecoveryLanes(input) {
|
|
|
22
22
|
if (callerStop.signal.aborted)
|
|
23
23
|
return false;
|
|
24
24
|
const passSignal = AbortSignal.any([prepared.abortController.signal, callerStop.signal, ...(turnSignal !== undefined ? [turnSignal] : [])]);
|
|
25
|
+
const passReport = compactionPassReport(queue, ident);
|
|
25
26
|
try {
|
|
26
27
|
const comp = await maybeCompact({
|
|
27
28
|
session: prepared.session,
|
|
@@ -50,6 +51,7 @@ export function runRecoveryLanes(input) {
|
|
|
50
51
|
...compactionRestateOptions(prepared),
|
|
51
52
|
...windowSafetyOptions(prepared.harness.getModel()),
|
|
52
53
|
...compactionHookOptions(runner, prepared, "forced", passSignal),
|
|
54
|
+
onStarted: passReport.onStarted,
|
|
53
55
|
});
|
|
54
56
|
if (comp.compacted) {
|
|
55
57
|
compactionBreaker.failures = 0;
|
|
@@ -95,13 +97,13 @@ export function runRecoveryLanes(input) {
|
|
|
95
97
|
catch (err) {
|
|
96
98
|
if (isCompactionManualCancel(err) || (turnSignal?.aborted === true && !prepared.abortController.signal.aborted)) {
|
|
97
99
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "cancelled", ts: Date.now() }));
|
|
98
|
-
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "forced", reason: "cancelled", ...ident() });
|
|
100
|
+
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "forced", reason: "cancelled", ...passReport.end, ...ident() });
|
|
99
101
|
return false;
|
|
100
102
|
}
|
|
101
103
|
compactionBreaker.failures += 1;
|
|
102
104
|
runner.deps.onError?.(asThrownError(err), { phase: "compaction", sessionId: prepared.sessionId });
|
|
103
105
|
const msg = thrownText(err);
|
|
104
|
-
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
|
|
106
|
+
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...passReport.end, ...ident() });
|
|
105
107
|
return false;
|
|
106
108
|
}
|
|
107
109
|
};
|
|
@@ -282,6 +282,8 @@ export async function runSettleAndTeardown(input) {
|
|
|
282
282
|
const writeDateAnchor = owedDateAnchor(prepared.session, prepared.dateChange, rs.attach.dateState);
|
|
283
283
|
if (writeDateAnchor !== undefined)
|
|
284
284
|
await settleTeardownLeg(writeDateAnchor, "date anchor (settle leg)", (e) => runner.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId }));
|
|
285
|
+
if (prepared.crossBoundary !== undefined)
|
|
286
|
+
await prepared.crossBoundary.settle();
|
|
285
287
|
const ownedEnvDying = prepared.ownedEnv !== undefined &&
|
|
286
288
|
hasDestroy(prepared.ownedEnv) &&
|
|
287
289
|
isIsolated(prepared.ownedEnv) &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact } from "../auto-compaction.js";
|
|
1
|
+
import { compactionPassReport, DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact } from "../auto-compaction.js";
|
|
2
2
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
3
3
|
import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
|
|
4
4
|
import { emitTrace } from "../trace.js";
|
|
@@ -10,6 +10,7 @@ import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions }
|
|
|
10
10
|
import { compactionRestateOptions } from "./compaction-restate.js";
|
|
11
11
|
import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
12
12
|
import { emitSettled, reconciledEnd } from "./tool-end-body.js";
|
|
13
|
+
import { isApprovalWordsFrame } from "./approval-feedback.js";
|
|
13
14
|
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
14
15
|
export async function runTerminalAdoption(input) {
|
|
15
16
|
const { spec, queue, prepared, rs, stats, ident, emitCommitted, startedToolCallIds, toolLabels, timeout, compactionBrain, compactionBreaker, windowSafetyOptions, undrainedUserAtEnd, userInputLostAtEnd, final, abortedLive, userInterruptedLive, callerStop, runner, sessions, next } = input;
|
|
@@ -78,21 +79,28 @@ export async function runTerminalAdoption(input) {
|
|
|
78
79
|
}
|
|
79
80
|
let migratedParked = { steer: 0, followUp: 0 };
|
|
80
81
|
let carriedContinuation = 0;
|
|
82
|
+
let carriedApproval = 0;
|
|
83
|
+
let approvalRefusal;
|
|
81
84
|
if (committedPause !== undefined && runner.deps.checkpointStore !== undefined) {
|
|
82
85
|
const parkToken = committedPause.cause.token;
|
|
83
86
|
const parkScope = committedPause.scope;
|
|
84
87
|
const store = runner.deps.checkpointStore;
|
|
85
88
|
const carried = [];
|
|
86
89
|
const owed = [
|
|
87
|
-
...prepared.undeliveredContinuationRef.current.parkedSteers.map((
|
|
90
|
+
...prepared.undeliveredContinuationRef.current.parkedSteers.map((frame) => {
|
|
91
|
+
const { seq: _seq, ...input } = frame;
|
|
92
|
+
return { input, ...(isApprovalWordsFrame(frame) ? { approval: true } : {}) };
|
|
93
|
+
}),
|
|
88
94
|
...prepared.harness.readParkableUserInputs().map((record) => ({ input: record, live: record })),
|
|
89
95
|
];
|
|
90
|
-
for (const { input, live } of owed) {
|
|
96
|
+
for (const { input, live, approval } of owed) {
|
|
91
97
|
try {
|
|
92
98
|
if (!(await store.setPendingSteer(parkToken, parkScope, input)))
|
|
93
99
|
break;
|
|
94
100
|
if (live !== undefined)
|
|
95
101
|
carried.push(live);
|
|
102
|
+
else if (approval)
|
|
103
|
+
carriedApproval += 1;
|
|
96
104
|
else
|
|
97
105
|
carriedContinuation += 1;
|
|
98
106
|
}
|
|
@@ -102,14 +110,17 @@ export async function runTerminalAdoption(input) {
|
|
|
102
110
|
sessionId: prepared.sessionId,
|
|
103
111
|
});
|
|
104
112
|
const parkCode = errorCodeOf(parkErr);
|
|
105
|
-
if (parkCode === "steering.invalid_content" || parkCode === "steering.queue_full")
|
|
113
|
+
if (parkCode === "steering.invalid_content" || parkCode === "steering.queue_full") {
|
|
114
|
+
if (approval)
|
|
115
|
+
approvalRefusal = parkCode;
|
|
106
116
|
continue;
|
|
117
|
+
}
|
|
107
118
|
break;
|
|
108
119
|
}
|
|
109
120
|
}
|
|
110
121
|
migratedParked = prepared.harness.dropParkedUserInputs(carried);
|
|
111
122
|
}
|
|
112
|
-
const owedUserInput = { held: undrainedUserAtEnd.current, migrated: migratedParked, continuation: { frames: prepared.undeliveredContinuationRef.current.parkedSteers, carried: carriedContinuation } };
|
|
123
|
+
const owedUserInput = { held: undrainedUserAtEnd.current, migrated: migratedParked, continuation: { frames: prepared.undeliveredContinuationRef.current.parkedSteers.filter((f) => !isApprovalWordsFrame(f)), carried: carriedContinuation, approval: { owed: prepared.undeliveredContinuationRef.current.parkedSteers.filter(isApprovalWordsFrame).length, carried: carriedApproval, ...(approvalRefusal !== undefined ? { refusal: approvalRefusal } : {}) } } };
|
|
113
124
|
if (rs.attach.attachState?.postCompactPending === true && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
|
|
114
125
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
115
126
|
}
|
|
@@ -120,7 +131,8 @@ export async function runTerminalAdoption(input) {
|
|
|
120
131
|
minTokens: rs.counters.compactionFloor,
|
|
121
132
|
brain: compactionBrain,
|
|
122
133
|
windowSafety: windowSafetyOptions(prepared.harness.getModel()),
|
|
123
|
-
|
|
134
|
+
passReport: compactionPassReport(queue, ident),
|
|
135
|
+
onCompactionOutcome: (outcome, reason, end) => queue.push({ type: "compaction_outcome", outcome, trigger: "auto", reason, ...end, ...ident() }),
|
|
124
136
|
}, runner, sessions);
|
|
125
137
|
return await next({ threw, comp, answerSettled: settled, owedUserInput });
|
|
126
138
|
}
|
|
@@ -153,7 +165,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
153
165
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
154
166
|
...seamCCompactionOptions(runner, prepared),
|
|
155
167
|
...compactionRestateOptions(prepared),
|
|
156
|
-
...compactionHookOptions(runner, prepared, "auto", opts.callerStop.signal),
|
|
168
|
+
...compactionHookOptions(runner, prepared, "auto", opts.callerStop.signal), onStarted: opts.passReport.onStarted,
|
|
157
169
|
});
|
|
158
170
|
recordCompactionReuse(runner, prepared, finishComp);
|
|
159
171
|
if (finishComp.unevaluableWindow) {
|
|
@@ -170,7 +182,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
170
182
|
reason: "cancelled",
|
|
171
183
|
ts: Date.now(),
|
|
172
184
|
}));
|
|
173
|
-
opts?.onCompactionOutcome?.("mooted", "cancelled");
|
|
185
|
+
opts?.onCompactionOutcome?.("mooted", "cancelled", opts.passReport.end);
|
|
174
186
|
}
|
|
175
187
|
else {
|
|
176
188
|
const failMsg = thrownText(err);
|
|
@@ -183,7 +195,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
183
195
|
reason: failReason,
|
|
184
196
|
ts: Date.now(),
|
|
185
197
|
}));
|
|
186
|
-
opts?.onCompactionOutcome?.("failed", failReason);
|
|
198
|
+
opts?.onCompactionOutcome?.("failed", failReason, opts.passReport.end);
|
|
187
199
|
runner.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
188
200
|
}
|
|
189
201
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { compactionKindOf } from "../task-notification.js";
|
|
2
2
|
import { mintSystemReminder } from "../reminder-mint.js";
|
|
3
3
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
4
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
4
|
+
import { compactionPassReport, DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
5
5
|
import { emitTrace } from "../trace.js";
|
|
6
6
|
import { redactThenCut } from "../../agents/subagent-steps.js";
|
|
7
7
|
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, TOTAL_TOKENS_REMINDER_DEFAULT_MODE, attachmentEnvelopeTags, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, isIntactSource, rebaseCadenceWindows, renderAgentListingFrame, renderMcpDroppedTools, renderMcpInstructionsDelta, selectMcpDroppedBatch, renderSkillsListingFrame, renderToolsDelta } from "./turn-attachments.js";
|
|
@@ -558,6 +558,7 @@ export function createTurnBoundary(input) {
|
|
|
558
558
|
const passTrigger = trimPressure ? "forced" : forceManual ? "manual" : "auto";
|
|
559
559
|
let manualOutcome = "failed";
|
|
560
560
|
const manualInstructions = claimedManual?.instructions;
|
|
561
|
+
const passReport = compactionPassReport(queue, ident);
|
|
561
562
|
try {
|
|
562
563
|
const comp = await maybeCompact({
|
|
563
564
|
session: prepared.session,
|
|
@@ -587,6 +588,7 @@ export function createTurnBoundary(input) {
|
|
|
587
588
|
...compactionRestateOptions(prepared),
|
|
588
589
|
...windowSafetyOptions(event.model),
|
|
589
590
|
...compactionHookOptions(runner, prepared, passTrigger, compactionPassSignal),
|
|
591
|
+
onStarted: passReport.onStarted,
|
|
590
592
|
});
|
|
591
593
|
if (comp.blocked) {
|
|
592
594
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
@@ -670,7 +672,7 @@ export function createTurnBoundary(input) {
|
|
|
670
672
|
catch (err) {
|
|
671
673
|
if (isCompactionManualCancel(err)) {
|
|
672
674
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "cancelled", ts: Date.now() }));
|
|
673
|
-
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: passTrigger, reason: "cancelled", ...ident() });
|
|
675
|
+
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: passTrigger, reason: "cancelled", ...passReport.end, ...ident() });
|
|
674
676
|
manualOutcome = "mooted";
|
|
675
677
|
}
|
|
676
678
|
else if (!prepared.abortController.signal.aborted) {
|
|
@@ -686,11 +688,15 @@ export function createTurnBoundary(input) {
|
|
|
686
688
|
reason: failReason,
|
|
687
689
|
ts: Date.now(),
|
|
688
690
|
}));
|
|
689
|
-
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: passTrigger, reason: failReason, ...ident() });
|
|
691
|
+
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: passTrigger, reason: failReason, ...passReport.end, ...ident() });
|
|
690
692
|
manualOutcome = "failed";
|
|
691
693
|
}
|
|
692
694
|
else {
|
|
693
695
|
manualOutcome = "mooted";
|
|
696
|
+
if (passReport.pass !== undefined) {
|
|
697
|
+
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "task_ending", ts: Date.now() }));
|
|
698
|
+
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: passTrigger, reason: "task_ending", ...passReport.end, ...ident() });
|
|
699
|
+
}
|
|
694
700
|
}
|
|
695
701
|
}
|
|
696
702
|
if (forceManual)
|
|
@@ -481,7 +481,7 @@ export class Runner {
|
|
|
481
481
|
outcome,
|
|
482
482
|
taskConfig,
|
|
483
483
|
internals,
|
|
484
|
-
runner: this.depsSeat,
|
|
484
|
+
runner: this.depsSeat, sessions: this.sessions,
|
|
485
485
|
parentConstraintRegistry: this.parentConstraintRegistry,
|
|
486
486
|
next: (admitted) => this.resumeLadder(token, outcome, admitted),
|
|
487
487
|
});
|
|
@@ -41,10 +41,13 @@ export type TranscriptFileRecord = {
|
|
|
41
41
|
content: string;
|
|
42
42
|
/** The recording message's own timestamp — the most honest "last read at" available. */
|
|
43
43
|
at: number;
|
|
44
|
+
/** A Read result's call id — the transmission that carried the text (a Write's or an Edit's post-condition: none). */ transmittedByToolCallId?: string;
|
|
44
45
|
} | {
|
|
45
46
|
path: string;
|
|
46
|
-
/** The entry the delivery committed. */
|
|
47
|
-
observation: UserAttachmentObservation["entry"]
|
|
47
|
+
/** The entry the delivery committed, stamped with the frame that carried it (a trim that drops the frame retires it). */
|
|
48
|
+
observation: UserAttachmentObservation["entry"] & {
|
|
49
|
+
transmittedByToolCallId: string;
|
|
50
|
+
};
|
|
48
51
|
at: number;
|
|
49
52
|
};
|
|
50
53
|
/**
|
|
@@ -3,6 +3,7 @@ import { isAbsoluteForFamily, pathFamilyOf } from "../../tools/fs/safety.js";
|
|
|
3
3
|
import { applyRecordedEdit } from "../../tools/fs/fs-shared.js";
|
|
4
4
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
5
5
|
import { isOffloadedDetailReplacement } from "../tool-result-store.js";
|
|
6
|
+
import { attachmentTransmissionId } from "../user-attachment.js";
|
|
6
7
|
const READ_TOOL = "Read";
|
|
7
8
|
const WRITE_TOOL = "Write";
|
|
8
9
|
const EDIT_TOOL = "Edit";
|
|
@@ -69,7 +70,7 @@ export function wholeFileRecordsFromTranscript(messages) {
|
|
|
69
70
|
if (attachment?.observation !== undefined) {
|
|
70
71
|
const { key, entry } = attachment.observation;
|
|
71
72
|
if (isAbsoluteForFamily(pathFamilyOf({ root: key }), key))
|
|
72
|
-
byPath.set(key, { path: key, observation: entry, at: m.timestamp });
|
|
73
|
+
byPath.set(key, { path: key, observation: { ...entry, transmittedByToolCallId: attachmentTransmissionId(attachment) }, at: m.timestamp });
|
|
73
74
|
continue;
|
|
74
75
|
}
|
|
75
76
|
if (m.role !== "toolResult" || m.isError)
|
|
@@ -112,7 +113,7 @@ export function wholeFileRecordsFromTranscript(messages) {
|
|
|
112
113
|
continue;
|
|
113
114
|
if (isOffloadedDetailReplacement(content))
|
|
114
115
|
continue;
|
|
115
|
-
byPath.set(filePath, { path: filePath, content, at: m.timestamp });
|
|
116
|
+
byPath.set(filePath, { path: filePath, content, at: m.timestamp, ...(isRead ? { transmittedByToolCallId: m.toolCallId } : {}) });
|
|
116
117
|
}
|
|
117
118
|
return [...byPath.values()];
|
|
118
119
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE reading of `TaskSpec.skills` — the one place the caller's skill objects are read. The config doors call it before any
|
|
3
|
+
* resource (a resume before its claim) and hand its copy on as the leg's `skills`; the disclosure block and the `Skill` tool read
|
|
4
|
+
* that copy through `normalizeSkills` (synthetic-tools.ts), its sorted view.
|
|
5
|
+
*/
|
|
6
|
+
import type { SkillSpec } from "../agent-definition.js";
|
|
7
|
+
/**
|
|
8
|
+
* Total, and once: a value that is not a list, an entry that is not an object, a `name` / `description` / `content` that is not a
|
|
9
|
+
* string, or `files` that is not a list of `{ path, content }` strings refuses by name (`config.invalid_argument`, `detail.field` =
|
|
10
|
+
* the member's path); `baseDir` by its own reader ({@link skillBaseDirOf}, `config.skill_base_dir_invalid`). Every entry is
|
|
11
|
+
* screened, a later duplicate of a name included. Each field is read ONCE, BY NAME (an inherited or non-enumerable list counts;
|
|
12
|
+
* a rest copy would drop it). What it returns is plain data in declaration order, first of a name: every member and attachment
|
|
13
|
+
* copied, the two tables read ({@link readSkillTable}: a non-list is one entry that reads as nothing).
|
|
14
|
+
*/
|
|
15
|
+
export declare function readSkillsSeat(skills: unknown): SkillSpec[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readSkillTable } from "../skill-grant.js";
|
|
2
|
+
import { assertNoRetiredKeys, RETIRED_TASK_SPEC_KEYS } from "../retired-keys.js";
|
|
3
|
+
import { skillBaseDirOf } from "./skill-base-dir.js";
|
|
4
|
+
const SKILLS_SEAT = "TaskSpec.skills";
|
|
5
|
+
const kindOf = (v) => (v === null ? "null" : Array.isArray(v) ? "an array" : typeof v);
|
|
6
|
+
function skillsSeatRefusal(field, problem) {
|
|
7
|
+
const e = new Error(`${field} ${problem} — an entry the engine cannot read is refused rather than dropped.`);
|
|
8
|
+
e.code = "config.invalid_argument";
|
|
9
|
+
e.detail = { field };
|
|
10
|
+
return e;
|
|
11
|
+
}
|
|
12
|
+
function readSkillFiles(files, at) {
|
|
13
|
+
if (files === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
if (!Array.isArray(files))
|
|
16
|
+
throw skillsSeatRefusal(at, `must be a list of { path, content } attachments (got ${kindOf(files)})`);
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const [j, f] of files.entries()) {
|
|
19
|
+
if (typeof f !== "object" || f === null || Array.isArray(f))
|
|
20
|
+
throw skillsSeatRefusal(`${at}[${j}]`, `must be an attachment object { path, content } (got ${kindOf(f)})`);
|
|
21
|
+
const { path, content } = f;
|
|
22
|
+
if (typeof path !== "string")
|
|
23
|
+
throw skillsSeatRefusal(`${at}[${j}].path`, `must be a string (got ${kindOf(path)})`);
|
|
24
|
+
if (typeof content !== "string")
|
|
25
|
+
throw skillsSeatRefusal(`${at}[${j}].content`, `must be a string (got ${kindOf(content)})`);
|
|
26
|
+
out.push({ path, content });
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
export function readSkillsSeat(skills) {
|
|
31
|
+
if (skills === undefined)
|
|
32
|
+
return [];
|
|
33
|
+
if (!Array.isArray(skills))
|
|
34
|
+
throw skillsSeatRefusal(SKILLS_SEAT, `must be a list (got ${kindOf(skills)})`);
|
|
35
|
+
assertNoRetiredKeys({ skills }, RETIRED_TASK_SPEC_KEYS, "TaskSpec");
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const [i, raw] of skills.entries()) {
|
|
39
|
+
const at = `${SKILLS_SEAT}[${i}]`;
|
|
40
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw))
|
|
41
|
+
throw skillsSeatRefusal(at, `must be an object { name, description, content } (got ${kindOf(raw)})`);
|
|
42
|
+
const { name, description, content, allowedTools, disallowedTools, files: rawFiles, baseDir, ...rest } = raw;
|
|
43
|
+
void rest;
|
|
44
|
+
for (const [member, value] of [["name", name], ["description", description], ["content", content]]) {
|
|
45
|
+
if (typeof value !== "string")
|
|
46
|
+
throw skillsSeatRefusal(`${at}.${member}`, `must be a string (got ${kindOf(value)})`);
|
|
47
|
+
}
|
|
48
|
+
const files = readSkillFiles(rawFiles, `${at}.files`);
|
|
49
|
+
skillBaseDirOf(baseDir);
|
|
50
|
+
if (seen.has(name))
|
|
51
|
+
continue;
|
|
52
|
+
seen.add(name);
|
|
53
|
+
const [allowed, denied] = [readSkillTable(allowedTools), readSkillTable(disallowedTools)];
|
|
54
|
+
out.push({ name, description, content, ...(allowed !== undefined ? { allowedTools: allowed } : {}), ...(denied !== undefined ? { disallowedTools: denied } : {}), ...(files !== undefined ? { files } : {}), ...(baseDir !== undefined ? { baseDir } : {}) });
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|