@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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence } from "../auto-mode.js";
|
|
2
|
+
import { deliverEngineNotice } from "../types.js";
|
|
3
|
+
import { inlineUntrusted } from "../untrusted-text.js";
|
|
4
|
+
export function attachRebuiltDenialTrackers(entries, denialLimit) {
|
|
5
|
+
const rebuiltTrackers = new Map();
|
|
6
|
+
const trackerForRebuilt = (decider) => {
|
|
7
|
+
let t = rebuiltTrackers.get(decider);
|
|
8
|
+
if (t === undefined) {
|
|
9
|
+
t = createAutoModeDenialTracker(denialLimit);
|
|
10
|
+
rebuiltTrackers.set(decider, t);
|
|
11
|
+
}
|
|
12
|
+
return t;
|
|
13
|
+
};
|
|
14
|
+
return entries?.map((pc) => pc.autoMode !== undefined && pc.autoMode.denialTracking === undefined
|
|
15
|
+
? { ...pc, autoMode: { ...pc.autoMode, denialTracking: trackerForRebuilt(pc.autoMode.decider) } }
|
|
16
|
+
: pc);
|
|
17
|
+
}
|
|
18
|
+
export function createDenialLimitStop(opts) {
|
|
19
|
+
const gateStopRef = {};
|
|
20
|
+
const stopForDenialLimit = (info) => {
|
|
21
|
+
if (gateStopRef.terminal !== undefined)
|
|
22
|
+
return;
|
|
23
|
+
const message = `too many classifier denials in headless mode — ${denialLimitSentence(info.fallback)} The run was stopped: ` +
|
|
24
|
+
`the auto-mode classifier's denial limit falls back to a person, and no approver is wired to fall back to ` +
|
|
25
|
+
`(latest blocked action: "${info.toolName}").`;
|
|
26
|
+
const terminal = Object.assign(new Error(message), { code: "classifier.denial_limit" });
|
|
27
|
+
gateStopRef.terminal = terminal;
|
|
28
|
+
deliverEngineNotice(opts.onNotice, {
|
|
29
|
+
code: "classifier.denial_limit",
|
|
30
|
+
message,
|
|
31
|
+
detail: { sessionId: opts.sessionId, runId: opts.runId, toolName: info.toolName, toolCallId: info.toolCallId, consecutive: info.fallback.consecutive, total: info.fallback.total, limit: info.fallback.limit },
|
|
32
|
+
});
|
|
33
|
+
opts.abort();
|
|
34
|
+
};
|
|
35
|
+
return { gateStopRef, stopForDenialLimit };
|
|
36
|
+
}
|
|
37
|
+
export function frozenClassifierExcluded(d) {
|
|
38
|
+
return d.decisionReason === "hook" || d.matchedAskRule !== undefined || d.denialLimitFallback !== undefined;
|
|
39
|
+
}
|
|
40
|
+
export async function judgeInheritedClassifier(opts) {
|
|
41
|
+
const { autoMode, ask, req } = opts;
|
|
42
|
+
if (autoMode === undefined || frozenClassifierExcluded(ask))
|
|
43
|
+
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false };
|
|
44
|
+
const verdict = await autoMode.decider
|
|
45
|
+
.decide({ req, ...(ask.message !== undefined ? { askMessage: ask.message } : {}) }, opts.signal)
|
|
46
|
+
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
47
|
+
if (verdict.kind === "allow") {
|
|
48
|
+
autoMode.denialTracking?.recordAllow();
|
|
49
|
+
return { kind: "allow" };
|
|
50
|
+
}
|
|
51
|
+
if (verdict.kind !== "block")
|
|
52
|
+
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false };
|
|
53
|
+
const reason = verdict.reason ? inlineUntrusted(verdict.reason) : "";
|
|
54
|
+
const category = verdict.category ? inlineUntrusted(verdict.category) : "";
|
|
55
|
+
const tracked = autoMode.denialTracking?.recordBlock();
|
|
56
|
+
if (tracked?.limitReached !== true) {
|
|
57
|
+
return {
|
|
58
|
+
kind: "deny",
|
|
59
|
+
result: {
|
|
60
|
+
action: "deny",
|
|
61
|
+
message: `auto-mode classifier blocked ${opts.subject} at an inherited ancestor layer${reason ? `: ${reason}` : category ? `: [${category}]` : ""}`,
|
|
62
|
+
decisionReason: "classifier",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const fallback = tracked.fallback;
|
|
67
|
+
return {
|
|
68
|
+
kind: "resolve",
|
|
69
|
+
ask: { ...ask, message: denialLimitFallbackMessage(fallback, reason || category || req.toolName), decisionReason: "classifier", requiresRealApproval: true, denialLimitFallback: fallback },
|
|
70
|
+
fallback,
|
|
71
|
+
mintedHere: true,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function headlessDenyAtFold(r) {
|
|
75
|
+
return r.resolution === "no_approver" || r.resolution === "blanket_allow_refused";
|
|
76
|
+
}
|
|
77
|
+
export function headlessDenyAtRecheck(r) {
|
|
78
|
+
return headlessDenyAtFold(r) || r.resolution === "approver_unavailable" || r.approverUnavailable === true;
|
|
79
|
+
}
|
|
80
|
+
export function settleDenialLimitFallback(opts) {
|
|
81
|
+
const { fallback, resolved } = opts;
|
|
82
|
+
if (fallback === undefined)
|
|
83
|
+
return;
|
|
84
|
+
if (resolved.action === "allow") {
|
|
85
|
+
if (opts.mintedHere)
|
|
86
|
+
opts.tracker?.recordAllow();
|
|
87
|
+
}
|
|
88
|
+
else if (resolved.action === "deny" && opts.headless(resolved)) {
|
|
89
|
+
opts.stop({ toolName: opts.toolName, toolCallId: opts.toolCallId, fallback });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FileEditedHook, TaskResult } from "../types.js";
|
|
2
|
+
/** The ceiling on DISTINCT FILES in a run's edited-file ledger (`TaskResult.editedFiles`). A result
|
|
3
|
+
* seat must be bounded by something other than how long the model keeps going; a single host-side
|
|
4
|
+
* user message that touches a thousand distinct files is already past what that observation is for.
|
|
5
|
+
* Far above the delegated-child projection's own cap, which bounds a NOTIFICATION preview rather
|
|
6
|
+
* than a per-message ledger — different job, different ceiling. The field's contract states this
|
|
7
|
+
* number and the saturation behaviour, so a consumer can tell a full list from a clipped one. */
|
|
8
|
+
export declare const RUN_EDITED_FILES_CAP = 1000;
|
|
9
|
+
/** A run's OWN edited-file ledger — the observation behind `TaskResult.editedFiles`. */
|
|
10
|
+
export interface EditedFilesLedger {
|
|
11
|
+
/** The hands band's post-write seat feeds this (the other end of the same mutation lane the
|
|
12
|
+
* first-touch hook sits at). */
|
|
13
|
+
note: FileEditedHook;
|
|
14
|
+
/** The read face: the ledger as `TaskResult.editedFiles`, or `undefined` when this run's hands
|
|
15
|
+
* landed nothing (the key is ABSENT then, never an empty array). A LIVE reader, read at result
|
|
16
|
+
* assembly — so the throw-path backstop terminal sees the same ledger the ordinary assembly does. */
|
|
17
|
+
snapshot: () => TaskResult["editedFiles"];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build the ledger. Built UNCONDITIONALLY by prepare — unlike the first-touch history hook, which
|
|
21
|
+
* exists only when a history store is wired, this answers "what did this run change", a fact about
|
|
22
|
+
* the run and not about the store. Insertion order = order of first edit; the cap keeps a result seat
|
|
23
|
+
* bounded by something other than the model's persistence (past it, listed paths keep counting and
|
|
24
|
+
* new ones are dropped — see the field's own contract).
|
|
25
|
+
*
|
|
26
|
+
* Identity is the CANONICAL key, display is the model's own spelling. The two differ, and both halves
|
|
27
|
+
* matter: the band resolves a relative argument against the LIVE cwd, so one spelling can name two
|
|
28
|
+
* different files across a `cd` (keying on the spelling would merge them and drop a real file from
|
|
29
|
+
* the ledger), and two spellings can name one file (keying on the spelling would split one file into
|
|
30
|
+
* two rows). The first spelling that reached a given file is what the row shows — the coordinate the
|
|
31
|
+
* transcript used, which is the seat's stated path form.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createEditedFilesLedger(cap?: number): EditedFilesLedger;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const RUN_EDITED_FILES_CAP = 1000;
|
|
2
|
+
export function createEditedFilesLedger(cap = RUN_EDITED_FILES_CAP) {
|
|
3
|
+
const counts = new Map();
|
|
4
|
+
return {
|
|
5
|
+
note: (n) => {
|
|
6
|
+
const seen = counts.get(n.key);
|
|
7
|
+
if (seen !== undefined)
|
|
8
|
+
seen.edits += 1;
|
|
9
|
+
else if (counts.size < cap)
|
|
10
|
+
counts.set(n.key, { path: n.path, edits: 1 });
|
|
11
|
+
},
|
|
12
|
+
snapshot: () => (counts.size > 0 ? [...counts.values()].map(({ path, edits }) => ({ path, edits })) : undefined),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -204,6 +204,11 @@ export interface PrepareHandsReadFaceInput {
|
|
|
204
204
|
/** design/381 — the first-touch history hook prepare built (present iff a fileHistoryStore is
|
|
205
205
|
* wired and the run mounts a real fs env); threaded to the write band's trackFileEdit seat. */
|
|
206
206
|
trackFileEdit?: import("../types.js").TrackFileEditHook;
|
|
207
|
+
/** borrowed — the mutation lane's LANDED observation seat (the accumulator behind
|
|
208
|
+
* `TaskResult.editedFiles`); threaded to the write band's onFileEdited seat. Unlike
|
|
209
|
+
* `trackFileEdit` it does NOT depend on a wired history store: it observes what this run's hands
|
|
210
|
+
* did, so it is present on every run that mounts the band. */
|
|
211
|
+
onFileEdited?: import("../types.js").FileEditedHook;
|
|
207
212
|
}
|
|
208
213
|
/** The phase's outputs (相 API 规则件 four-class form) — ALL settled before the return; the driver
|
|
209
214
|
* binds them as fresh consts (R-5) except the inverted-closure trio and the two shellGated bits,
|
|
@@ -313,6 +313,7 @@ export async function prepareHandsMount(input) {
|
|
|
313
313
|
reminderMark: input.reminderMark,
|
|
314
314
|
reminderDisclosureCounts: input.reminderDisclosureCounts,
|
|
315
315
|
...(input.trackFileEdit !== undefined ? { trackFileEdit: input.trackFileEdit } : {}),
|
|
316
|
+
...(input.onFileEdited !== undefined ? { onFileEdited: input.onFileEdited } : {}),
|
|
316
317
|
includeShell: handsIncludeShell,
|
|
317
318
|
readOnly: handsReadOnly,
|
|
318
319
|
...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AgentHarness, type ThinkingLevel } from "../../internal/harness.js";
|
|
2
2
|
import type { Model } from "../../internal/llm.js";
|
|
3
3
|
import { type CompactionForkContext } from "../auto-compaction.js";
|
|
4
|
-
import { type AutoModeDecider } from "../auto-mode.js";
|
|
4
|
+
import { type AutoModeDecider, type AutoModeDenialTracker } from "../auto-mode.js";
|
|
5
5
|
import { type AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
6
6
|
import { type MaterializedMcp } from "../mcp.js";
|
|
7
7
|
import { type MaterializedA2a } from "../a2a.js";
|
|
@@ -214,6 +214,24 @@ export interface FileHistoryBoundarySeat {
|
|
|
214
214
|
begin(entryId: string): void;
|
|
215
215
|
settle(): Promise<void>;
|
|
216
216
|
}
|
|
217
|
+
/** The ONE reading of {@link RunInternals.fileHistoryLineage} (see {@link resolveFileHistoryCoordinates}):
|
|
218
|
+
* the lineage's scope iff BOTH tree coordinates match; otherwise the run's own session. */
|
|
219
|
+
export declare function resolveFileHistoryScope(lineage: RunInternals["fileHistoryLineage"], historyRoot: string, historyFs: string, sessionId: string): string;
|
|
220
|
+
/**
|
|
221
|
+
* WHICH filesystem an env is a view of — the second coordinate of tree identity for the file-history
|
|
222
|
+
* lineage, read from what the env's MINTER states rather than inferred from a path:
|
|
223
|
+
* · a remote workspace names itself through its {@link WorkspaceHandle} (provider + sandbox, and the
|
|
224
|
+
* device lane's id when stamped) — two runs on one sandbox share a tree, two sandboxes never do;
|
|
225
|
+
* · an env that DECLARES its paths host-local (`hostLocalPaths: true`), or is the host adapter itself
|
|
226
|
+
* (a {@link NodeExecutionEnv}, which the #211 seam names as the host-local default), is the
|
|
227
|
+
* control-plane host's filesystem — every such env is one tree;
|
|
228
|
+
* · anything else — an env declaring `hostLocalPaths: false`, or an undeclared custom adapter — is
|
|
229
|
+
* attested to nothing, so it is its OWN tree: a per-instance token, which still matches when the
|
|
230
|
+
* child literally holds the parent's env object and never matches a fresh per-task mint.
|
|
231
|
+
* A remote handle that cannot be read (an env not yet connected) falls to the per-instance arm for
|
|
232
|
+
* the same reason: unattested is not shared.
|
|
233
|
+
*/
|
|
234
|
+
export declare function fileHistoryFilesystemIdentity(env: ExecutionEnv): string;
|
|
217
235
|
export interface Prepared {
|
|
218
236
|
harness: AgentHarness;
|
|
219
237
|
/** The CONCRETE built-in session (engine-internal: prepare constructs/acquires `StoredSession` itself,
|
|
@@ -320,6 +338,11 @@ export interface Prepared {
|
|
|
320
338
|
/** RB-430-a: prepare-time rewind disclosures (conversation-only branch / no snapshot backend / no file
|
|
321
339
|
* env), echoed verbatim onto `TaskResult.rewindNotes`. Present only when there is something to say. */
|
|
322
340
|
rewindNotes?: NonNullable<TaskResult["rewindNotes"]>;
|
|
341
|
+
/** The run's edited-file ledger read face — what its OWN hands landed, as `TaskResult.editedFiles`
|
|
342
|
+
* (undefined when nothing landed: the key is absent, never an empty array). A LIVE reader rather
|
|
343
|
+
* than a snapshot, so the throw-path backstop terminal reports the same ledger the ordinary
|
|
344
|
+
* assembly would. Always present on Prepared; independent of whether a fileHistoryStore is wired. */
|
|
345
|
+
editedFilesSnapshot: () => TaskResult["editedFiles"];
|
|
323
346
|
/** design/381 — the run's turn-start boundary seat (present iff a fileHistoryStore is wired and
|
|
324
347
|
* the run mounts a real fs env). runtask calls begin() at the first committed user entry and
|
|
325
348
|
* awaits settle() at the lease close + the finish tail. */
|
|
@@ -725,6 +748,19 @@ export interface Prepared {
|
|
|
725
748
|
* the task the typed terminal — the harness turns a loop throw into an error assistant message, so
|
|
726
749
|
* without this the cause would reach the caller only as the generic `provider.error`. */
|
|
727
750
|
brainCallGuardrailRef: BrainCallGuardrailRef;
|
|
751
|
+
/**
|
|
752
|
+
* #548 — the tool gate's own TYPED STOP: set (once) when the classifier denial limit was reached with
|
|
753
|
+
* no approver to fall back to (headless), together with the run abort. The run loop adopts it as the
|
|
754
|
+
* terminal `threw` (`TaskResult.errorCode` = the error's `code`, `errorMessage` = its sentence) the
|
|
755
|
+
* same way it adopts the brain-call guardrail's — a loop that ended because THIS lane aborted it must
|
|
756
|
+
* report the cause, not the consequence. The abort-result details seam reads it too, so the aborted
|
|
757
|
+
* call's own `tool_end` carries the code. `undefined` ⇒ no gate stop happened.
|
|
758
|
+
*/
|
|
759
|
+
gateStopRef: {
|
|
760
|
+
terminal?: Error & {
|
|
761
|
+
code: string;
|
|
762
|
+
};
|
|
763
|
+
};
|
|
728
764
|
/** design/80 D-B — set by a tool calling `ctx.requestReview()` (the first-party `present_plan` tool, CC
|
|
729
765
|
* ExitPlanMode parity): the run loop honors it at the next CLEAN turn boundary by minting a `plan_review`
|
|
730
766
|
* checkpoint. `{ pending }` is set (with an optional reason) the moment a tool requests review; the boundary
|
|
@@ -1269,6 +1305,13 @@ export interface InheritedGate {
|
|
|
1269
1305
|
*/
|
|
1270
1306
|
autoMode?: {
|
|
1271
1307
|
decider: AutoModeDecider;
|
|
1308
|
+
/**
|
|
1309
|
+
* #548 — the ancestor's per-run DENIAL-LIMIT tracker, frozen beside its decider (same owner). The
|
|
1310
|
+
* wrapper arms count the frozen classifier's blocks on it and, at a bound, resolve the fallback
|
|
1311
|
+
* ask at the frozen approver instead of denying (`requiresRealApproval` set, sandbox admission
|
|
1312
|
+
* excluded). Live-only, like the decider: a cross-process redemption starts a fresh count.
|
|
1313
|
+
*/
|
|
1314
|
+
denialTracking?: AutoModeDenialTracker;
|
|
1272
1315
|
/**
|
|
1273
1316
|
* #503 — the SERIALIZABLE criteria half of this classifier (assembly inputs + knobs + the
|
|
1274
1317
|
* deployment's settings epoch), present when the arming deployment opted in
|
|
@@ -1801,6 +1844,24 @@ export interface RunInternals {
|
|
|
1801
1844
|
* parent's cwd instead of an empty per-task sandbox. `isolation: "worktree"` wins over this when both set.
|
|
1802
1845
|
*/
|
|
1803
1846
|
parentCwd?: string;
|
|
1847
|
+
/**
|
|
1848
|
+
* The spawning run's file-history LINEAGE — the scope it records first-touch edits into and the
|
|
1849
|
+
* TREE those records' keys are minted against (the canonical root spelling + the filesystem
|
|
1850
|
+
* identity of {@link fileHistoryFilesystemIdentity}) — threaded VERBATIM by core delegation
|
|
1851
|
+
* callers from {@link import("../types.js").ToolExecuteContext.fileHistoryLineage} (NEVER a
|
|
1852
|
+
* {@link TaskSpec} field). {@link resolveFileHistoryScope} is the ONE reading: this run records
|
|
1853
|
+
* into the lineage's scope iff its own tree coordinates BOTH equal the lineage's, and then
|
|
1854
|
+
* re-exposes the SAME triple on its own ctx, so every same-tree descendant of a root session — at
|
|
1855
|
+
* any depth — lands in the root session's scope (the fixed point), while a descendant on another
|
|
1856
|
+
* tree (worktree isolation, explicit `cwd`, a fresh per-task sandbox) becomes the root of its own
|
|
1857
|
+
* subtree's history. Absent on a top-level run, on a run with no live history store, and on a
|
|
1858
|
+
* tier-3 revival (the reviver's lineage says nothing about the revived row's tree).
|
|
1859
|
+
*/
|
|
1860
|
+
fileHistoryLineage?: {
|
|
1861
|
+
scope: string;
|
|
1862
|
+
root: string;
|
|
1863
|
+
fs: string;
|
|
1864
|
+
};
|
|
1804
1865
|
/**
|
|
1805
1866
|
* [c209-D] — the EXPLICIT Agent.cwd request, distinct from the best-effort `parentCwd`
|
|
1806
1867
|
* inheritance hint above: inheritance may be silently ignored by a factory (or absent without one),
|