@sema-agent/core 7.3.1 → 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 +35 -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/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 +120 -89
- package/dist/core/runner/runtask.js +10 -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/types.d.ts +207 -10
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8 -3
- package/dist/orchestration/workflow.js +7 -3
- package/dist/tools/fs/fs-write.d.ts +4 -4
- package/dist/tools/fs/fs-write.js +30 -11
- 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 +169 -1
package/dist/core/types.d.ts
CHANGED
|
@@ -733,6 +733,32 @@ export interface ToolExecuteContext {
|
|
|
733
733
|
* the tool runs outside a Runner task.
|
|
734
734
|
*/
|
|
735
735
|
parentCwd?: string;
|
|
736
|
+
/**
|
|
737
|
+
* The RUNNING task's file-history LINEAGE (Runner-filled, read-only, never a model/tool argument;
|
|
738
|
+
* present iff a `RunnerDeps.fileHistoryStore` is live for the run): the history scope this run
|
|
739
|
+
* records its first-touch edits into and the TREE those records' keys are minted against — the
|
|
740
|
+
* canonical root spelling plus the env's filesystem identity (`fs`: a remote workspace's handle,
|
|
741
|
+
* `"host"` for the control-plane host's filesystem, else a per-env-instance token).
|
|
742
|
+
*
|
|
743
|
+
* WHICH children are threaded it, exactly: the Agent tool's FRESH spawn legs (sync / steer /
|
|
744
|
+
* background / fork) pass it VERBATIM into the child's trusted internals
|
|
745
|
+
* (`RunInternals.fileHistoryLineage`), which is how a same-process delegation tree keeps ONE
|
|
746
|
+
* history: a child on the SAME tree (both coordinates equal) records into the lineage's scope (the
|
|
747
|
+
* root session's), so the root session's `rewindFilesTo` reaches the child's edits; a child on a
|
|
748
|
+
* different tree (worktree isolation, an explicit `cwd`, a fresh per-task sandbox) starts its own
|
|
749
|
+
* lineage, because root-relative keys re-joined against a different tree would name files that run
|
|
750
|
+
* never edited. Three delegated legs deliberately carry NO lineage and keep their own scope: the
|
|
751
|
+
* Agent tool's REVIVE arm (a revival's lineage would be the REVIVER's tree, which says nothing
|
|
752
|
+
* about the revived row's), and the workflow / team orchestration spawn legs (`run_workflow`,
|
|
753
|
+
* `runTeamDiscussion` — they build their children's internals without this seat). Edits made by
|
|
754
|
+
* those children are therefore NOT reachable from the root session's rewind, and a restore request
|
|
755
|
+
* on one of them is not refused either — it converges that child's own scope.
|
|
756
|
+
*/
|
|
757
|
+
fileHistoryLineage?: {
|
|
758
|
+
scope: string;
|
|
759
|
+
root: string;
|
|
760
|
+
fs: string;
|
|
761
|
+
};
|
|
736
762
|
/**
|
|
737
763
|
* design/319 (A ticket) — the RUNNING task's reminder provenance mark, Runner-filled, read-only,
|
|
738
764
|
* NEVER a model/tool argument. The Agent tool's FORK route threads it into the forked child's
|
|
@@ -1299,6 +1325,35 @@ export type TrackEditResult = {
|
|
|
1299
1325
|
refusal: string;
|
|
1300
1326
|
};
|
|
1301
1327
|
export type TrackFileEditHook = (req: TrackEditRequest) => Promise<TrackEditResult>;
|
|
1328
|
+
/**
|
|
1329
|
+
* The hands band's MUTATION-EDITED observation seat — the counterpart of {@link TrackEditRequest},
|
|
1330
|
+
* fired at the other end of the same lane. Called once per Write/Edit/NotebookEdit call whose FINAL
|
|
1331
|
+
* env write did not PROVABLY write nothing, at the single point every mutation lane funnels through,
|
|
1332
|
+
* under the same three-valued law as the first-touch record's own retraction:
|
|
1333
|
+
* - a call the argument checks, the read-before-edit gate, the write gate or the first-touch
|
|
1334
|
+
* history seat refused never fires it (nothing was written);
|
|
1335
|
+
* - a call whose write FAILED with a code whose contract says nothing was written never fires it;
|
|
1336
|
+
* - a call whose write failed AMBIGUOUSLY (a non-atomic env that truncated and then errored) or
|
|
1337
|
+
* THREW fires it — exactly the arm where the retraction KEEPS the first-touch record because the
|
|
1338
|
+
* file may really have changed. The two seats agree on purpose: a file whose baseline was kept
|
|
1339
|
+
* for a possible modification must also be listed as possibly modified;
|
|
1340
|
+
* - a `Bash` command that changed a file never fires it: bash does not run through this band's
|
|
1341
|
+
* write lanes at all, which is the whole reason this seat is not a tool-name table.
|
|
1342
|
+
* PURE OBSERVATION: it returns nothing and it cannot refuse. A FAULT in it is contained in both
|
|
1343
|
+
* shapes an observer can fail in — a synchronous throw, and (the return type is `void`, but an
|
|
1344
|
+
* `async` function still type-checks there) a rejected promise, which is sunk rather than left
|
|
1345
|
+
* unhandled. Neither is awaited: an observer must never turn a landed write into a failed tool
|
|
1346
|
+
* answer, nor delay one.
|
|
1347
|
+
*/
|
|
1348
|
+
export interface FileEditedNotice {
|
|
1349
|
+
tool: "Write" | "Edit" | "NotebookEdit";
|
|
1350
|
+
/** The model-supplied path argument — the SAME coordinate the delegated-child projection's
|
|
1351
|
+
* `SubagentEditedFile.path` carries, so the two seats can be read side by side. */
|
|
1352
|
+
path: string;
|
|
1353
|
+
/** The resolved canonical containment key the bytes actually landed on. */
|
|
1354
|
+
key: string;
|
|
1355
|
+
}
|
|
1356
|
+
export type FileEditedHook = (notice: FileEditedNotice) => void;
|
|
1302
1357
|
/**
|
|
1303
1358
|
* design/141 件2 — the SAFE deployment-configurable subset of the hands toolkit ({@link RunnerDeps.hands}).
|
|
1304
1359
|
* Only fields whose injection is purely additive for a deployment are here; Runner-internal orchestration
|
|
@@ -2130,6 +2185,15 @@ export interface TaskSpec {
|
|
|
2130
2185
|
* target with no history boundary fails loud (`rewind_snapshot.unresolvable`). Requires
|
|
2131
2186
|
* `RunnerDeps.fileHistoryStore`. The session leaf is untouched — the next turn continues the
|
|
2132
2187
|
* CURRENT conversation, only the files moved.
|
|
2188
|
+
*
|
|
2189
|
+
* A same-process child THE AGENT TOOL SPAWNED FRESH (the legs that are handed a lineage — see
|
|
2190
|
+
* `ToolExecuteContext.fileHistoryLineage` for the exact set, and for the legs that are not) which
|
|
2191
|
+
* shares its root session's tree records its edits into the ROOT session's history, so a rewind
|
|
2192
|
+
* that should cover those edits is the root session's action; a restore request on such a child run
|
|
2193
|
+
* is refused loud (`rewind.child_scope_unsupported`) rather than converging a scope that holds none
|
|
2194
|
+
* of its edits. A delegated child that was handed NO lineage — a revival, a workflow/team member —
|
|
2195
|
+
* keeps its own scope: its edits are outside this target's reach, and its own restore request is an
|
|
2196
|
+
* ordinary one.
|
|
2133
2197
|
*/
|
|
2134
2198
|
rewindFilesTo?: string;
|
|
2135
2199
|
/**
|
|
@@ -3532,7 +3596,7 @@ export interface TaskResult {
|
|
|
3532
3596
|
* ignores the field is unchanged. A rewind that could NOT be delivered as asked is NOT a note — it is a
|
|
3533
3597
|
* terminal failure (`rewind_snapshot.unresolvable` / `rewind.store_unconfigured` /
|
|
3534
3598
|
* `rewind.restore_failed` / `rewind.invalid_spec` / `rewind.rewind_files_retired` /
|
|
3535
|
-
* `rewind.conflicting_targets`).
|
|
3599
|
+
* `rewind.conflicting_targets` / `rewind.child_scope_unsupported`).
|
|
3536
3600
|
*
|
|
3537
3601
|
* - `conversation_only` — {@link TaskSpec.resumeAt} branched the transcript WITHOUT
|
|
3538
3602
|
* {@link TaskSpec.restoreFiles}, so the working tree was deliberately left where it was (CC's
|
|
@@ -3555,6 +3619,56 @@ export interface TaskResult {
|
|
|
3555
3619
|
/** Human-readable statement of what did NOT happen and why — safe to show a user verbatim. */
|
|
3556
3620
|
message: string;
|
|
3557
3621
|
}>;
|
|
3622
|
+
/**
|
|
3623
|
+
* The files THIS RUN's hands actually mutated, with how many times each — the same
|
|
3624
|
+
* `{path, edits}` shape the delegated-child projection publishes as
|
|
3625
|
+
* {@link import("../agents/subagent-steps.js").SubagentEditedFile}, so a host can read the two
|
|
3626
|
+
* seats side by side. A run is one host-side user message, which is the granularity a
|
|
3627
|
+
* "restore the code this message changed" affordance needs; the per-model-turn beat
|
|
3628
|
+
* (`turn_end`) deliberately has no such seat.
|
|
3629
|
+
*
|
|
3630
|
+
* **Source**: the hands band's own mutation lane. One entry is bumped when a `Write`/`Edit`/
|
|
3631
|
+
* `NotebookEdit` call's FINAL env write returns success — not when a tool call starts, not from a
|
|
3632
|
+
* tool-name table over the event stream. The consequences are the contract:
|
|
3633
|
+
* - a refused, gated or FAILED write is not counted (a write whose bytes never landed is not an
|
|
3634
|
+
* edit — the same lane point that retracts its first-touch history record);
|
|
3635
|
+
* - a `Bash` command that changed a file is not counted: it does not go through those lanes.
|
|
3636
|
+
* This matches the reference implementation's own trigger set;
|
|
3637
|
+
* - one tool CALL counts once, so a batch `Edit` (multiple `edits[]` against one file in one
|
|
3638
|
+
* call) is one edit here, exactly as the child projection counts it.
|
|
3639
|
+
*
|
|
3640
|
+
* **`path` form**: the model-supplied path argument, verbatim — the same coordinate
|
|
3641
|
+
* `SubagentEditedFile.path` uses, deliberately not the canonical containment key, so both seats
|
|
3642
|
+
* spell a file the way the transcript does. IDENTITY, however, is the canonical FILE, not the
|
|
3643
|
+
* spelling: a relative argument resolves against the run's LIVE cwd, so one spelling can name two
|
|
3644
|
+
* different files across a `cd` (two entries, each showing that spelling) and two spellings can
|
|
3645
|
+
* name one file (ONE entry, showing the first spelling that reached it). Insertion order = order
|
|
3646
|
+
* of first edit.
|
|
3647
|
+
*
|
|
3648
|
+
* **Three-valued on the failure edge, upper bound never lower**: a write failure is three-valued.
|
|
3649
|
+
* A failure whose error code's contract states nothing was written is a proven no-op and is NOT
|
|
3650
|
+
* counted. An AMBIGUOUS failure or a thrown write IS counted: an `ExecutionEnv` whose write is not
|
|
3651
|
+
* atomic can truncate a file and then report an error, that file may really have changed, and the
|
|
3652
|
+
* first-touch history record for it is kept for exactly that reason — so this list names it too,
|
|
3653
|
+
* and a `rewindFilesTo` will restore it. (The reference env replaces whole files by stage+rename —
|
|
3654
|
+
* fully old or fully new — so this edge belongs to its in-place fallback arms and to third-party
|
|
3655
|
+
* envs.) A host therefore reads this list as "files that may differ from before this run", never
|
|
3656
|
+
* as "writes the model saw succeed"; the tool answers carry that.
|
|
3657
|
+
*
|
|
3658
|
+
* **In-presence condition**: present iff this run's hands landed ≥1 such write — zero edits means
|
|
3659
|
+
* the key is ABSENT, never an empty array. It does NOT depend on whether a
|
|
3660
|
+
* `RunnerDeps.fileHistoryStore` is wired: this is an observation of what this run did, not a
|
|
3661
|
+
* durable history, and a deployment with no history store still gets it (what a store adds is the
|
|
3662
|
+
* ability to REWIND, not the ability to say what changed). It covers this run's OWN band only — a
|
|
3663
|
+
* delegated child's writes ride the child's own result and the delegation projection, not this
|
|
3664
|
+
* key.
|
|
3665
|
+
*
|
|
3666
|
+
* **Bound**: at most 1000 distinct files. Past that, already-listed files keep counting and new
|
|
3667
|
+
* ones are not added — a result seat cannot be unbounded, and a single user message reaching a
|
|
3668
|
+
* thousand distinct files is already outside what this observation is for. A consumer that must
|
|
3669
|
+
* know whether it is reading a saturated list can compare the length against that ceiling.
|
|
3670
|
+
*/
|
|
3671
|
+
editedFiles?: import("../agents/subagent-steps.js").SubagentEditedFile[];
|
|
3558
3672
|
/**
|
|
3559
3673
|
* Present (`true`) exactly when the run's FINAL turn was halted by a person's BARE rejection of a
|
|
3560
3674
|
* tool call — the parent-thread control-flow boundary: the rejected call's same-message siblings
|
|
@@ -5954,6 +6068,20 @@ export interface EngineNotice {
|
|
|
5954
6068
|
* (whoever answered the card is the one entitled to hear the answer ran nothing); a host may
|
|
5955
6069
|
* forward it on its own wire.
|
|
5956
6070
|
*
|
|
6071
|
+
* - `"classifier.denial_limit"` (#548, CC 2.1.250 `FO`/"too many classifier denials in headless
|
|
6072
|
+
* mode") — the auto-mode classifier's DENIAL LIMIT was reached (3 consecutive blocks, or 20 in the
|
|
6073
|
+
* run; `RunnerDeps.autoMode.denialLimit`) and the fallback ask it turns into had NO approver to go
|
|
6074
|
+
* to: none wired, a blanket `onAsk:"allow"` (refused for a real-approval ask), or an approver that
|
|
6075
|
+
* reported unavailable with no durable park to take it. The run was STOPPED — `TaskResult.status`
|
|
6076
|
+
* `"failed"`, `errorCode` the same code, `errorMessage` the limit sentence — rather than kept
|
|
6077
|
+
* spending on a model the classifier denies without end; the triggering call's own result is the
|
|
6078
|
+
* deny that stood. Once per run (the first headless fallback owns the terminal);
|
|
6079
|
+
* `detail: { sessionId, runId, toolName, toolCallId, consecutive, total, limit }` — `limit` is
|
|
6080
|
+
* `"consecutive"` | `"total"`, the bound that tripped. Audience `"user"` (the person whose run
|
|
6081
|
+
* ended is the one entitled to hear why, and to review the transcript the sentence points at).
|
|
6082
|
+
* With an approver wired the same bound mints NO notice — the fallback ask reaches the person
|
|
6083
|
+
* instead (`AskRequest.denialLimitFallback`).
|
|
6084
|
+
*
|
|
5957
6085
|
* - `"steering.parked_input_blocked"` (design/373 §4.3) — a PARKED steer entry was withheld when
|
|
5958
6086
|
* a resume redelivered it (any resume kind that drains parked steers — wake included) by the
|
|
5959
6087
|
* deployment's `userPromptSubmit` screen (block verdict, or a fail-closed non-answer/crash):
|
|
@@ -6125,23 +6253,61 @@ export interface EngineNotice {
|
|
|
6125
6253
|
* disposition table with the error dispositions. The loser MUST NOT retry into the winner's
|
|
6126
6254
|
* account. A consumer diffing its own table against the catalog must not add a row for it.
|
|
6127
6255
|
*
|
|
6256
|
+
* - `"config.peer_admission_out_of_range"` (#551) — a `RunnerDeps.peerAdmission` field the
|
|
6257
|
+
* resolver could not use: outside its legal range, or not a finite number at all (a string off
|
|
6258
|
+
* an untyped host's wiring, `NaN`, `Infinity`). THAT FIELD falls back to its own default (never
|
|
6259
|
+
* a clamp to the nearest edge, never a whole-config reject) and every other field is unaffected.
|
|
6260
|
+
* One notice PER FIELD — two bad fields in one config are two notices — deduped per SINK on
|
|
6261
|
+
* `(field, given)` rather than per call (the resolver runs on every SendMessage and every drain
|
|
6262
|
+
* round; the fact is about the wiring, not about the call). Per SINK, not per process: two
|
|
6263
|
+
* Runners in one process each own an `onNotice`, and a process-global ledger would deliver the
|
|
6264
|
+
* fact to whichever resolved first and leave the other silently without its own disclosure. The
|
|
6265
|
+
* unwired arm (no `onNotice`: the shared `console.warn` throat) latches per process instead —
|
|
6266
|
+
* the console IS one process-wide channel. `detail: { field, given, default,
|
|
6267
|
+
* range: [lo, hi], reason }`, where `reason` is `"out_of_range" | "not_a_finite_number"`.
|
|
6268
|
+
* Audience `"operator"` (a configuration fact; the fix is the deployment's).
|
|
6128
6269
|
* - `"config.peer_lane_unmounted"` (design/385 §1.2⑥) — `RunnerDeps.peerDirectory` is wired but
|
|
6129
6270
|
* the cross-session lane could not mount on this leg: no `mailboxStore`, or one that does not
|
|
6130
6271
|
* declare `crossProcessSafe: true` (several terminals would share one session box on luck). The
|
|
6131
6272
|
* seat is inert for the run (no ListAgents, peer addresses refuse with the same reason). Once per
|
|
6132
6273
|
* prepared leg; `detail: { reason, code, mailboxWired, sessionId, runId }`. Audience `"operator"`
|
|
6133
6274
|
* (a wiring fact; the fix is the deployment's).
|
|
6134
|
-
* - `"peer.inbound_disposition"` (design/385 §1.2④ / §4.6) — a message parked in THIS
|
|
6135
|
-
* own box was settled at the drain WITHOUT reaching the model. `detail.disposition` is
|
|
6136
|
-
* `"refused"` (this session's `crossSessionInbound`
|
|
6137
|
-
* cannot be rendered canonically), `"admission_refused"`
|
|
6138
|
-
* duplicate / hop loop / runaway — `cause` names it),
|
|
6139
|
-
*
|
|
6140
|
-
*
|
|
6141
|
-
*
|
|
6142
|
-
*
|
|
6275
|
+
* - `"peer.inbound_disposition"` (design/385 §1.2④ / §4.6 / §4.4) — a message parked in THIS
|
|
6276
|
+
* session's own box was settled at the drain WITHOUT reaching the model. `detail.disposition` is
|
|
6277
|
+
* the arm (closed set `PeerInboundDisposition`): `"refused"` (this session's `crossSessionInbound`
|
|
6278
|
+
* setting, or a record whose typed fields cannot be rendered canonically), `"admission_refused"`
|
|
6279
|
+
* (the drain-stage admission re-check: duplicate / hop loop / runaway — `cause` names it),
|
|
6280
|
+
* `"notice_unrouted"` (a notice-kind record the notice face cannot render — no typed `notice`, or
|
|
6281
|
+
* a state that does not fit its kind — settled without delivery, never ridden through the
|
|
6282
|
+
* peer-message envelope), `"notice_user_only"` (a delivery receipt / idle notice whose sender this
|
|
6283
|
+
* session's parity would HOLD: surfaced here for the user, not read by the model — CC's
|
|
6284
|
+
* `modelVisible:false` arm; `cause` carries the rendered notice text), or `"held"` — the parity
|
|
6285
|
+
* judgment held it: the message ENTERED this session's process-level held queue and was acked
|
|
6286
|
+
* from the box (later messages are not blocked behind it), where it is re-judged at every
|
|
6287
|
+
* boundary, released or refused with a receipt, and expires WITH a receipt under `dialogExpiry`
|
|
6288
|
+
* (see `peer.held_settled`); `cause` is a `PeerInboundHoldCause` — the parity causes, or the
|
|
6289
|
+
* drain gate's TRANSIENT `rate_limited` (that one does NOT enter the queue: the message stays in
|
|
6290
|
+
* the box and the drain stops at its seq until the bucket refills). Disclosed once per seq per leg,
|
|
6291
|
+
* and again when a re-judgment moves a held entry's cause. `detail: { disposition, cause, seq, box,
|
|
6143
6292
|
* fromSession?, sessionId, runId }`. Audience `"user"`: the session's user is the one who did
|
|
6144
6293
|
* not receive it.
|
|
6294
|
+
* - `"peer.held_settled"` (design/385 §4.4, slice 4) — a message left THIS session's held queue.
|
|
6295
|
+
* `detail.settlement` names how: `"delivered"` (a re-judgment or an approval released it into
|
|
6296
|
+
* this session's model context), `"refused"` (the setting moved to refuse, or an approval met a
|
|
6297
|
+
* refusing policy), `"expired"` (the `dialogExpiry` deadline, an eviction when the 100-entry queue
|
|
6298
|
+
* was full, a cancelled review, or a graceful shutdown), `"denied"` (the review face), `"dropped"`
|
|
6299
|
+
* (the injection lane refused the released frame). `detail.reason` is the trigger
|
|
6300
|
+
* (`deadline | evicted | shutdown | rejudge | approved | denied | cancelled | inject_failed`); the
|
|
6301
|
+
* sender was sent the matching receipt whenever the record carried a reply address.
|
|
6302
|
+
* `detail: { settlement, reason, seq, box, fromSession?, heldCount, sessionId, runId }`. Audience
|
|
6303
|
+
* `"user"`: it is this session's user who held (or was holding) the message.
|
|
6304
|
+
* - `"peer.idle_subscription"` (design/385 §5.2, slice 4) — a peer session asked to be told when
|
|
6305
|
+
* THIS session next goes idle (`SendMessage … notify_when_idle`). `detail.outcome`: `"recorded"`
|
|
6306
|
+
* / `"refreshed"` (a same-requester re-ask; the subscription is one-shot and expires unfired after
|
|
6307
|
+
* 12 h), `"full"` (the 32-entry table is full) or `"refused"` (this session's `crossSessionInbound`
|
|
6308
|
+
* is `refuse`) — the last two answered the requester with an `unavailable` notice. `detail:
|
|
6309
|
+
* { outcome, fromSession, live, seq, box, sessionId, runId }`. Audience `"user"`: the person whose
|
|
6310
|
+
* session is being watched is the one entitled to know (CC announces it in the session UI).
|
|
6145
6311
|
*
|
|
6146
6312
|
* - `"delegation.transcript_integrity"` (subagent transcript persistence) — a durable agent row
|
|
6147
6313
|
* with a BOUND transcript sessionId met a session store that attests `not_found` for it: the
|
|
@@ -6891,6 +7057,25 @@ export interface RunnerDeps {
|
|
|
6891
7057
|
consecutiveFailures: number;
|
|
6892
7058
|
lastCause: string;
|
|
6893
7059
|
}) => void;
|
|
7060
|
+
/**
|
|
7061
|
+
* The classifier DENIAL LIMIT (CC 2.1.250 `FO`/`AKe`): a run whose classifier keeps blocking falls
|
|
7062
|
+
* back to a PERSON instead of being denied without end. Per run: a `block` first increments the
|
|
7063
|
+
* consecutive and total counts and then judges `consecutive >= maxConsecutive || total >= maxTotal`
|
|
7064
|
+
* — the block that reaches a bound is itself the one that becomes an `ask` (the 3rd consecutive
|
|
7065
|
+
* block asks). That ask carries `requiresRealApproval: true` (no automatic lane may clear it — not
|
|
7066
|
+
* a sandbox admission, not an inherited resolver, not a blanket `onAsk:"allow"`) plus the additive
|
|
7067
|
+
* `denialLimitFallback` member with the counts and its own auto-deny window; an unanswered ask
|
|
7068
|
+
* auto-denies after `autoDenyAfterMs` (default 120s; `0` = no window). A classifier allow, or a
|
|
7069
|
+
* person's allow of the fallback ask, zeroes the consecutive count; reaching the total bound
|
|
7070
|
+
* zeroes everything. With no approver wired at all (headless), the fallback has nowhere to go and
|
|
7071
|
+
* the run STOPS with `TaskResult.errorCode = "classifier.denial_limit"` (a notice of the same
|
|
7072
|
+
* code is minted).
|
|
7073
|
+
*
|
|
7074
|
+
* Every member optional (defaults 3 / 20 / 120_000). A present member with a bad value is refused
|
|
7075
|
+
* loudly at prepare — never clamped, never silently read as the default.
|
|
7076
|
+
* See {@link import("./auto-mode.js").AutoModeDenialLimitOptions}.
|
|
7077
|
+
*/
|
|
7078
|
+
denialLimit?: import("./auto-mode.js").AutoModeDenialLimitOptions;
|
|
6894
7079
|
/**
|
|
6895
7080
|
* #503 — OPT IN to recording this arming's serializable criteria (an
|
|
6896
7081
|
* {@link import("./auto-mode-arming.js").AutoModeArmingRecipe}) on the constraint-chain entries a
|
|
@@ -7051,6 +7236,18 @@ export interface RunnerDeps {
|
|
|
7051
7236
|
* everywhere (mode parity, the CC default). Inert without {@link peerDirectory}.
|
|
7052
7237
|
*/
|
|
7053
7238
|
crossSessionInbound?: import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers | (() => import("../agents/cross-session-judge.js").CrossSessionInboundSettingLayers);
|
|
7239
|
+
/**
|
|
7240
|
+
* design/385 §4.4 (slice 4) — this deployment's `dialogExpiry` (CC settings key, verbatim vocabulary
|
|
7241
|
+
* `"60s" | "5m" | "10m" | "never"`, default `"5m"`): how long a HELD cross-session message whose cause
|
|
7242
|
+
* a human review could resolve (mode-mismatch / no-mode-asserted / invalid attestation) waits in this
|
|
7243
|
+
* session's held queue before it resolves to its safe no-action default — EXPIRED, dropped WITH a
|
|
7244
|
+
* receipt to the sender, never silently. `"never"` disables the deadline. Read at every drain round
|
|
7245
|
+
* (getter form for a host that re-reads its settings). A value outside the vocabulary is announced
|
|
7246
|
+
* ONCE per leg through `onError` (`classification: "peer-dialog-expiry"`) and the default applies —
|
|
7247
|
+
* a garbage setting never silently reads as a policy. Absent = the default. Inert without
|
|
7248
|
+
* {@link peerDirectory}.
|
|
7249
|
+
*/
|
|
7250
|
+
crossSessionDialogExpiry?: import("../agents/peer-notices.js").CrossSessionDialogExpiry | (() => import("../agents/peer-notices.js").CrossSessionDialogExpiry | undefined);
|
|
7054
7251
|
/**
|
|
7055
7252
|
* design/164 件五 — DEPLOYMENT-level usage governance: allowances that span TASKS, evaluated per
|
|
7056
7253
|
* principal (or once for the whole deployment when a task declares none). A different axis from
|
package/dist/index.d.ts
CHANGED
|
@@ -125,7 +125,7 @@ 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";
|
|
@@ -284,7 +289,7 @@ export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS, type RouteAdjudicationVector } f
|
|
|
284
289
|
export { type BrainTimeoutConfig } from "./brain/timeout.js";
|
|
285
290
|
export { createAssistantMessageEventStream } from "./internal/llm.js";
|
|
286
291
|
export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
|
|
287
|
-
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, ToolInputVerdict, ToolInputValidationContext, 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";
|
|
288
293
|
export { Type } from "typebox";
|
|
289
294
|
export type { TSchema, Static } from "typebox";
|
|
290
295
|
export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";
|
package/dist/index.js
CHANGED
|
@@ -102,7 +102,7 @@ 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";
|
|
@@ -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" },
|
|
@@ -151,7 +170,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
151
170
|
const tracked = await trackToolEdit(trackEdit, "Edit", path, r.key, ctx.signal);
|
|
152
171
|
if (tracked.refusal !== undefined)
|
|
153
172
|
return errorResult(tracked.refusal);
|
|
154
|
-
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 } });
|
|
155
174
|
if (!write.ok) {
|
|
156
175
|
if (write.error.code === "already_exists") {
|
|
157
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).`);
|
|
@@ -205,7 +224,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
205
224
|
if (trackedOverwrite.refusal !== undefined)
|
|
206
225
|
return errorResult(trackedOverwrite.refusal);
|
|
207
226
|
const encodedOverwrite = encodeTextForFile(newContent, preDec.encoding, preDec.endings);
|
|
208
|
-
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 } });
|
|
209
228
|
if (!write.ok)
|
|
210
229
|
return errorResult(`Error (Edit): cannot write "${path}": ${write.error.message}`);
|
|
211
230
|
const persistedOverwrite = persistedTextOf(encodedOverwrite);
|
|
@@ -282,7 +301,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
282
301
|
if (trackedEdit.refusal !== undefined)
|
|
283
302
|
return errorResult(trackedEdit.refusal);
|
|
284
303
|
const encodedEdit = encodeTextForFile(working, decoded.encoding, decoded.endings);
|
|
285
|
-
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 } });
|
|
286
305
|
if (!write.ok)
|
|
287
306
|
return errorResult(`Error (Edit): cannot write "${path}": ${write.error.message}`);
|
|
288
307
|
const persistedEdit = persistedTextOf(encodedEdit);
|
|
@@ -305,7 +324,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
305
324
|
},
|
|
306
325
|
});
|
|
307
326
|
}
|
|
308
|
-
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit) {
|
|
327
|
+
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
309
328
|
return defineTool({
|
|
310
329
|
name: "Write",
|
|
311
330
|
contract: { contractId: "core.write@1", implementationRevision: "1" },
|
|
@@ -373,7 +392,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
373
392
|
if (trackedWrite.refusal !== undefined)
|
|
374
393
|
return errorResult(trackedWrite.refusal);
|
|
375
394
|
const encodedWrite = encodeTextForFile(content, decodedPrev.encoding, "preserve");
|
|
376
|
-
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 } });
|
|
377
396
|
if (!write.ok)
|
|
378
397
|
return errorResult(`Error (Write): cannot write "${path}": ${write.error.message}`);
|
|
379
398
|
const persistedWrite = persistedTextOf(encodedWrite);
|
|
@@ -389,7 +408,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
389
408
|
const trackedCreate = await trackToolEdit(trackEdit, "Write", path, r.key, ctx.signal);
|
|
390
409
|
if (trackedCreate.refusal !== undefined)
|
|
391
410
|
return errorResult(trackedCreate.refusal);
|
|
392
|
-
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 } });
|
|
393
412
|
if (!write.ok)
|
|
394
413
|
return errorResult(`Error (Write): cannot write "${path}": ${write.error.message}`);
|
|
395
414
|
const totalLines = countLines(content);
|
|
@@ -405,7 +424,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
405
424
|
const notebookNotIpynbMessage = (notebookPath) => `Error (NotebookEdit): "${notebookPath}" is not a .ipynb file; use Edit for other file types.`;
|
|
406
425
|
const NOTEBOOK_CELL_TYPE_REQUIRED_MESSAGE = `Error (NotebookEdit): Cell type is required when using edit_mode=insert.`;
|
|
407
426
|
const NOTEBOOK_CELL_ID_REQUIRED_MESSAGE = `Error (NotebookEdit): cell_id is required for replace/delete.`;
|
|
408
|
-
export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit) {
|
|
427
|
+
export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
409
428
|
return defineTool({
|
|
410
429
|
name: "NotebookEdit",
|
|
411
430
|
contract: { contractId: "core.notebook_edit@1", implementationRevision: "1" },
|
|
@@ -543,7 +562,7 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
|
|
|
543
562
|
const trackedNb = await trackToolEdit(trackEdit, "NotebookEdit", notebook_path, r.key, ctx.signal);
|
|
544
563
|
if (trackedNb.refusal !== undefined)
|
|
545
564
|
return errorResult(trackedNb.refusal);
|
|
546
|
-
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 } });
|
|
547
566
|
if (!w.ok)
|
|
548
567
|
return errorResult(`Error (NotebookEdit): cannot write "${notebook_path}": ${w.error.message}`);
|
|
549
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) {
|