@sema-agent/core 7.3.0 → 7.4.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 +49 -0
- package/dist/agents/peer-admission.d.ts +18 -3
- package/dist/agents/peer-admission.js +79 -4
- package/dist/agents/peer-held-queue.d.ts +101 -0
- package/dist/agents/peer-held-queue.js +229 -0
- package/dist/agents/peer-idle.d.ts +109 -0
- package/dist/agents/peer-idle.js +240 -0
- package/dist/agents/peer-notice-route.d.ts +33 -0
- package/dist/agents/peer-notice-route.js +46 -0
- package/dist/agents/peer-notices.d.ts +103 -0
- package/dist/agents/peer-notices.js +206 -0
- package/dist/agents/peer-session-drain.d.ts +39 -4
- package/dist/agents/peer-session-drain.js +248 -42
- package/dist/agents/send-message-tool.d.ts +8 -1
- package/dist/agents/send-message-tool.js +96 -30
- package/dist/agents/subagent.js +1 -0
- package/dist/brain/status-sink.d.ts +10 -0
- package/dist/brain/status-sink.js +13 -4
- package/dist/brain/stream-engine.d.ts +11 -0
- package/dist/brain/stream-engine.js +39 -3
- package/dist/core/arg-summary.d.ts +13 -3
- package/dist/core/arg-summary.js +138 -7
- package/dist/core/auto-mode-defaults.d.ts +11 -0
- package/dist/core/auto-mode-defaults.js +2 -0
- package/dist/core/auto-mode.d.ts +59 -0
- package/dist/core/auto-mode.js +57 -1
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +8 -0
- package/dist/core/hooks.d.ts +30 -0
- package/dist/core/hooks.js +43 -8
- package/dist/core/mailbox-store.d.ts +33 -1
- package/dist/core/mailbox-store.js +42 -2
- package/dist/core/runner/assemble-result.d.ts +5 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/denial-limit-arms.d.ts +149 -0
- package/dist/core/runner/denial-limit-arms.js +91 -0
- package/dist/core/runner/edited-files-ledger.d.ts +33 -0
- package/dist/core/runner/edited-files-ledger.js +14 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +5 -0
- package/dist/core/runner/prepare-hands-readface.js +1 -0
- package/dist/core/runner/prepare-task.d.ts +62 -1
- package/dist/core/runner/prepare-task.js +135 -89
- package/dist/core/runner/runtask.js +12 -0
- package/dist/core/sensitive-path-policy.d.ts +27 -6
- package/dist/core/sensitive-path-policy.js +57 -2
- package/dist/core/task-notification.d.ts +24 -2
- package/dist/core/task-notification.js +6 -1
- package/dist/core/tool-policy.d.ts +55 -4
- package/dist/core/tool-policy.js +28 -5
- package/dist/core/tools.js +1 -0
- package/dist/core/types.d.ts +251 -15
- package/dist/core/wiring-manifest.d.ts +41 -5
- package/dist/core/wiring-manifest.js +8 -0
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +3 -0
- package/dist/engine/harness/types.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +7 -0
- package/dist/engine/loop/agent-loop.js +79 -0
- package/dist/engine/loop/types.d.ts +42 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.js +10 -4
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/orchestration/workflow.js +7 -3
- package/dist/tools/fs/fs-write.d.ts +4 -4
- package/dist/tools/fs/fs-write.js +99 -14
- package/dist/tools/fs/index.d.ts +7 -1
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/fs/safety.d.ts +29 -8
- package/dist/tools/fs/safety.js +11 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +181 -1
|
@@ -19,6 +19,26 @@ export type StreamFn = LlmStreamFn;
|
|
|
19
19
|
* while tool-result message artifacts are emitted later in assistant source order.
|
|
20
20
|
*/
|
|
21
21
|
export type ToolExecutionMode = "sequential" | "parallel";
|
|
22
|
+
/**
|
|
23
|
+
* The verdict a tool's INPUT PRE-VALIDATION returns (see {@link AgentTool.validateInput}). `ok: true`
|
|
24
|
+
* (or an `undefined` return) lets the call proceed to the gate; `ok: false` refuses it BEFORE any
|
|
25
|
+
* permission question is asked — `message` is the model-facing text (the same text the tool's own
|
|
26
|
+
* execution would have produced for the same input, so the refusal reads identically either way) and
|
|
27
|
+
* `code` an optional machine-readable reason that rides the error result's structured details.
|
|
28
|
+
*/
|
|
29
|
+
export type ToolInputVerdict = {
|
|
30
|
+
ok: true;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
message: string;
|
|
34
|
+
code?: string;
|
|
35
|
+
};
|
|
36
|
+
/** The context an input pre-validation hook receives: the call's identity and its abort signal, and
|
|
37
|
+
* nothing else — a validator reads state, it does not act on the run. */
|
|
38
|
+
export interface ToolInputValidationContext {
|
|
39
|
+
toolCallId: string;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
}
|
|
22
42
|
/**
|
|
23
43
|
* Controls how many queued user messages are injected when the agent loop reaches a queue drain point.
|
|
24
44
|
*
|
|
@@ -527,6 +547,16 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
527
547
|
* signal re-check, which has always minted its abort result with no details.
|
|
528
548
|
*/
|
|
529
549
|
abortResultDetails?: () => Record<string, unknown> | undefined;
|
|
550
|
+
/**
|
|
551
|
+
* Fault seat for a THROWING {@link AgentTool.validateInput}: the loop reads the throw as "no
|
|
552
|
+
* verdict" (the call proceeds to the gate as if the tool had no validator) and reports the fault
|
|
553
|
+
* here so a host can disclose it. Must not throw; a throwing sink is swallowed.
|
|
554
|
+
*/
|
|
555
|
+
onToolInputValidationFault?: (info: {
|
|
556
|
+
toolName: string;
|
|
557
|
+
toolCallId: string;
|
|
558
|
+
error: unknown;
|
|
559
|
+
}) => void;
|
|
530
560
|
/**
|
|
531
561
|
* Called after a tool finishes executing, before `tool_execution_end` and tool-result message events are emitted.
|
|
532
562
|
*
|
|
@@ -684,6 +714,18 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unk
|
|
|
684
714
|
* (boundInputHash binds args, never the preview). NEVER adjudication input. Mechanism-neutral:
|
|
685
715
|
* any tool may declare one (run_workflow projects its script meta). */
|
|
686
716
|
approvalPreview?: (args: unknown) => unknown;
|
|
717
|
+
/**
|
|
718
|
+
* OPTIONAL input pre-validation, run by the loop AFTER schema validation and BEFORE the tool-call
|
|
719
|
+
* gate (permission hooks, policy, the human/classifier ask). A refusal is returned to the model as a
|
|
720
|
+
* typed error result and no question is ever asked for the call — the point of the seat: a call the
|
|
721
|
+
* tool would refuse on its own precondition (a file edit whose target was never read this session)
|
|
722
|
+
* must not cost the operator an approval card it can only ever fail on. Pure by contract: reads
|
|
723
|
+
* state (read-tracking, path grammar), never acts. A THROWING validator is read as "no verdict"
|
|
724
|
+
* (the call proceeds to the gate; the fault is disclosed through the loop's fault seat) — a broken
|
|
725
|
+
* validator must not refuse tools. The tool's own execution keeps validating the same precondition
|
|
726
|
+
* (the two reads are not one atomic step, and direct `execute` callers skip this seat).
|
|
727
|
+
*/
|
|
728
|
+
validateInput?: (args: Static<TParameters>, ctx: ToolInputValidationContext) => Promise<ToolInputVerdict | undefined> | ToolInputVerdict | undefined;
|
|
687
729
|
/** Execute the tool call. Throw on failure instead of encoding errors in `content`. */
|
|
688
730
|
execute: (toolCallId: string, params: Static<TParameters>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<TDetails>) => Promise<AgentToolResult<TDetails>>;
|
|
689
731
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -119,13 +119,13 @@ export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/f
|
|
|
119
119
|
export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
|
|
120
120
|
export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
|
|
121
121
|
export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, type SemaProvenance, } from "./core/task-notification.js";
|
|
122
|
-
export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, type WiringManifest, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
|
|
122
|
+
export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, type AutoModeArmReason, type WiringManifest, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
|
|
123
123
|
export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, type ParkSelfCheckResult, type ParkProbeFinding, type ParkProbeFindingCode, } from "./core/park-selfcheck.js";
|
|
124
124
|
export { type StoreDurability } from "./core/checkpoint-store.js";
|
|
125
125
|
export { type StoreFidelity } from "./core/checkpoint-store.js";
|
|
126
126
|
export { projectHumanInput, buildHumanInputEvent, type HumanInputSource, type HumanInputFrame, type HumanInputEvent, type HumanInputDelivery, } from "./core/human-input-projection.js";
|
|
127
127
|
export { TaskRegistry, defaultTaskRegistry, createTaskOutputTool, createTaskStopTool, type SemaTaskType, type SemaTaskStatus, type SemaTaskHandle, type ParkedClaimTicket, type TaskAccess, type UnifiedTaskOutput, type TaskRetrievalStatus, type StopSource, type RegisterMonitorInput, type MonitorTimers, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccessWorkflowRun, DURABLE_AGENT_HANDLE_RE, } from "./core/task-registry.js";
|
|
128
|
-
export { InMemoryMailboxStore, type MailboxStore, type MailboxMessage, type MailboxLease, MailboxStoreError, type MailboxStoreErrorCode, MAILBOX_TOMBSTONED_RECIPIENT_CODE, type MailboxAppendMessage, type MailboxPeerMeta, type MailboxPeerFromMode, type MailboxPeerRecordKind, MAILBOX_PEER_FROM_MODES, MAILBOX_PEER_RECORD_KINDS, MAILBOX_INVALID_PEER_META_CODE, MAILBOX_CROSS_PROCESS_UNSAFE_CODE, readMailboxPeerMeta, mailboxCrossProcessMountVerdict, type MailboxCrossProcessVerdict, } from "./core/mailbox-store.js";
|
|
128
|
+
export { InMemoryMailboxStore, type MailboxStore, type MailboxMessage, type MailboxLease, MailboxStoreError, type MailboxStoreErrorCode, MAILBOX_TOMBSTONED_RECIPIENT_CODE, type MailboxAppendMessage, type MailboxPeerMeta, type MailboxPeerFromMode, type MailboxPeerRecordKind, type MailboxPeerNoticeMeta, type MailboxPeerNoticeState, MAILBOX_PEER_NOTICE_STATES, MAILBOX_PEER_FROM_MODES, MAILBOX_PEER_RECORD_KINDS, MAILBOX_INVALID_PEER_META_CODE, MAILBOX_CROSS_PROCESS_UNSAFE_CODE, readMailboxPeerMeta, mailboxCrossProcessMountVerdict, type MailboxCrossProcessVerdict, } from "./core/mailbox-store.js";
|
|
129
129
|
export { FileMailboxStore, type FileMailboxStoreOptions } from "./stores/file/mailbox-store.js";
|
|
130
130
|
export { createFileTaskListStore } from "./stores/file/task-list-store.js";
|
|
131
131
|
export { createCcFileTaskListStore } from "./stores/cc/task-list-store.js";
|
|
@@ -145,7 +145,8 @@ export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_N
|
|
|
145
145
|
export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
|
|
146
146
|
export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
|
|
147
147
|
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, type AskDenyResolution, ASK_DENY_RESOLUTION_VALUES, isAskDenyResolution, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
148
|
-
export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, } from "./core/auto-mode.js";
|
|
148
|
+
export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
|
|
149
|
+
export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS } from "./core/auto-mode-defaults.js";
|
|
149
150
|
export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
|
|
150
151
|
export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
|
|
151
152
|
export { AUTO_MODE_ARMING_RECIPE_VERSION, autoModeArmingRecipeOf, sanitizeAutoModeArmingRecipe, foldAutoModeArming, type AutoModeArmingRecipe, type AutoModeArmingFace, type AutoModeArmingFold, type AutoModeRebuildRefusal, } from "./core/auto-mode-arming.js";
|
|
@@ -258,8 +259,12 @@ export { PEER_REF_MIN, PEER_REF_MAX, PEER_REF_RE, mintPeerRef, formatPeerNameRef
|
|
|
258
259
|
export { PEER_SESSION_RECORD_SCHEMA_VERSION, PEER_SESSION_RECORD_MAX_BYTES, SESSION_BOX_PREFIX, peerSessionBoxHandle, isPeerSessionId, PEER_SESSION_ID_GRAMMAR, parsePeerSessionAddress, readPeerSessionRecord, defaultPeerLivenessProbe, isPeerSessionProcessAlive, mintPeerSessionCandidates, resolvePeerSessions, createInMemoryPeerDirectory, vetPeerRegistryDirectory, judgePeerRecordFile, type PeerSessionRecord, type PeerSessionLiveness, type PeerSessionTempo, type PeerSessionRecordRefusal, type PeerSessionRecordRead, type PeerLivenessProbe, type PeerDirectory, type PeerDirectoryAccess, type PeerSessionCandidate, type PeerSessionResolution, type InMemoryPeerDirectory, type PeerRegistryDirectoryRefusal, type PeerRegistryDirectoryVerdict, } from "./agents/peer-directory.js";
|
|
259
260
|
export { createListAgentsTool, LIST_AGENTS_TOOL_NAME, LIST_AGENTS_TOOL_ALIAS, LIST_AGENTS_MAX_RESULT_CHARS, type ListAgentsToolOptions, type ListAgentsRow } from "./agents/list-agents-tool.js";
|
|
260
261
|
export { CROSS_SESSION_CLASSIFIER_RULE } from "./agents/cross-session-envelope.js";
|
|
261
|
-
export { PEER_SESSION_ID_UNGRAMMATICAL_CODE, type PeerLaneMountVerdict } from "./agents/peer-session-drain.js";
|
|
262
|
-
export { renderCrossSessionMessageFrame } from "./core/task-notification.js";
|
|
262
|
+
export { PEER_SESSION_ID_UNGRAMMATICAL_CODE, type PeerLaneMountVerdict, type PeerInboundDisposition } from "./agents/peer-session-drain.js";
|
|
263
|
+
export { renderCrossSessionMessageFrame, renderCrossSessionNoticeLine } from "./core/task-notification.js";
|
|
264
|
+
export { PEER_HELD_QUEUE_CAP, PEER_IDLE_SUBSCRIPTION_TTL_MS, PEER_IDLE_SUBSCRIBER_TABLE_CAP, PEER_IDLE_OUTSTANDING_CAP, PEER_IDLE_PRIORS_KEPT, PEER_IDLE_FIRE_DEBOUNCE_MS, PEER_IDLE_HELD_BACKOFF_MS, PEER_IDLE_LABEL_MAX, CROSS_SESSION_DIALOG_EXPIRY_VALUES, CROSS_SESSION_DIALOG_EXPIRY_DEFAULT, resolveCrossSessionDialogExpiry, PEER_HELD_REVIEW_CAUSES, PEER_DELIVERY_RECEIPT_STATES, isPeerDeliveryReceiptState, PEER_DROP_REASONS, peerDeliveryReceiptLabel, describePeerDeliveryReceipt, peerRecipientSuffix, renderCrossSessionDeliveryNotice, describePeerDropReasons, renderCrossSessionDroppedNotice, PEER_IDLE_NOTICE_KINDS, isPeerIdleNoticeKind, PEER_IDLE_UNAVAILABLE_CAUSES, peerIdleNoticeLabel, peerIdleDetailOf, formatPeerNoticeClock, peerIdleNoticeSummary, renderCrossSessionIdleNotice, describePeerIdleSubscription, type CrossSessionDialogExpiry, type ResolvedCrossSessionDialogExpiry, type PeerDeliveryReceiptState, type PeerDropReason, type PeerIdleNoticeKind, type PeerIdleNoticeFields, } from "./agents/peer-notices.js";
|
|
265
|
+
export { createPeerHeldQueue, peerHeldQueueFor, peerHeldQueueIfAny, peerHeldQueueKey, realPeerClock, type PeerInboundHoldCause, type PeerHeldEntry, type PeerHeldSettlement, type PeerHeldSettleReason, type PeerHeldSettleOutcome, type PeerClock, type PeerHeldQueueSink, type PeerHeldQueueConfig, type PeerHeldQueue, } from "./agents/peer-held-queue.js";
|
|
266
|
+
export { createPeerIdleTarget, createPeerIdleRequester, peerIdleMachineFor, peerIdleMachineIfAny, peerIdleMachineKey, type PeerIdleSubscriber, type PeerIdleTargetNotice, type PeerIdleTargetSink, type PeerIdleSubscribeOutcome, type PeerIdleTarget, type PeerIdleOutstanding, type PeerIdleRequesterSink, type PeerIdleRequester, type PeerIdleMachine, } from "./agents/peer-idle.js";
|
|
267
|
+
export { routePeerDeliveryReceipt, routePeerDroppedReceipt, routePeerIdleNotice, type PeerNoticeRoute } from "./agents/peer-notice-route.js";
|
|
263
268
|
export { type PeerAdmissionStage } from "./agents/peer-admission.js";
|
|
264
269
|
export { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME, type AgentTranscriptToolOptions, } from "./agents/agent-transcript-tool.js";
|
|
265
270
|
export { defineAgent } from "./agents/agent-definition.js";
|
|
@@ -277,13 +282,14 @@ export { repairTextToolCalls } from "./brain/tool-call-repair.js";
|
|
|
277
282
|
export { createCircuitBreakerBrain, type CircuitBreakerOptions, type BreakerState, type BreakerSnapshot, type BreakerPhase, CIRCUIT_OPEN_MARKER, } from "./brain/circuit-breaker.js";
|
|
278
283
|
export { createDegradingBrain, readDegradation, DEGRADED_DIAGNOSTIC_TYPE, type DegradingBrainOptions, type DegradeReason, type DegradationInfo, } from "./brain/degrading.js";
|
|
279
284
|
export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
|
|
285
|
+
export { WAITING_FIRST_TOKEN_AFTER_MS, WAITING_FIRST_TOKEN_EVERY_MS } from "./brain/stream-engine.js";
|
|
280
286
|
export { adjudicateModelRoute, resolveRouteCredential, routeRefusalText, routePairingStatus, normalizeBaseUrl, hasAuthCarrier, type RoutePairingStatus, } from "./brain/route-adjudicator.js";
|
|
281
287
|
export type { RouteAdjudication, RouteCredential, RouteCredentialSource, RoutePairingConfig, RoutePairingPosture, RouteRefusalCode, RouteRefusalDetail, } from "./internal/llm.js";
|
|
282
288
|
export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS, type RouteAdjudicationVector } from "./brain/route-conformance.js";
|
|
283
289
|
export { type BrainTimeoutConfig } from "./brain/timeout.js";
|
|
284
290
|
export { createAssistantMessageEventStream } from "./internal/llm.js";
|
|
285
291
|
export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
|
|
286
|
-
export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, TrackFileEditHook, TrackEditRequest, TrackEditResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, ResumePreflightInfo, ResumePreflightVerdict, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ReversibilityVerdict, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
|
|
292
|
+
export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, TrackFileEditHook, TrackEditRequest, TrackEditResult, FileEditedHook, FileEditedNotice, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, BrainRetryErrClass, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, ResumePreflightInfo, ResumePreflightVerdict, EngineNotice, RuntimeCaps, BackgroundChildEvent, DelegationLifecycleEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, EffectiveMemoryScopes, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolInputVerdict, ToolInputValidationContext, ReversibilityVerdict, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
|
|
287
293
|
export { Type } from "typebox";
|
|
288
294
|
export type { TSchema, Static } from "typebox";
|
|
289
295
|
export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";
|
package/dist/index.js
CHANGED
|
@@ -96,13 +96,13 @@ export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
|
|
|
96
96
|
export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
|
|
97
97
|
export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
|
|
98
98
|
export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, } from "./core/task-notification.js";
|
|
99
|
-
export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, } from "./core/wiring-manifest.js";
|
|
99
|
+
export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, } from "./core/wiring-manifest.js";
|
|
100
100
|
export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, } from "./core/park-selfcheck.js";
|
|
101
101
|
export {} from "./core/checkpoint-store.js";
|
|
102
102
|
export {} from "./core/checkpoint-store.js";
|
|
103
103
|
export { projectHumanInput, buildHumanInputEvent, } from "./core/human-input-projection.js";
|
|
104
104
|
export { TaskRegistry, defaultTaskRegistry, createTaskOutputTool, createTaskStopTool, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccessWorkflowRun, DURABLE_AGENT_HANDLE_RE, } from "./core/task-registry.js";
|
|
105
|
-
export { InMemoryMailboxStore, MailboxStoreError, MAILBOX_TOMBSTONED_RECIPIENT_CODE, MAILBOX_PEER_FROM_MODES, MAILBOX_PEER_RECORD_KINDS, MAILBOX_INVALID_PEER_META_CODE, MAILBOX_CROSS_PROCESS_UNSAFE_CODE, readMailboxPeerMeta, mailboxCrossProcessMountVerdict, } from "./core/mailbox-store.js";
|
|
105
|
+
export { InMemoryMailboxStore, MailboxStoreError, MAILBOX_TOMBSTONED_RECIPIENT_CODE, MAILBOX_PEER_NOTICE_STATES, MAILBOX_PEER_FROM_MODES, MAILBOX_PEER_RECORD_KINDS, MAILBOX_INVALID_PEER_META_CODE, MAILBOX_CROSS_PROCESS_UNSAFE_CODE, readMailboxPeerMeta, mailboxCrossProcessMountVerdict, } from "./core/mailbox-store.js";
|
|
106
106
|
export { FileMailboxStore } from "./stores/file/mailbox-store.js";
|
|
107
107
|
export { createFileTaskListStore } from "./stores/file/task-list-store.js";
|
|
108
108
|
export { createCcFileTaskListStore } from "./stores/cc/task-list-store.js";
|
|
@@ -120,7 +120,8 @@ export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTIN
|
|
|
120
120
|
export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
|
|
121
121
|
export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
|
|
122
122
|
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, ASK_DENY_RESOLUTION_VALUES, isAskDenyResolution, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
123
|
-
export { parseAutoModeResponse, createAutoModeDecider, } from "./core/auto-mode.js";
|
|
123
|
+
export { parseAutoModeResponse, createAutoModeDecider, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, } from "./core/auto-mode.js";
|
|
124
|
+
export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS } from "./core/auto-mode-defaults.js";
|
|
124
125
|
export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
|
|
125
126
|
export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
|
|
126
127
|
export { AUTO_MODE_ARMING_RECIPE_VERSION, autoModeArmingRecipeOf, sanitizeAutoModeArmingRecipe, foldAutoModeArming, } from "./core/auto-mode-arming.js";
|
|
@@ -216,7 +217,11 @@ export { PEER_SESSION_RECORD_SCHEMA_VERSION, PEER_SESSION_RECORD_MAX_BYTES, SESS
|
|
|
216
217
|
export { createListAgentsTool, LIST_AGENTS_TOOL_NAME, LIST_AGENTS_TOOL_ALIAS, LIST_AGENTS_MAX_RESULT_CHARS } from "./agents/list-agents-tool.js";
|
|
217
218
|
export { CROSS_SESSION_CLASSIFIER_RULE } from "./agents/cross-session-envelope.js";
|
|
218
219
|
export { PEER_SESSION_ID_UNGRAMMATICAL_CODE } from "./agents/peer-session-drain.js";
|
|
219
|
-
export { renderCrossSessionMessageFrame } from "./core/task-notification.js";
|
|
220
|
+
export { renderCrossSessionMessageFrame, renderCrossSessionNoticeLine } from "./core/task-notification.js";
|
|
221
|
+
export { PEER_HELD_QUEUE_CAP, PEER_IDLE_SUBSCRIPTION_TTL_MS, PEER_IDLE_SUBSCRIBER_TABLE_CAP, PEER_IDLE_OUTSTANDING_CAP, PEER_IDLE_PRIORS_KEPT, PEER_IDLE_FIRE_DEBOUNCE_MS, PEER_IDLE_HELD_BACKOFF_MS, PEER_IDLE_LABEL_MAX, CROSS_SESSION_DIALOG_EXPIRY_VALUES, CROSS_SESSION_DIALOG_EXPIRY_DEFAULT, resolveCrossSessionDialogExpiry, PEER_HELD_REVIEW_CAUSES, PEER_DELIVERY_RECEIPT_STATES, isPeerDeliveryReceiptState, PEER_DROP_REASONS, peerDeliveryReceiptLabel, describePeerDeliveryReceipt, peerRecipientSuffix, renderCrossSessionDeliveryNotice, describePeerDropReasons, renderCrossSessionDroppedNotice, PEER_IDLE_NOTICE_KINDS, isPeerIdleNoticeKind, PEER_IDLE_UNAVAILABLE_CAUSES, peerIdleNoticeLabel, peerIdleDetailOf, formatPeerNoticeClock, peerIdleNoticeSummary, renderCrossSessionIdleNotice, describePeerIdleSubscription, } from "./agents/peer-notices.js";
|
|
222
|
+
export { createPeerHeldQueue, peerHeldQueueFor, peerHeldQueueIfAny, peerHeldQueueKey, realPeerClock, } from "./agents/peer-held-queue.js";
|
|
223
|
+
export { createPeerIdleTarget, createPeerIdleRequester, peerIdleMachineFor, peerIdleMachineIfAny, peerIdleMachineKey, } from "./agents/peer-idle.js";
|
|
224
|
+
export { routePeerDeliveryReceipt, routePeerDroppedReceipt, routePeerIdleNotice } from "./agents/peer-notice-route.js";
|
|
220
225
|
export {} from "./agents/peer-admission.js";
|
|
221
226
|
export { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME, } from "./agents/agent-transcript-tool.js";
|
|
222
227
|
export { defineAgent } from "./agents/agent-definition.js";
|
|
@@ -234,6 +239,7 @@ export { repairTextToolCalls } from "./brain/tool-call-repair.js";
|
|
|
234
239
|
export { createCircuitBreakerBrain, CIRCUIT_OPEN_MARKER, } from "./brain/circuit-breaker.js";
|
|
235
240
|
export { createDegradingBrain, readDegradation, DEGRADED_DIAGNOSTIC_TYPE, } from "./brain/degrading.js";
|
|
236
241
|
export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
|
|
242
|
+
export { WAITING_FIRST_TOKEN_AFTER_MS, WAITING_FIRST_TOKEN_EVERY_MS } from "./brain/stream-engine.js";
|
|
237
243
|
export { adjudicateModelRoute, resolveRouteCredential, routeRefusalText, routePairingStatus, normalizeBaseUrl, hasAuthCarrier, } from "./brain/route-adjudicator.js";
|
|
238
244
|
export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS } from "./brain/route-conformance.js";
|
|
239
245
|
export {} from "./brain/timeout.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export type { CompactionPreparation, SummarizationClampDryRun } from "../engine/compaction/compaction.js";
|
|
8
8
|
export type { InvokedSkillRetention } from "../engine/compaction/utils.js";
|
|
9
9
|
export type { AgentCoreRuntimeDeps } from "../engine/loop/runtime-deps.js";
|
|
10
|
-
export type { AgentMessage, AgentTool, AgentToolResult, AgentToolUpdateCallback, ThinkingLevel, ToolExecutionMode, } from "../engine/loop/types.js";
|
|
10
|
+
export type { AgentMessage, AgentTool, AgentToolResult, AgentToolUpdateCallback, ThinkingLevel, ToolExecutionMode, ToolInputValidationContext, ToolInputVerdict, } from "../engine/loop/types.js";
|
|
11
11
|
export type { AgentHarnessEvent, CompactionSettings, ExecutionEnv, ExecutionErrorCode, FileError, FileErrorCode, FileInfo, Result, WriteExpectation, WriteReceipt, Session, SessionMetadata, SessionRepo, SessionStorage, SessionTreeEntry, Skill, } from "../engine/harness/types.js";
|
|
12
12
|
export type { ExecutionEnvExecOptions, ExecResult } from "../engine/harness/types.js";
|
|
13
13
|
export type { SessionWriteOptions, CompactionEntry } from "../engine/harness/types.js";
|
|
@@ -768,6 +768,9 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
768
768
|
};
|
|
769
769
|
let journalTail = Promise.resolve();
|
|
770
770
|
const JOURNAL_DRAIN_MAX_MS = 5_000;
|
|
771
|
+
const journalDurableResult = (result) => result.status !== "completed" && typeof result.errorMessage === "string" && result.errorMessage !== ""
|
|
772
|
+
? { ...result, errorMessage: boundedRedactedSummary(result.errorMessage, MAX_TRANSCRIPT_CHARS) }
|
|
773
|
+
: result;
|
|
771
774
|
const journalAppend = async (callKey, result, label) => {
|
|
772
775
|
const dbg = typeof process !== "undefined" && process.env?.SEMA_DEBUG_WORKFLOW_JOURNAL === "1";
|
|
773
776
|
if (!journalStore) {
|
|
@@ -775,9 +778,10 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
775
778
|
console.error(`[sema:wf-journal] runId=${runId} callKey=${callKey} SKIP (no journalStore on this run)`);
|
|
776
779
|
return;
|
|
777
780
|
}
|
|
781
|
+
const stored = journalDurableResult(result);
|
|
778
782
|
let serialized;
|
|
779
783
|
try {
|
|
780
|
-
serialized = JSON.stringify(
|
|
784
|
+
serialized = JSON.stringify(stored);
|
|
781
785
|
}
|
|
782
786
|
catch {
|
|
783
787
|
serialized = undefined;
|
|
@@ -788,7 +792,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
788
792
|
run.journalSkips = (run.journalSkips ?? 0) + 1;
|
|
789
793
|
emitRunLog(`resume-journal: agent #${callKeyOrdinal(callKey)}${label !== undefined ? ` "${label.slice(0, 80)}"` : ""} result is ${bytes} bytes, ` +
|
|
790
794
|
`over the ${MAX_JOURNAL_RESULT_BYTES}-byte per-entry cap — NOT cached. A resume from this run re-runs this agent and everything after it live.`);
|
|
791
|
-
const tombstone = journalStore.append(runId, scope, { callKey, result: journalOversizeTombstone(
|
|
795
|
+
const tombstone = journalStore.append(runId, scope, { callKey, result: journalOversizeTombstone(stored, bytes) });
|
|
792
796
|
journalTail = journalTail.then(() => tombstone).catch(() => undefined);
|
|
793
797
|
try {
|
|
794
798
|
await tombstone;
|
|
@@ -801,7 +805,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
801
805
|
}
|
|
802
806
|
return;
|
|
803
807
|
}
|
|
804
|
-
const p = journalStore.append(runId, scope, { callKey, result });
|
|
808
|
+
const p = journalStore.append(runId, scope, { callKey, result: stored });
|
|
805
809
|
journalTail = journalTail.then(() => p).catch(() => undefined);
|
|
806
810
|
try {
|
|
807
811
|
await p;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
|
-
import type { BeforeWriteHook, TrackFileEditHook } from "../../core/types.js";
|
|
2
|
+
import type { BeforeWriteHook, FileEditedHook, TrackFileEditHook } from "../../core/types.js";
|
|
3
3
|
import { type ReadFileState } from "./safety.js";
|
|
4
4
|
import { type CwdRef } from "./fs-shared.js";
|
|
5
5
|
/**
|
|
@@ -12,8 +12,8 @@ import { type CwdRef } from "./fs-shared.js";
|
|
|
12
12
|
* prefix comparison there (零开销直通). Absent hook ⇒ byte-identical behavior.
|
|
13
13
|
*/
|
|
14
14
|
export type { BeforeWriteRequest, BeforeWriteResult, BeforeWriteHook, TrackEditRequest, TrackEditResult, TrackFileEditHook } from "../../core/types.js";
|
|
15
|
-
export declare function createEditFileTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], beforeWrite?: BeforeWriteHook, trackEdit?: TrackFileEditHook): AgentTool;
|
|
16
|
-
export declare function createWriteFileTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], beforeWrite?: BeforeWriteHook, trackEdit?: TrackFileEditHook): AgentTool;
|
|
15
|
+
export declare function createEditFileTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], beforeWrite?: BeforeWriteHook, trackEdit?: TrackFileEditHook, onEdited?: FileEditedHook): AgentTool;
|
|
16
|
+
export declare function createWriteFileTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], beforeWrite?: BeforeWriteHook, trackEdit?: TrackFileEditHook, onEdited?: FileEditedHook): AgentTool;
|
|
17
17
|
/**
|
|
18
18
|
* design v1.163 — NotebookEdit: replace/insert/delete a single cell in a .ipynb. CC-parity tool over the SAME hand-band
|
|
19
19
|
* safety skeleton as Edit/Write (resolveKey containment → requireRead read-before-edit → checkStale content-hash
|
|
@@ -21,4 +21,4 @@ export declare function createWriteFileTool(env: ExecutionEnv, state: ReadFileSt
|
|
|
21
21
|
* .ipynb as the RB-227 cell projection but records read state (hash/totalLines) in the RAW notebook-text
|
|
22
22
|
* coordinate — that raw-coordinate read record is what this tool's freshness check depends on.
|
|
23
23
|
*/
|
|
24
|
-
export declare function createNotebookEditTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], beforeWrite?: BeforeWriteHook, trackEdit?: TrackFileEditHook): AgentTool;
|
|
24
|
+
export declare function createNotebookEditTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], beforeWrite?: BeforeWriteHook, trackEdit?: TrackFileEditHook, onEdited?: FileEditedHook): AgentTool;
|
|
@@ -5,16 +5,35 @@ import { sha256, resolveKey, violationText, violationDetails, requireRead, check
|
|
|
5
5
|
import { decodeTextBytes, encodeTextForFile, normalizeEditText, normalizeFileText } from "./encoding.js";
|
|
6
6
|
import { MAX_EDIT_BYTES, decodeEditBytes, tooLargeToEditMessage, truncatedUtf16BodyMessage, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, ipynbRedirect, countLines, applyRecordedEdit, } from "./fs-shared.js";
|
|
7
7
|
async function envFinalWrite(env, key, content, signal, opts) {
|
|
8
|
+
const fireEdited = () => {
|
|
9
|
+
if (opts?.edited?.hook === undefined)
|
|
10
|
+
return;
|
|
11
|
+
try {
|
|
12
|
+
const ack = opts.edited.hook({ tool: opts.edited.tool, path: opts.edited.path, key });
|
|
13
|
+
if (ack !== null && (typeof ack === "object" || typeof ack === "function") && typeof ack.then === "function") {
|
|
14
|
+
void ack.then(undefined, () => undefined);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
}
|
|
19
|
+
};
|
|
8
20
|
let r;
|
|
9
21
|
try {
|
|
10
22
|
r = await writeThroughEnv(env, key, content, signal, opts);
|
|
11
23
|
}
|
|
12
24
|
catch (err) {
|
|
13
25
|
await discardTrackedEdit(opts?.track, "verify");
|
|
26
|
+
fireEdited();
|
|
14
27
|
throw err;
|
|
15
28
|
}
|
|
16
|
-
if (!r.ok)
|
|
17
|
-
|
|
29
|
+
if (!r.ok) {
|
|
30
|
+
const proven = NOTHING_WAS_WRITTEN.has(r.error.code);
|
|
31
|
+
await discardTrackedEdit(opts?.track, proven ? "proven" : "verify");
|
|
32
|
+
if (!proven)
|
|
33
|
+
fireEdited();
|
|
34
|
+
return r;
|
|
35
|
+
}
|
|
36
|
+
fireEdited();
|
|
18
37
|
return r;
|
|
19
38
|
}
|
|
20
39
|
const NOTHING_WAS_WRITTEN = new Set(["already_exists", "precondition_failed"]);
|
|
@@ -66,7 +85,7 @@ async function gateToolWrite(hook, tool, path, key, content) {
|
|
|
66
85
|
return `Error (${tool}): write rejected by the write gate (${res.code}): ${clipHookText(res.reason)}`;
|
|
67
86
|
return undefined;
|
|
68
87
|
}
|
|
69
|
-
export function createEditFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit) {
|
|
88
|
+
export function createEditFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
70
89
|
return defineTool({
|
|
71
90
|
name: "Edit",
|
|
72
91
|
contract: { contractId: "core.edit@1", implementationRevision: "1" },
|
|
@@ -83,6 +102,35 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
83
102
|
replace_all: Type.Optional(Type.Boolean({ description: "Replace all occurrences of old_string (default false)" })),
|
|
84
103
|
}),
|
|
85
104
|
effect: "write",
|
|
105
|
+
validateInput: async (args, ctx) => {
|
|
106
|
+
const a = args;
|
|
107
|
+
if (Array.isArray(a.edits) && a.edits.length > 0)
|
|
108
|
+
return { ok: true };
|
|
109
|
+
if (typeof a.old_string !== "string" || typeof a.new_string !== "string")
|
|
110
|
+
return { ok: true };
|
|
111
|
+
const path = fileArgPath(args);
|
|
112
|
+
if (path === undefined)
|
|
113
|
+
return { ok: true };
|
|
114
|
+
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
115
|
+
if (!r.ok)
|
|
116
|
+
return { ok: true };
|
|
117
|
+
if (a.old_string === a.new_string)
|
|
118
|
+
return { ok: true };
|
|
119
|
+
const exists = await env.exists(r.key, ctx.signal);
|
|
120
|
+
if (!exists.ok || !exists.value)
|
|
121
|
+
return { ok: true };
|
|
122
|
+
const editInfo = await env.fileInfo(r.key, ctx.signal);
|
|
123
|
+
if (editInfo.ok && editInfo.value.size > MAX_EDIT_BYTES)
|
|
124
|
+
return { ok: true };
|
|
125
|
+
if (a.old_string === "")
|
|
126
|
+
return { ok: true };
|
|
127
|
+
if (ipynbRedirect("Edit", path))
|
|
128
|
+
return { ok: true };
|
|
129
|
+
const notRead = requireRead(state, r.key);
|
|
130
|
+
if (notRead === undefined)
|
|
131
|
+
return { ok: true };
|
|
132
|
+
return { ok: false, code: notRead.code, message: await notReadRefusalText(env, "Edit", r.key, notRead, ctx.signal, READ_REFUSED_ESCAPE_HINT) };
|
|
133
|
+
},
|
|
86
134
|
execute: async (args, ctx) => {
|
|
87
135
|
const a = args;
|
|
88
136
|
const batch = Array.isArray(a.edits) && a.edits.length > 0;
|
|
@@ -122,7 +170,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
122
170
|
const tracked = await trackToolEdit(trackEdit, "Edit", path, r.key, ctx.signal);
|
|
123
171
|
if (tracked.refusal !== undefined)
|
|
124
172
|
return errorResult(tracked.refusal);
|
|
125
|
-
const write = await envFinalWrite(env, r.key, created, ctx.signal, { exclusive: true, track: tracked });
|
|
173
|
+
const write = await envFinalWrite(env, r.key, created, ctx.signal, { exclusive: true, track: tracked, edited: { hook: onEdited, tool: "Edit", path } });
|
|
126
174
|
if (!write.ok) {
|
|
127
175
|
if (write.error.code === "already_exists") {
|
|
128
176
|
return errorResult(`Error (Edit): cannot create "${path}": file already exists (created concurrently since the existence check). Read the file first, then edit it normally (or Write after the Read to overwrite it).`);
|
|
@@ -176,7 +224,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
176
224
|
if (trackedOverwrite.refusal !== undefined)
|
|
177
225
|
return errorResult(trackedOverwrite.refusal);
|
|
178
226
|
const encodedOverwrite = encodeTextForFile(newContent, preDec.encoding, preDec.endings);
|
|
179
|
-
const write = await envFinalWrite(env, r.key, encodedOverwrite, ctx.signal, { track: trackedOverwrite });
|
|
227
|
+
const write = await envFinalWrite(env, r.key, encodedOverwrite, ctx.signal, { track: trackedOverwrite, edited: { hook: onEdited, tool: "Edit", path } });
|
|
180
228
|
if (!write.ok)
|
|
181
229
|
return errorResult(`Error (Edit): cannot write "${path}": ${write.error.message}`);
|
|
182
230
|
const persistedOverwrite = persistedTextOf(encodedOverwrite);
|
|
@@ -253,7 +301,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
253
301
|
if (trackedEdit.refusal !== undefined)
|
|
254
302
|
return errorResult(trackedEdit.refusal);
|
|
255
303
|
const encodedEdit = encodeTextForFile(working, decoded.encoding, decoded.endings);
|
|
256
|
-
const write = await envFinalWrite(env, r.key, encodedEdit, ctx.signal, { track: trackedEdit });
|
|
304
|
+
const write = await envFinalWrite(env, r.key, encodedEdit, ctx.signal, { track: trackedEdit, edited: { hook: onEdited, tool: "Edit", path } });
|
|
257
305
|
if (!write.ok)
|
|
258
306
|
return errorResult(`Error (Edit): cannot write "${path}": ${write.error.message}`);
|
|
259
307
|
const persistedEdit = persistedTextOf(encodedEdit);
|
|
@@ -276,7 +324,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
276
324
|
},
|
|
277
325
|
});
|
|
278
326
|
}
|
|
279
|
-
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit) {
|
|
327
|
+
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
280
328
|
return defineTool({
|
|
281
329
|
name: "Write",
|
|
282
330
|
contract: { contractId: "core.write@1", implementationRevision: "1" },
|
|
@@ -290,6 +338,23 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
290
338
|
content: Type.String({ description: "The content to write to the file" }),
|
|
291
339
|
}),
|
|
292
340
|
effect: "write",
|
|
341
|
+
validateInput: async (args, ctx) => {
|
|
342
|
+
const path = fileArgPath(args);
|
|
343
|
+
if (path === undefined)
|
|
344
|
+
return { ok: true };
|
|
345
|
+
if (ipynbRedirect("Write", path))
|
|
346
|
+
return { ok: true };
|
|
347
|
+
const r = await resolveKey(env, rootCanonical, path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
348
|
+
if (!r.ok)
|
|
349
|
+
return { ok: true };
|
|
350
|
+
const exists = await env.exists(r.key, ctx.signal);
|
|
351
|
+
if (!exists.ok || !exists.value)
|
|
352
|
+
return { ok: true };
|
|
353
|
+
const notRead = requireRead(state, r.key);
|
|
354
|
+
if (notRead === undefined)
|
|
355
|
+
return { ok: true };
|
|
356
|
+
return { ok: false, code: notRead.code, message: await notReadRefusalText(env, "Write", r.key, notRead, ctx.signal, READ_REFUSED_ESCAPE_HINT) };
|
|
357
|
+
},
|
|
293
358
|
execute: async (args, ctx) => {
|
|
294
359
|
const { content } = args;
|
|
295
360
|
const path = fileArgPath(args);
|
|
@@ -327,7 +392,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
327
392
|
if (trackedWrite.refusal !== undefined)
|
|
328
393
|
return errorResult(trackedWrite.refusal);
|
|
329
394
|
const encodedWrite = encodeTextForFile(content, decodedPrev.encoding, "preserve");
|
|
330
|
-
const write = await envFinalWrite(env, r.key, encodedWrite, ctx.signal, { track: trackedWrite });
|
|
395
|
+
const write = await envFinalWrite(env, r.key, encodedWrite, ctx.signal, { track: trackedWrite, edited: { hook: onEdited, tool: "Write", path } });
|
|
331
396
|
if (!write.ok)
|
|
332
397
|
return errorResult(`Error (Write): cannot write "${path}": ${write.error.message}`);
|
|
333
398
|
const persistedWrite = persistedTextOf(encodedWrite);
|
|
@@ -343,7 +408,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
343
408
|
const trackedCreate = await trackToolEdit(trackEdit, "Write", path, r.key, ctx.signal);
|
|
344
409
|
if (trackedCreate.refusal !== undefined)
|
|
345
410
|
return errorResult(trackedCreate.refusal);
|
|
346
|
-
const write = await envFinalWrite(env, r.key, content, ctx.signal, { track: trackedCreate });
|
|
411
|
+
const write = await envFinalWrite(env, r.key, content, ctx.signal, { track: trackedCreate, edited: { hook: onEdited, tool: "Write", path } });
|
|
347
412
|
if (!write.ok)
|
|
348
413
|
return errorResult(`Error (Write): cannot write "${path}": ${write.error.message}`);
|
|
349
414
|
const totalLines = countLines(content);
|
|
@@ -356,7 +421,10 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
356
421
|
},
|
|
357
422
|
});
|
|
358
423
|
}
|
|
359
|
-
|
|
424
|
+
const notebookNotIpynbMessage = (notebookPath) => `Error (NotebookEdit): "${notebookPath}" is not a .ipynb file; use Edit for other file types.`;
|
|
425
|
+
const NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE = `Error (NotebookEdit): Cell type is required when using edit_mode=insert.`;
|
|
426
|
+
const NOTEBOOK_CELL_ID_REQUIRED_MESSAGE = `Error (NotebookEdit): cell_id is required for replace/delete.`;
|
|
427
|
+
export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
360
428
|
return defineTool({
|
|
361
429
|
name: "NotebookEdit",
|
|
362
430
|
contract: { contractId: "core.notebook_edit@1", implementationRevision: "1" },
|
|
@@ -382,18 +450,35 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
|
|
|
382
450
|
})),
|
|
383
451
|
}),
|
|
384
452
|
effect: "write",
|
|
453
|
+
validateInput: async (args, ctx) => {
|
|
454
|
+
const a = args;
|
|
455
|
+
const mode = a.edit_mode ?? "replace";
|
|
456
|
+
if (!a.notebook_path.toLowerCase().endsWith(".ipynb"))
|
|
457
|
+
return { ok: false, code: "invalid", message: notebookNotIpynbMessage(a.notebook_path) };
|
|
458
|
+
if (mode === "insert" && !a.cell_type)
|
|
459
|
+
return { ok: false, code: "invalid", message: NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE };
|
|
460
|
+
if (mode !== "insert" && !a.cell_id)
|
|
461
|
+
return { ok: false, code: "invalid", message: NOTEBOOK_CELL_ID_REQUIRED_MESSAGE };
|
|
462
|
+
const r = await resolveKey(env, rootCanonical, a.notebook_path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
463
|
+
if (!r.ok)
|
|
464
|
+
return { ok: true };
|
|
465
|
+
const notRead = requireRead(state, r.key);
|
|
466
|
+
if (notRead === undefined)
|
|
467
|
+
return { ok: true };
|
|
468
|
+
return { ok: false, code: notRead.code, message: await notReadRefusalText(env, "NotebookEdit", r.key, notRead, ctx.signal, READ_REFUSED_ESCAPE_HINT) };
|
|
469
|
+
},
|
|
385
470
|
execute: async (args, ctx) => {
|
|
386
471
|
const a = args;
|
|
387
472
|
const { notebook_path, cell_id, new_source } = a;
|
|
388
473
|
let cellType = a.cell_type;
|
|
389
474
|
const mode = a.edit_mode ?? "replace";
|
|
390
475
|
if (!notebook_path.toLowerCase().endsWith(".ipynb")) {
|
|
391
|
-
return errorResult(
|
|
476
|
+
return errorResult(notebookNotIpynbMessage(notebook_path));
|
|
392
477
|
}
|
|
393
478
|
if (mode === "insert" && !cellType)
|
|
394
|
-
return errorResult(
|
|
479
|
+
return errorResult(NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE);
|
|
395
480
|
if (mode !== "insert" && !cell_id)
|
|
396
|
-
return errorResult(
|
|
481
|
+
return errorResult(NOTEBOOK_CELL_ID_REQUIRED_MESSAGE);
|
|
397
482
|
const r = await resolveKey(env, rootCanonical, notebook_path, ctx.signal, cwdRef?.current, additionalRoots);
|
|
398
483
|
if (!r.ok)
|
|
399
484
|
return errorResult(violationText("NotebookEdit", r.violation), violationDetails(r.violation));
|
|
@@ -477,7 +562,7 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
|
|
|
477
562
|
const trackedNb = await trackToolEdit(trackEdit, "NotebookEdit", notebook_path, r.key, ctx.signal);
|
|
478
563
|
if (trackedNb.refusal !== undefined)
|
|
479
564
|
return errorResult(trackedNb.refusal);
|
|
480
|
-
const w = await envFinalWrite(env, r.key, encodeTextForFile(updated, decodedNb.encoding, decodedNb.endings), ctx.signal, { track: trackedNb });
|
|
565
|
+
const w = await envFinalWrite(env, r.key, encodeTextForFile(updated, decodedNb.encoding, decodedNb.endings), ctx.signal, { track: trackedNb, edited: { hook: onEdited, tool: "NotebookEdit", path: notebook_path } });
|
|
481
566
|
if (!w.ok)
|
|
482
567
|
return errorResult(`Error (NotebookEdit): cannot write "${notebook_path}": ${w.error.message}`);
|
|
483
568
|
state.set(r.key, { hash: sha256(updated), totalLines: countLines(updated), truncated: false, lastReadAt: Date.now() });
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
|
-
import { type BeforeWriteHook, type TrackFileEditHook } from "../../core/types.js";
|
|
2
|
+
import { type BeforeWriteHook, type FileEditedHook, type TrackFileEditHook } from "../../core/types.js";
|
|
3
3
|
import { type TaskRegistry } from "../../core/task-registry.js";
|
|
4
4
|
import { type ReadFileState } from "./safety.js";
|
|
5
5
|
import type { PdfModelCapabilities } from "./pdf.js";
|
|
@@ -120,6 +120,12 @@ export interface HandsToolkitOptions {
|
|
|
120
120
|
* env write, so the file's pre-edit state is durably recorded before it changes. The Runner
|
|
121
121
|
* wires it to `FileHistoryStore.trackEdit`; absent ⇒ byte-identical behavior (no history). */
|
|
122
122
|
trackFileEdit?: TrackFileEditHook;
|
|
123
|
+
/** The mutation lane's LANDED observation seat — fired once per Write/Edit/NotebookEdit call whose
|
|
124
|
+
* bytes reached disk (never for a refused/failed one, never for a Bash-authored change). Wired by
|
|
125
|
+
* the Runner to the per-run accumulator behind `TaskResult.editedFiles`; absent ⇒ nothing observes.
|
|
126
|
+
* Purely additive: it cannot refuse a write, and a fault in it is contained in both shapes (a
|
|
127
|
+
* synchronous throw and a rejected promise), neither of them awaited. */
|
|
128
|
+
onFileEdited?: FileEditedHook;
|
|
123
129
|
/** #181-F6 — see createBashTool's taskOpts field of the same name: whether the Monitor tool is on
|
|
124
130
|
* this run's roster (the Runner mounts Monitor, this band never does). `false` drops the gh
|
|
125
131
|
* rate-limit hint's Monitor clause; absent ⇒ historic full wording (byte-compat). */
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -50,7 +50,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
50
50
|
createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, opts.readCyberReminder, readDeny, readFace, opts.reminderMark, opts.reminderDisclosureCounts),
|
|
51
51
|
];
|
|
52
52
|
if (!readOnly) {
|
|
53
|
-
tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit));
|
|
53
|
+
tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited));
|
|
54
54
|
}
|
|
55
55
|
tools.push(createGrepTool(env, rootCanonical, readFaceRoots, readDeny, readFace), createGlobTool(env, rootCanonical, readFaceRoots, readDeny, readFace), createRepoMapTool(env, rootCanonical, readFaceRoots, readDeny, readFace));
|
|
56
56
|
if (includeShell) {
|
|
@@ -138,6 +138,18 @@ export declare function isAbsolutePathForm(p: string): boolean;
|
|
|
138
138
|
* imports are `node:crypto` plus a type, so there is no layering wall between them.
|
|
139
139
|
*/
|
|
140
140
|
export declare function isWinFormPath(p: string): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Expand a LEADING `~` (bare, or followed by a separator of either family) against `home`; every other
|
|
143
|
+
* spelling is returned untouched — a `~` anywhere but the front is an ordinary filename character.
|
|
144
|
+
*
|
|
145
|
+
* Lives here for the same reason `isWinFormPath` does: it is a path-FAMILY decision (which characters
|
|
146
|
+
* end the first component), and the domain gate holds this module as the one home for those. Callers
|
|
147
|
+
* that need it: the sensitive-path guard's data-root resolution, and — spelled privately for now —
|
|
148
|
+
* `tool-policy.ts`'s `lexicalPath`. NOT a resolution of relative paths: a caller that also needs that
|
|
149
|
+
* says so with its own `resolve`, because the two consumers of this value disagree about which cwd a
|
|
150
|
+
* relative spelling belongs to and that disagreement is theirs to state, not this helper's to hide.
|
|
151
|
+
*/
|
|
152
|
+
export declare function expandHomeTilde(p: string, home: string): string;
|
|
141
153
|
export declare function isBlockedDevicePath(key: string): boolean;
|
|
142
154
|
/**
|
|
143
155
|
* RB-153 — purely LEXICAL path normalization: collapse repeated separators, drop `.` segments, and
|
|
@@ -335,16 +347,25 @@ export declare function withinAnyRoot(rootsCanonical: readonly string[], p: stri
|
|
|
335
347
|
/**
|
|
336
348
|
* RB-371 ① — the escape-hatch tail for the `path_not_in_root` refusal: its three sibling hints
|
|
337
349
|
* below each name a sanctioned next step; this refusal named none, leaving the model to oscillate
|
|
338
|
-
* between "the boundary is hard" and
|
|
350
|
+
* between "the boundary is hard" and hunting for a way around it.
|
|
351
|
+
*
|
|
352
|
+
* The card names the SANCTIONED way out and nothing else: widening the declaration
|
|
353
|
+
* (`additionalDirectories` / read-only `additionalReadDirectories`, design/119), or the
|
|
354
|
+
* deployment-level `readFace: "open"` for reads.
|
|
339
355
|
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
356
|
+
* A trailing sentence naming the shell as unconfined by this fence was REMOVED (it had been kept on
|
|
357
|
+
* the argument that a statement of fact is not a recommendation, and that omitting it would only let
|
|
358
|
+
* the model discover the same thing by trial). A live retest settled that argument the other way: a
|
|
359
|
+
* refused agent read the sentence as the next step, went to the shell, and rewrote through it the
|
|
360
|
+
* very file the fence had just protected. A refusal card is a PROMPT surface — naming, at the moment
|
|
361
|
+
* of denial, a second tool that does not enforce the boundary is a recipe whatever its grammar, and
|
|
362
|
+
* the model cannot be relied on to hear "fact, not advice". The underlying fact is unchanged and
|
|
363
|
+
* still documented where humans read it (design/44 §5: the shell is deliberately unconfined by this
|
|
364
|
+
* fence, and every shell call still passes the deployment's approval policy); it is simply no longer
|
|
365
|
+
* handed to the model inside a denial. What the card says now: this is denied, and here is the
|
|
366
|
+
* sanctioned way to be allowed.
|
|
346
367
|
*/
|
|
347
|
-
export declare const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories \u2014 or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.
|
|
368
|
+
export declare const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories \u2014 or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.)";
|
|
348
369
|
/** inv 1 (read-before-edit): a file must have been read this task before it can be edited/overwritten.
|
|
349
370
|
* Message is CC 2.1.198 live-verbatim (all-tools-live-probe 2026-07-08 §2.1/§3.1/§5.1 — one message for
|
|
350
371
|
* Edit/Write/NotebookEdit: "before writing to it", not the old sema "before editing").
|
package/dist/tools/fs/safety.js
CHANGED
|
@@ -53,6 +53,16 @@ const WIN_RESERVED_RE = /^(CON|PRN|AUX|NUL|COM[0-9¹²³]|LPT[0-9¹²³]|CONIN\$
|
|
|
53
53
|
export function isWinFormPath(p) {
|
|
54
54
|
return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith("\\\\") || (!p.startsWith("/") && p.includes("\\"));
|
|
55
55
|
}
|
|
56
|
+
export function expandHomeTilde(p, home) {
|
|
57
|
+
if (p === "~")
|
|
58
|
+
return home;
|
|
59
|
+
if (!p.startsWith("~"))
|
|
60
|
+
return p;
|
|
61
|
+
const next = p[1];
|
|
62
|
+
if (next !== "/" && next !== "\\")
|
|
63
|
+
return p;
|
|
64
|
+
return home + "/" + p.slice(2);
|
|
65
|
+
}
|
|
56
66
|
function isWinReservedDeviceKey(key) {
|
|
57
67
|
if (!isWinFormPath(key))
|
|
58
68
|
return false;
|
|
@@ -493,7 +503,7 @@ export function violationDetails(v) {
|
|
|
493
503
|
export function withinAnyRoot(rootsCanonical, p) {
|
|
494
504
|
return rootsCanonical.some((r) => within(r, p));
|
|
495
505
|
}
|
|
496
|
-
export const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories — or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.
|
|
506
|
+
export const PATH_NOT_IN_ROOT_ESCAPE_HINT = "(This boundary applies to the structured file tools. If you genuinely need content outside the allowed root(s), ask for the directory to be added to the deployment's additionalDirectories — or additionalReadDirectories for read-only access; a deployment that wants reads open everywhere can declare readFace: \"open\" instead of listing directories.)";
|
|
497
507
|
export function requireRead(state, key) {
|
|
498
508
|
const entry = state.get(key);
|
|
499
509
|
if (entry === undefined || entry.isPartialView) {
|