@sema-agent/core 7.49.5 → 7.50.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 +138 -0
- package/dist/agents/observer.js +3 -10
- package/dist/agents/subagent-steps.d.ts +6 -14
- package/dist/agents/subagent-steps.js +3 -17
- package/dist/brain/stream-shared.d.ts +1 -1
- package/dist/core/allow-entry.d.ts +1 -1
- package/dist/core/allow-entry.js +1 -1
- package/dist/core/ask-origin.d.ts +15 -11
- package/dist/core/ask-origin.js +6 -14
- package/dist/core/auto-mode-prompt.js +6 -2
- package/dist/core/checkpoint-store.d.ts +6 -4
- package/dist/core/checkpoint-store.js +1 -1
- package/dist/core/context-edit.d.ts +2 -2
- package/dist/core/decision-receipt.d.ts +2 -2
- package/dist/core/decision-receipt.js +30 -11
- package/dist/core/delegation-frames.d.ts +66 -18
- package/dist/core/delegation-frames.js +22 -0
- package/dist/core/engine-notice.d.ts +7 -5
- package/dist/core/frame-settlement.d.ts +6 -1
- package/dist/core/gate-fold.js +19 -13
- package/dist/core/gate-lanes.js +12 -11
- package/dist/core/gate-outcome.d.ts +2 -2
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +28 -20
- package/dist/core/human-input-projection.js +2 -0
- package/dist/core/lsp-diagnostics.d.ts +3 -0
- package/dist/core/lsp-diagnostics.js +6 -0
- package/dist/core/lsp-session.d.ts +39 -17
- package/dist/core/lsp-session.js +118 -58
- package/dist/core/lsp.d.ts +83 -3
- package/dist/core/lsp.js +146 -16
- package/dist/core/mcp.js +2 -1
- package/dist/core/media-byte-cap.d.ts +14 -0
- package/dist/core/media-byte-cap.js +15 -3
- package/dist/core/memory-engine/consolidation-driver.d.ts +23 -1
- package/dist/core/memory-engine/consolidation-driver.js +54 -6
- package/dist/core/memory-engine/consolidation-write-gate.d.ts +46 -0
- package/dist/core/memory-engine/consolidation-write-gate.js +82 -0
- package/dist/core/memory-engine/consolidation.d.ts +17 -0
- package/dist/core/memory-engine/consolidation.js +1 -0
- package/dist/core/memory-engine/distiller.d.ts +8 -2
- package/dist/core/memory-engine/distiller.js +8 -6
- package/dist/core/memory-engine/engine.d.ts +37 -4
- package/dist/core/memory-engine/engine.js +127 -38
- package/dist/core/memory-engine/file-backend.d.ts +8 -3
- package/dist/core/memory-engine/file-backend.js +44 -16
- package/dist/core/memory-engine/index.d.ts +2 -2
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/memory-engine/landing-admission.d.ts +65 -0
- package/dist/core/memory-engine/landing-admission.js +68 -0
- package/dist/core/memory-engine/types.d.ts +23 -0
- package/dist/core/mode-policy.d.ts +3 -2
- package/dist/core/mode-policy.js +2 -0
- package/dist/core/permission-rule-model.d.ts +19 -13
- package/dist/core/permission-rule-model.js +7 -4
- package/dist/core/permission-rule-provider.d.ts +11 -0
- package/dist/core/permission-rules.d.ts +1 -1
- package/dist/core/permission-rules.js +5 -2
- package/dist/core/persisted-rule-arms.d.ts +15 -7
- package/dist/core/persisted-rule-arms.js +13 -6
- package/dist/core/reminder-mint.d.ts +5 -0
- package/dist/core/reminder-mint.js +1 -0
- package/dist/core/retired-keys.d.ts +8 -0
- package/dist/core/retired-keys.js +4 -0
- package/dist/core/rule-lane-reads.d.ts +4 -4
- package/dist/core/rule-lane-reads.js +10 -1
- package/dist/core/runner/construction-doors.d.ts +10 -4
- package/dist/core/runner/construction-doors.js +25 -2
- package/dist/core/runner/contracts.d.ts +20 -3
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/gate-exit.js +2 -1
- package/dist/core/runner/memory-consolidation-driver.d.ts +6 -4
- package/dist/core/runner/memory-consolidation-driver.js +5 -2
- package/dist/core/runner/memory-consolidation-write-judge.d.ts +17 -0
- package/dist/core/runner/memory-consolidation-write-judge.js +14 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +16 -22
- package/dist/core/runner/permission-rule-lanes.js +13 -43
- package/dist/core/runner/person-path-rows.d.ts +244 -0
- package/dist/core/runner/person-path-rows.js +387 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +8 -1
- package/dist/core/runner/prepare-artifact.d.ts +5 -3
- package/dist/core/runner/prepare-artifact.js +2 -8
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
- package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
- package/dist/core/runner/prepare-context-lane.js +2 -2
- package/dist/core/runner/prepare-gate-stations.d.ts +3 -3
- package/dist/core/runner/prepare-gate-stations.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +19 -10
- package/dist/core/runner/prepare-hands-readface.js +57 -43
- package/dist/core/runner/prepare-lsp.d.ts +15 -13
- package/dist/core/runner/prepare-lsp.js +11 -11
- package/dist/core/runner/prepare-policy-chain.d.ts +8 -4
- package/dist/core/runner/prepare-policy-chain.js +6 -7
- package/dist/core/runner/prepare-run-refs.js +1 -1
- package/dist/core/runner/prepare-task.js +24 -16
- package/dist/core/runner/prepare-turn-wiring.d.ts +13 -6
- package/dist/core/runner/prepare-turn-wiring.js +19 -8
- package/dist/core/runner/read-root-grant.d.ts +1 -25
- package/dist/core/runner/read-root-grant.js +1 -17
- package/dist/core/runner/retired-runner-deps.d.ts +2 -2
- package/dist/core/runner/run-brain-sinks.d.ts +1 -5
- package/dist/core/runner/run-brain-sinks.js +2 -4
- package/dist/core/runner/run-harness-handlers.js +5 -4
- package/dist/core/runner/run-identity-wiring.d.ts +4 -4
- package/dist/core/runner/run-identity-wiring.js +10 -1
- package/dist/core/runner/run-leg.d.ts +1 -1
- package/dist/core/runner/run-leg.js +22 -9
- package/dist/core/runner/run-settle-and-teardown.d.ts +0 -4
- package/dist/core/runner/run-settle-and-teardown.js +7 -4
- package/dist/core/runner/run-terminal-adoption.d.ts +1 -1
- package/dist/core/runner/run-terminal-adoption.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +2 -2
- package/dist/core/runner/runtask.js +8 -8
- package/dist/core/runner/session-file-state-replay.d.ts +14 -4
- package/dist/core/runner/session-file-state-replay.js +8 -0
- package/dist/core/runner/stream-steer-verb.js +3 -0
- package/dist/core/runner/tool-end-body.d.ts +5 -4
- package/dist/core/runner/tool-end-body.js +4 -4
- package/dist/core/runner/tool-output-projection.d.ts +6 -4
- package/dist/core/runner/tool-output-projection.js +66 -47
- package/dist/core/runner-deps.d.ts +6 -3
- package/dist/core/shared-memory/types.d.ts +2 -2
- package/dist/core/shell-read-silence.d.ts +23 -0
- package/dist/core/shell-read-silence.js +18 -0
- package/dist/core/synthetic-read-verdict.d.ts +80 -21
- package/dist/core/synthetic-read-verdict.js +89 -18
- package/dist/core/task-event.d.ts +113 -25
- package/dist/core/task-spec.d.ts +44 -0
- package/dist/core/task-tool-shape.d.ts +1 -1
- package/dist/core/tool-catalog-entries.js +16 -2
- package/dist/core/tool-policy.d.ts +25 -15
- package/dist/core/tool-policy.js +9 -13
- package/dist/core/trace.d.ts +3 -2
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.js +1 -1
- package/dist/core/untrusted-text.d.ts +2 -2
- package/dist/core/user-attachment.d.ts +164 -0
- package/dist/core/user-attachment.js +437 -0
- package/dist/engine/compaction/compaction.js +10 -3
- package/dist/engine/compaction/utils.js +6 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +19 -1
- package/dist/engine/execution-env/node-execution-env.js +125 -0
- package/dist/engine/harness/agent-harness.d.ts +12 -2
- package/dist/engine/harness/agent-harness.js +15 -3
- package/dist/engine/harness/types.d.ts +21 -0
- package/dist/engine/llm/content-block-render.d.ts +15 -0
- package/dist/engine/llm/content-block-render.js +10 -0
- package/dist/engine/llm/index.d.ts +1 -0
- package/dist/engine/llm/index.js +1 -0
- package/dist/engine/llm/types.d.ts +80 -4
- package/dist/engine/llm/types.js +43 -1
- package/dist/engine/session/import-validate.js +2 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/workflow.d.ts +4 -5
- package/dist/orchestration/workflow.js +2 -2
- package/dist/prompts/default.d.ts +1 -1
- package/dist/tools/artifact/artifact-tool.js +1 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/dir-entries.d.ts +4 -10
- package/dist/tools/fs/dir-entries.js +4 -11
- package/dist/tools/fs/file-read-check.js +3 -3
- package/dist/tools/fs/fs-pdf.js +2 -4
- package/dist/tools/fs/fs-read.d.ts +47 -4
- package/dist/tools/fs/fs-read.js +63 -19
- package/dist/tools/fs/fs-search-tools.d.ts +3 -3
- package/dist/tools/fs/fs-search-tools.js +7 -2
- package/dist/tools/fs/fs-write.d.ts +1 -1
- package/dist/tools/fs/fs-write.js +7 -6
- package/dist/tools/fs/index.d.ts +7 -1
- package/dist/tools/fs/index.js +56 -34
- package/dist/tools/fs/read-deny-home.d.ts +19 -1
- package/dist/tools/fs/read-deny-home.js +32 -22
- package/dist/tools/fs/read-deny.d.ts +14 -0
- package/dist/tools/fs/read-deny.js +8 -0
- package/dist/tools/fs/repo-map.d.ts +3 -2
- package/dist/tools/fs/repo-map.js +11 -3
- package/dist/tools/fs/safety.d.ts +41 -2
- package/dist/tools/fs/safety.js +12 -3
- package/dist/tools/fs/shell-read-check.d.ts +12 -3
- package/dist/tools/fs/shell-read-check.js +28 -15
- package/dist/tools/fs/shell-read-device.d.ts +1 -1
- package/dist/tools/fs/shell-read-judge.d.ts +9 -4
- package/dist/tools/fs/shell-read-judge.js +1 -1
- package/dist/tools/loop-tick.d.ts +2 -2
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +39 -1
- package/wire-changes/core@7.50.0.json +1699 -0
- package/dist/core/runner/persisted-path-rows.d.ts +0 -88
- package/dist/core/runner/persisted-path-rows.js +0 -108
package/dist/core/hooks.d.ts
CHANGED
|
@@ -939,6 +939,14 @@ export interface UserPromptSubmitContext {
|
|
|
939
939
|
* {@link source}, and absence means "anonymous caller", exactly as on the event face.
|
|
940
940
|
*/
|
|
941
941
|
actor?: ActorAssertion;
|
|
942
|
+
/**
|
|
943
|
+
* The files declared on this objective (`TaskSpec.userAttachments`), in order — present only on `source:"objective"`
|
|
944
|
+
* when the objective declares any. A path entry shows its path; a host entry its name and byte count, never its
|
|
945
|
+
* bytes. Blocking the objective reads and delivers none of them. A hook does not govern the engine's reading of an
|
|
946
|
+
* allowed attachment (PreToolUse does not run for it either): a deployment that must keep a path from the model
|
|
947
|
+
* writes a deny rule, which the attachment read honors like the Read tool does.
|
|
948
|
+
*/
|
|
949
|
+
userAttachments?: readonly import("./user-attachment.js").UserAttachmentDeclaration[];
|
|
942
950
|
}
|
|
943
951
|
/** #281 件A — the context of a {@link Hooks.postToolBatch} invocation (third parameter, additive —
|
|
944
952
|
* deliberately NOT folded into `meta`, whose presence already means "the engine injected here"). */
|
|
@@ -1105,35 +1113,35 @@ export interface PersistedRuleHitRule {
|
|
|
1105
1113
|
* deny > ask > allow — already chose it). `rules` is non-empty by contract — an empty set covers nothing
|
|
1106
1114
|
* and is not a hit (the normalizer reads it as a clean negative).
|
|
1107
1115
|
*
|
|
1108
|
-
* Why dots and not an id: a persisted rule HAS no scalar id — it is a (behavior, text, scope) group whose
|
|
1109
|
-
*
|
|
1110
|
-
* each carry their own. The set is the identity; a joined string would not be one.
|
|
1116
|
+
* Why dots and not an id: a persisted rule HAS no scalar id — it is a (behavior, text, scope) group whose identity is
|
|
1117
|
+
* the set of add dots that redeemed it, and two concurrently approved records for one text each carry their own.
|
|
1111
1118
|
*
|
|
1112
|
-
* The pre-375 single-rule object shape (`{ rule, dots? }`) and the pre-behavior `{ rules }` shape are
|
|
1113
|
-
*
|
|
1114
|
-
* never to a match — the safe failure shape for a lane that missed the same-window upgrade.
|
|
1119
|
+
* The pre-375 single-rule object shape (`{ rule, dots? }`) and the pre-behavior `{ rules }` shape are NOT accepted:
|
|
1120
|
+
* on this loosening seam an unrecognized answer degrades to a clean negative (more asks), never to a match.
|
|
1115
1121
|
*/
|
|
1116
1122
|
export interface PersistedRuleHit {
|
|
1117
1123
|
/** What the deciding rule(s) say about the call — the closed set's word. */
|
|
1118
1124
|
readonly behavior: import("./permission-rule-model.js").RuleBehavior;
|
|
1119
1125
|
/** The rule set the verdict stands on. Non-empty. */
|
|
1120
1126
|
readonly rules: readonly PersistedRuleHitRule[];
|
|
1127
|
+
/** A path DENY's coordinates (where the reaching row met the call): the gate's exits mint the refusal's card from it. */
|
|
1128
|
+
readonly denial?: import("../tools/fs/safety.js").PathDenialEvidence;
|
|
1121
1129
|
}
|
|
1122
1130
|
/**
|
|
1123
|
-
* design/252 G-2 — the answer a lane gives when it could not READ its store (no `reason`; the CALL form is `reason`'s).
|
|
1131
|
+
* design/252 G-2 — the answer a lane gives when it could not READ its store, or a partition of it (no `reason`; the CALL form is `reason`'s).
|
|
1124
1132
|
*
|
|
1125
|
-
* The store form
|
|
1126
|
-
* no
|
|
1127
|
-
* it,
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1133
|
+
* The store form fails CLOSED: a deny or ask row inside what could not be read may forbid this call and an allow beside it
|
|
1134
|
+
* is no yes, so the decision becomes a real-approval ask (origin `rule_store_unavailable`, class `governance_unavailable`:
|
|
1135
|
+
* no automatic lane clears it, `bypassPermissions` keeps it for a person, headless it is refused) until a read succeeds
|
|
1136
|
+
* again; a deny or ask row that WAS read still stands. The RECORD says `"unavailable"`, not `"no_match"`; the lane
|
|
1137
|
+
* announces the skipped partition once per run.
|
|
1130
1138
|
*/
|
|
1131
1139
|
export interface PersistedRuleUnreadable {
|
|
1132
1140
|
readonly unreadable: true;
|
|
1133
|
-
/** WHY the lane could not read: absent for a store read failure
|
|
1134
|
-
* not read against the person's standing deny/ask rows (an
|
|
1135
|
-
*
|
|
1136
|
-
* tightens to a real-approval ask of
|
|
1141
|
+
/** WHY the lane could not read: absent for a store (or partition) read failure — the store form, a real-approval ask of
|
|
1142
|
+
* class `governance_unavailable`; for a CALL the lane could not read against the person's standing deny/ask rows (an
|
|
1143
|
+
* expansion where a rule reads a word, an unterminated quote, a syntax error) the lexer's reason — the call form, an allow
|
|
1144
|
+
* tightens to a real-approval ask of class `rule_unreadable`. Both carry the origin `rule_store_unavailable`. */
|
|
1137
1145
|
readonly reason?: string;
|
|
1138
1146
|
}
|
|
1139
1147
|
/**
|
|
@@ -1610,10 +1618,10 @@ export interface ToolGateInput extends PermissionSeatInputs, SkillGateSeats, All
|
|
|
1610
1618
|
persistedRules?: {
|
|
1611
1619
|
/** The lane's verdict for this call — a {@link PersistedRuleHit} naming the behavior that spoke and
|
|
1612
1620
|
* the rule set it stands on, a coverage-carrying clean negative, an unreadable report, or
|
|
1613
|
-
* `undefined` (the store was READ and no rule speaks). Must not throw — and a store that cannot
|
|
1614
|
-
* read answers `{ unreadable: true }`, not `undefined`:
|
|
1615
|
-
*
|
|
1616
|
-
* announcing the skip. A call unreadable against the rows answers `{ unreadable: true, reason }` (
|
|
1621
|
+
* `undefined` (the store was READ and no rule speaks). Must not throw — and a store (or a partition of it) that cannot
|
|
1622
|
+
* be read answers `{ unreadable: true }`, not `undefined`: it fails closed (a row nobody could read may forbid the call;
|
|
1623
|
+
* the decision becomes a real-approval ask) and the evidence records `"unavailable"`, not `"no_match"`; the lane owns
|
|
1624
|
+
* announcing the skip. A call unreadable against the rows answers `{ unreadable: true, reason }` (the call form).
|
|
1617
1625
|
*
|
|
1618
1626
|
* For an ALLOW the set is the COVERAGE SET — each member's canonical text plus its add DOTS. A
|
|
1619
1627
|
* single rule admitting the whole command is a one-element set; a compound admitted by the
|
|
@@ -31,6 +31,7 @@ const HANDLED_HUMAN_INPUT_KEYS = {
|
|
|
31
31
|
delivery: "forwarded",
|
|
32
32
|
principal: "forwarded",
|
|
33
33
|
entryId: "forwarded",
|
|
34
|
+
attachments: "forwarded",
|
|
34
35
|
};
|
|
35
36
|
export function buildHumanInputEvent(input) {
|
|
36
37
|
void HANDLED_HUMAN_INPUT_KEYS;
|
|
@@ -45,6 +46,7 @@ export function buildHumanInputEvent(input) {
|
|
|
45
46
|
...(input.actor !== undefined ? { actor: snapshotActorAssertion(input.actor) } : {}),
|
|
46
47
|
...(input.principal !== undefined ? { principal: input.principal } : {}),
|
|
47
48
|
...(input.entryId !== undefined ? { entryId: input.entryId } : {}),
|
|
49
|
+
...(input.attachments !== undefined && input.attachments.length > 0 ? { attachments: input.attachments.map((a) => ({ ...a })) } : {}),
|
|
48
50
|
};
|
|
49
51
|
}
|
|
50
52
|
export function buildAppliedHumanInputEvent(input) {
|
|
@@ -79,6 +79,9 @@ export declare class LspDiagnosticsRegistry {
|
|
|
79
79
|
*/
|
|
80
80
|
drain(runIdent: string): LspFileDiagnostics[];
|
|
81
81
|
}
|
|
82
|
+
/** A drain with every file the run's read face withholds left out (`withheldOf` answers the batch: a withheld file is in
|
|
83
|
+
* the map; a judgment that fails withholds them all). The run's drain already marked them delivered, so they do not come back. */
|
|
84
|
+
export declare function withholdListedDiagnostics(files: LspFileDiagnostics[], withheldOf: (uris: readonly string[]) => Promise<ReadonlyMap<string, unknown>>): Promise<LspFileDiagnostics[]>;
|
|
82
85
|
/**
|
|
83
86
|
* CC 198 `formatDiagnosticsSummary` (198:320480-320499), verbatim shape:
|
|
84
87
|
* ```
|
|
@@ -74,6 +74,12 @@ export class LspDiagnosticsRegistry {
|
|
|
74
74
|
return out;
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
export async function withholdListedDiagnostics(files, withheldOf) {
|
|
78
|
+
if (files.length === 0)
|
|
79
|
+
return files;
|
|
80
|
+
const withheld = await withheldOf(files.map((f) => f.uri)).catch(() => undefined);
|
|
81
|
+
return withheld === undefined ? [] : files.filter((f) => !withheld.has(f.uri));
|
|
82
|
+
}
|
|
77
83
|
function basenameOfUri(uri) {
|
|
78
84
|
const path = uri.replace(/^file:\/\//, "");
|
|
79
85
|
const idx = path.lastIndexOf("/");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LspSession, LspOperation, LspRequestParams, LspResult, LspTransport, LspReadText } from "./lsp.js";
|
|
1
|
+
import type { LspSession, LspOperation, LspRequestParams, LspResult, LspTransport, LspReadText, LspReadGate } from "./lsp.js";
|
|
2
2
|
import type { LspDiagnosticsRegistry } from "./lsp-diagnostics.js";
|
|
3
3
|
export interface SessionWarmup {
|
|
4
4
|
/** Empty results within this window after session creation are retried (a freshly-spawned server answers
|
|
@@ -24,9 +24,14 @@ export declare class TransportLspSession implements LspSession {
|
|
|
24
24
|
* we ourselves sent removes the round-trip entirely (`uriToPath` stays the fallback for a server that
|
|
25
25
|
* re-spells the URI). */
|
|
26
26
|
private readonly openedUris;
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
|
|
27
|
+
/** Files carried over from a session that died ({@link warmOpen}): not read yet — the next gated sync opens each one
|
|
28
|
+
* its caller's gate admits, before that caller's query, and drops the rest. */
|
|
29
|
+
private readonly carried;
|
|
30
|
+
/** Per file, the generation of the newest sync started for it — the only one that may still publish. */
|
|
31
|
+
private readonly syncGen;
|
|
32
|
+
/** Per file, the newest sync in flight (dropped when it settles); concurrent by-name syncs share ONE read/didOpen.
|
|
33
|
+
* Each entry owns a {@link SharedAbortScope} so ONE caller's abort no longer cancels the read the others are waiting on
|
|
34
|
+
* (HRD-LSP-12). `byName` = a read with no gate (the only kind another caller may share). */
|
|
30
35
|
private readonly syncing;
|
|
31
36
|
private readonly createdAt;
|
|
32
37
|
/** HRD-LSP-11: the warm-up barrier as a shared JOB, not a boolean flag set before the await (which let the
|
|
@@ -47,7 +52,7 @@ export declare class TransportLspSession implements LspSession {
|
|
|
47
52
|
* closing one mid-op turns a healthy answer into `server_terminated` for a caller that did nothing wrong. */
|
|
48
53
|
get inFlight(): number;
|
|
49
54
|
private activeRequests;
|
|
50
|
-
request(op: LspOperation, params: LspRequestParams, signal?: AbortSignal): Promise<LspResult>;
|
|
55
|
+
request(op: LspOperation, params: LspRequestParams, signal?: AbortSignal, gate?: LspReadGate): Promise<LspResult>;
|
|
51
56
|
private requestOnce;
|
|
52
57
|
/**
|
|
53
58
|
* HRD-LSP-11: run (or join) this session's ONE warm-up barrier.
|
|
@@ -60,13 +65,28 @@ export declare class TransportLspSession implements LspSession {
|
|
|
60
65
|
* and one caller's abort neither cancels the shared barrier nor makes the others wait for it.
|
|
61
66
|
*/
|
|
62
67
|
private warmupBarrierOnce;
|
|
63
|
-
/** Re-sync every opened file (+ the queried one) with its CURRENT on-disk text, in parallel
|
|
64
|
-
* QUERIED file's outcome — the one the op's honesty depends on (HRD-LSP-7). */
|
|
68
|
+
/** Re-sync every opened and carried file (+ the queried one) with its CURRENT on-disk text, in parallel, each through
|
|
69
|
+
* the caller's gate. Returns the QUERIED file's outcome — the one the op's honesty depends on (HRD-LSP-7). */
|
|
65
70
|
private syncOpenedFiles;
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The session's one read port. Every sync of a document takes the next generation of that document and publishes only if
|
|
73
|
+
* it is still the newest one started when its read settles — so a sync that publishes after another started after that
|
|
74
|
+
* publication and opened its object after it: an older object's text never lands after a newer one's. No sync ever waits
|
|
75
|
+
* for another's read (a read that never settles, abandoned or not, holds up nobody else). A superseded sync publishes nothing: its waiters take the newest sync's outcome, or sync
|
|
76
|
+
* again when that one is gone or abandoned. With a gate, the file is read only from the object the gate admitted and
|
|
77
|
+
* holds, never by its name again; a file the newest gate does not admit is closed (not read, not re-sent, its text
|
|
78
|
+
* dropped from the server's open documents). With none (a caller with no read face), by name through `readText` — and
|
|
79
|
+
* such a sync shares a by-name read already in flight for the document, each joiner keeping its OWN cancellation
|
|
80
|
+
* (HRD-LSP-12); a gated read is its caller's admission and is never shared.
|
|
81
|
+
*/
|
|
82
|
+
private syncGated;
|
|
83
|
+
private syncInTurn;
|
|
84
|
+
/** didClose a document the caller's gate refused and forget it (a later caller whose gate admits it opens it afresh). */
|
|
85
|
+
private closeRefused;
|
|
86
|
+
/** didOpen a new file / didChange (full-text replace) an opened one whose disk content moved, from the text `read`
|
|
87
|
+
* returns — only while `isNewest()` holds when the read settles (else {@link SUPERSEDED}, nothing sent). Never throws: it
|
|
88
|
+
* REPORTS failure instead (`{ok:false}`) — an unreadable file no longer looks like a clean sync to the caller. */
|
|
89
|
+
private publishRead;
|
|
70
90
|
/** Structural fallback classification for errors carrying no {@link lspFailureOf} brand (a
|
|
71
91
|
* third-party transport, e.g. the TOB WS lane) — abort signal and transport liveness are the only
|
|
72
92
|
* transport-neutral facts available; everything else is a server fault. Cannot produce
|
|
@@ -78,19 +98,21 @@ export declare class TransportLspSession implements LspSession {
|
|
|
78
98
|
* project config only "sees" its open files (e.g. tsserver inferred project), so without the carry-over,
|
|
79
99
|
* cross-file ops (references/calls) silently degrade to single-file after a heal. */
|
|
80
100
|
openedFiles(): string[];
|
|
81
|
-
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
|
|
101
|
+
/** Carry files over to a fresh session (heal): nothing is read here — no caller's read face is in hand — and the next
|
|
102
|
+
* gated sync (an op, a change notice) opens each file its caller's gate admits before that caller's query, so no op
|
|
103
|
+
* is answered from a document set missing an admitted carried file (HRD-LSP-1). `deferred` counts the files carried;
|
|
104
|
+
* a carried file that later cannot be read is logged by that sync (`lsp_sync_skipped`). */
|
|
105
|
+
warmOpen(filePaths: string[], _signal?: AbortSignal): Promise<{
|
|
85
106
|
opened: number;
|
|
86
107
|
failed: number;
|
|
108
|
+
deferred: number;
|
|
87
109
|
}>;
|
|
88
110
|
/**
|
|
89
111
|
* design/121: push the file's CURRENT text to the server WITHOUT an op (didOpen/didChange as
|
|
90
112
|
* appropriate). The agent's edit/write tools call this fire-and-forget so the server re-analyzes
|
|
91
113
|
* and pushes publishDiagnostics — the session's normal lazy sync only runs on the next LSP op,
|
|
92
|
-
* which may never come. Never throws (same best-effort contract as
|
|
114
|
+
* which may never come. Through the caller's `gate`, as every read. Never throws (same best-effort contract as publishRead).
|
|
93
115
|
*/
|
|
94
|
-
notifyFileChanged(filePath: string, signal?: AbortSignal): Promise<void>;
|
|
116
|
+
notifyFileChanged(filePath: string, signal?: AbortSignal, gate?: LspReadGate): Promise<void>;
|
|
95
117
|
close(): Promise<void>;
|
|
96
118
|
}
|
package/dist/core/lsp-session.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { uriToPath, buildRequest, callHierarchyMethod, parseResult, pathToUri } from "./lsp-protocol.js";
|
|
2
|
-
import { lspFailureOf, SharedAbortScope, settleOnAbort } from "./lsp.js";
|
|
2
|
+
import { lspFailureOf, MAX_LSP_FILE_BYTES, SharedAbortScope, settleOnAbort } from "./lsp.js";
|
|
3
3
|
import { thrownText } from "./thrown-value.js";
|
|
4
|
+
const UTF8 = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
5
|
+
async function readHeldText(held, signal) {
|
|
6
|
+
const bytes = await held.readBytes(MAX_LSP_FILE_BYTES, signal);
|
|
7
|
+
if (!bytes.ok)
|
|
8
|
+
throw bytes.error;
|
|
9
|
+
return UTF8.decode(bytes.value);
|
|
10
|
+
}
|
|
11
|
+
const SUPERSEDED = Symbol("superseded");
|
|
4
12
|
const WARMUP_MAX_ATTEMPTS = 3;
|
|
5
13
|
export class TransportLspSession {
|
|
6
14
|
transport;
|
|
@@ -10,6 +18,8 @@ export class TransportLspSession {
|
|
|
10
18
|
warmup;
|
|
11
19
|
opened = new Map();
|
|
12
20
|
openedUris = new Map();
|
|
21
|
+
carried = new Set();
|
|
22
|
+
syncGen = new Map();
|
|
13
23
|
syncing = new Map();
|
|
14
24
|
createdAt = Date.now();
|
|
15
25
|
warmupBarrier;
|
|
@@ -44,20 +54,20 @@ export class TransportLspSession {
|
|
|
44
54
|
return this.activeRequests;
|
|
45
55
|
}
|
|
46
56
|
activeRequests = 0;
|
|
47
|
-
async request(op, params, signal) {
|
|
57
|
+
async request(op, params, signal, gate) {
|
|
48
58
|
this.activeRequests += 1;
|
|
49
59
|
try {
|
|
50
|
-
return await this.requestOnce(op, params, signal);
|
|
60
|
+
return await this.requestOnce(op, params, signal, gate);
|
|
51
61
|
}
|
|
52
62
|
finally {
|
|
53
63
|
this.activeRequests -= 1;
|
|
54
64
|
}
|
|
55
65
|
}
|
|
56
|
-
async requestOnce(op, params, signal) {
|
|
66
|
+
async requestOnce(op, params, signal, gate) {
|
|
57
67
|
const built = buildRequest(op, params);
|
|
58
68
|
if (!built)
|
|
59
69
|
return { kind: "none", reason: "unsupported_operation" };
|
|
60
|
-
const sync = await this.syncOpenedFiles(params.filePath, signal);
|
|
70
|
+
const sync = await this.syncOpenedFiles(params.filePath, signal, gate);
|
|
61
71
|
if (!sync.ok) {
|
|
62
72
|
const reason = signal?.aborted ? "cancelled" : (sync.reason ?? "file_sync_failed");
|
|
63
73
|
this.log("lsp_op_degraded", { op, file: params.filePath, reason, err: sync.error });
|
|
@@ -131,64 +141,109 @@ export class TransportLspSession {
|
|
|
131
141
|
entry.scope.join(signal);
|
|
132
142
|
await settleOnAbort(entry.job, signal, () => undefined);
|
|
133
143
|
}
|
|
134
|
-
async syncOpenedFiles(queried, signal) {
|
|
135
|
-
const files = new Set(this.opened.keys());
|
|
144
|
+
async syncOpenedFiles(queried, signal, gate) {
|
|
145
|
+
const files = new Set([...this.opened.keys(), ...this.carried]);
|
|
136
146
|
files.add(queried);
|
|
137
|
-
const outcomes = await Promise.all([...files].map(async (fp) => [fp, await this.
|
|
147
|
+
const outcomes = await Promise.all([...files].map(async (fp) => [fp, await this.syncGated(fp, signal, gate)]));
|
|
138
148
|
return outcomes.find(([fp]) => fp === queried)?.[1] ?? { ok: true };
|
|
139
149
|
}
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
async syncGated(filePath, signal, gate) {
|
|
151
|
+
const outcome = await this.syncInTurn(filePath, signal, gate);
|
|
152
|
+
if (outcome.ok || signal?.aborted !== true)
|
|
153
|
+
this.carried.delete(filePath);
|
|
154
|
+
return outcome;
|
|
155
|
+
}
|
|
156
|
+
syncInTurn(filePath, signal, gate) {
|
|
157
|
+
const aborted = () => ({ ok: false, error: "aborted by the caller" });
|
|
158
|
+
const newest = this.syncing.get(filePath);
|
|
159
|
+
if (gate === undefined && newest?.byName === true && !newest.scope.signal.aborted) {
|
|
160
|
+
newest.scope.join(signal);
|
|
161
|
+
return settleOnAbort(newest.job, signal, aborted);
|
|
145
162
|
}
|
|
163
|
+
const gen = (this.syncGen.get(filePath) ?? 0) + 1;
|
|
164
|
+
this.syncGen.set(filePath, gen);
|
|
165
|
+
const isNewest = () => this.syncGen.get(filePath) === gen;
|
|
146
166
|
const scope = new SharedAbortScope();
|
|
147
167
|
scope.join(signal);
|
|
148
|
-
const entry = {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return { ok: false, error };
|
|
168
|
+
const entry = { scope, byName: gate === undefined, job: Promise.resolve({ ok: true }) };
|
|
169
|
+
entry.job = (async () => {
|
|
170
|
+
let published;
|
|
171
|
+
if (gate === undefined)
|
|
172
|
+
published = await this.publishRead(filePath, () => this.readText(filePath, scope.signal), isNewest);
|
|
173
|
+
else {
|
|
174
|
+
const admission = await gate(filePath, scope.signal).catch((e) => ({ ok: false, reason: `the read boundary could not judge this file (${thrownText(e)})` }));
|
|
175
|
+
if (!admission.ok) {
|
|
176
|
+
if (isNewest() && !scope.signal.aborted)
|
|
177
|
+
this.closeRefused(filePath);
|
|
178
|
+
return { ok: false, error: admission.reason };
|
|
159
179
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const version = cur.version + 1;
|
|
175
|
-
this.transport.notify("textDocument/didChange", { textDocument: { uri, version }, contentChanges: [{ text }] });
|
|
176
|
-
if (this.transport.closed === true) {
|
|
177
|
-
this.log("lsp_sync_notify_dropped", { file: filePath, kind: "didChange", version });
|
|
178
|
-
return { ok: false, error: "didChange was not delivered (transport closed)", reason: "server_terminated" };
|
|
179
|
-
}
|
|
180
|
-
this.opened.set(filePath, { version, text });
|
|
181
|
-
this.log("lsp_file_resync", { file: filePath, version });
|
|
182
|
-
}
|
|
183
|
-
return { ok: true };
|
|
184
|
-
})(),
|
|
185
|
-
};
|
|
180
|
+
const held = admission.object;
|
|
181
|
+
published = await this.publishRead(filePath, () => readHeldText(held, scope.signal).finally(() => held.close().catch(() => undefined)), isNewest);
|
|
182
|
+
}
|
|
183
|
+
if (published !== SUPERSEDED)
|
|
184
|
+
return published;
|
|
185
|
+
if (scope.signal.aborted)
|
|
186
|
+
return aborted();
|
|
187
|
+
const successor = this.syncing.get(filePath);
|
|
188
|
+
if (successor !== undefined && successor !== entry && !successor.scope.signal.aborted) {
|
|
189
|
+
successor.scope.join(scope.signal);
|
|
190
|
+
return successor.job;
|
|
191
|
+
}
|
|
192
|
+
return this.syncInTurn(filePath, scope.signal, gate);
|
|
193
|
+
})();
|
|
186
194
|
this.syncing.set(filePath, entry);
|
|
187
195
|
const settled = entry.job.finally(() => {
|
|
188
196
|
if (this.syncing.get(filePath) === entry)
|
|
189
197
|
this.syncing.delete(filePath);
|
|
190
198
|
});
|
|
191
|
-
return settleOnAbort(settled, signal,
|
|
199
|
+
return settleOnAbort(settled, signal, aborted);
|
|
200
|
+
}
|
|
201
|
+
closeRefused(filePath) {
|
|
202
|
+
this.carried.delete(filePath);
|
|
203
|
+
if (!this.opened.has(filePath))
|
|
204
|
+
return;
|
|
205
|
+
const uri = pathToUri(filePath);
|
|
206
|
+
this.opened.delete(filePath);
|
|
207
|
+
this.openedUris.delete(uri);
|
|
208
|
+
this.transport.notify("textDocument/didClose", { textDocument: { uri } });
|
|
209
|
+
this.log("lsp_doc_closed_refused", { file: filePath });
|
|
210
|
+
}
|
|
211
|
+
async publishRead(filePath, read, isNewest) {
|
|
212
|
+
let text;
|
|
213
|
+
try {
|
|
214
|
+
text = await read();
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
const error = thrownText(e);
|
|
218
|
+
this.log("lsp_sync_skipped", { file: filePath, error, opened: this.opened.has(filePath) });
|
|
219
|
+
return { ok: false, error };
|
|
220
|
+
}
|
|
221
|
+
if (!isNewest())
|
|
222
|
+
return SUPERSEDED;
|
|
223
|
+
const cur = this.opened.get(filePath);
|
|
224
|
+
const uri = pathToUri(filePath);
|
|
225
|
+
if (!cur) {
|
|
226
|
+
this.transport.notify("textDocument/didOpen", {
|
|
227
|
+
textDocument: { uri, languageId: this.languageId, version: 1, text },
|
|
228
|
+
});
|
|
229
|
+
if (this.transport.closed === true) {
|
|
230
|
+
this.log("lsp_sync_notify_dropped", { file: filePath, kind: "didOpen" });
|
|
231
|
+
return { ok: false, error: "didOpen was not delivered (transport closed)", reason: "server_terminated" };
|
|
232
|
+
}
|
|
233
|
+
this.opened.set(filePath, { version: 1, text });
|
|
234
|
+
this.openedUris.set(uri, filePath);
|
|
235
|
+
}
|
|
236
|
+
else if (cur.text !== text) {
|
|
237
|
+
const version = cur.version + 1;
|
|
238
|
+
this.transport.notify("textDocument/didChange", { textDocument: { uri, version }, contentChanges: [{ text }] });
|
|
239
|
+
if (this.transport.closed === true) {
|
|
240
|
+
this.log("lsp_sync_notify_dropped", { file: filePath, kind: "didChange", version });
|
|
241
|
+
return { ok: false, error: "didChange was not delivered (transport closed)", reason: "server_terminated" };
|
|
242
|
+
}
|
|
243
|
+
this.opened.set(filePath, { version, text });
|
|
244
|
+
this.log("lsp_file_resync", { file: filePath, version });
|
|
245
|
+
}
|
|
246
|
+
return { ok: true };
|
|
192
247
|
}
|
|
193
248
|
static classifyUnbranded(signal, transport) {
|
|
194
249
|
if (signal?.aborted)
|
|
@@ -201,15 +256,20 @@ export class TransportLspSession {
|
|
|
201
256
|
return this.transport.closed === true;
|
|
202
257
|
}
|
|
203
258
|
openedFiles() {
|
|
204
|
-
return [...this.opened.keys()];
|
|
259
|
+
return [...new Set([...this.opened.keys(), ...this.carried])];
|
|
205
260
|
}
|
|
206
|
-
async warmOpen(filePaths,
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
261
|
+
async warmOpen(filePaths, _signal) {
|
|
262
|
+
let deferred = 0;
|
|
263
|
+
for (const fp of filePaths) {
|
|
264
|
+
if (this.opened.has(fp) || this.carried.has(fp))
|
|
265
|
+
continue;
|
|
266
|
+
this.carried.add(fp);
|
|
267
|
+
deferred += 1;
|
|
268
|
+
}
|
|
269
|
+
return { opened: 0, failed: 0, deferred };
|
|
210
270
|
}
|
|
211
|
-
notifyFileChanged(filePath, signal) {
|
|
212
|
-
return this.
|
|
271
|
+
notifyFileChanged(filePath, signal, gate) {
|
|
272
|
+
return this.syncGated(filePath, signal, gate).then(() => undefined);
|
|
213
273
|
}
|
|
214
274
|
close() {
|
|
215
275
|
return this.transport.close();
|
package/dist/core/lsp.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type RootsSeat } from "../tools/fs/safety.js";
|
|
2
|
+
import { type ReadDenyMatcher } from "../tools/fs/read-deny.js";
|
|
3
|
+
import type { HomeViewsCarrier } from "../tools/fs/read-deny-home.js";
|
|
4
|
+
import type { ContainmentFace } from "../tools/fs/containment-face.js";
|
|
5
|
+
import type { AgentTool, BoundFileObject, ExecutionEnv } from "../internal/harness-types.js";
|
|
2
6
|
/**
|
|
3
7
|
* LSP code-intelligence tool (design/64 §13.1) — the app-dev命门 (go-to-def / find-refs / hover across
|
|
4
8
|
* Dart/Kotlin/TS). A single `lsp` tool with a 9-operation enum, built over a pluggable {@link LspServerManager}.
|
|
@@ -147,8 +151,71 @@ export interface LspRequestParams {
|
|
|
147
151
|
}
|
|
148
152
|
/** An active language-server session for one language, scoped to a workspace. */
|
|
149
153
|
export interface LspSession {
|
|
150
|
-
|
|
154
|
+
/** `gate` = the caller's read face at the session's file reads ({@link LspReadGate}); absent ⇒ a caller with none. */
|
|
155
|
+
request(op: LspOperation, params: LspRequestParams, signal?: AbortSignal, gate?: LspReadGate): Promise<LspResult>;
|
|
151
156
|
}
|
|
157
|
+
/** What a session's read gate answered for one file: the object its judgment named, opened and HELD (the environment's
|
|
158
|
+
* `openBound`) under the canonical key it was judged as — the session reads that object, never the name again, and closes
|
|
159
|
+
* it — or a refusal with its reason. */
|
|
160
|
+
export type LspReadAdmission = {
|
|
161
|
+
readonly ok: true;
|
|
162
|
+
readonly key: string;
|
|
163
|
+
readonly object: BoundFileObject;
|
|
164
|
+
} | {
|
|
165
|
+
readonly ok: false;
|
|
166
|
+
readonly reason: string;
|
|
167
|
+
};
|
|
168
|
+
/** May a session read `filePath` (a path {@link resolveLspPath} minted) for this caller, now — and if so, WHAT: the gate
|
|
169
|
+
* admits an object, not a name ({@link LspReadAdmission}). A session reads a file only from the object its caller's gate
|
|
170
|
+
* admitted, and closes an opened document the gate does not admit. */
|
|
171
|
+
export type LspReadGate = (filePath: string, signal?: AbortSignal) => Promise<LspReadAdmission>;
|
|
172
|
+
/** The read face one LSP mount reads under: the seats the fs read tools take, so the gate and the tool resolve and judge
|
|
173
|
+
* a `filePath` alike (see {@link LspToolOptions.readFace}). */
|
|
174
|
+
export interface LspReadFace {
|
|
175
|
+
/** The canonical task root: the primary containment root, and the base when no working directory is tracked. */
|
|
176
|
+
readonly rootCanonical: string;
|
|
177
|
+
/** The extra read roots (write-capable and read-only), read per call. */
|
|
178
|
+
readonly roots?: RootsSeat;
|
|
179
|
+
/** The tracked working directory; `current` is read per call as the base of a relative `filePath`. */
|
|
180
|
+
readonly cwdRef?: {
|
|
181
|
+
readonly current: string;
|
|
182
|
+
};
|
|
183
|
+
/** The read-deny table in force, with its placement's home views. */
|
|
184
|
+
readonly readDeny?: ReadDenyMatcher & HomeViewsCarrier;
|
|
185
|
+
/** The read containment face. Absent ⇒ `roots`. */
|
|
186
|
+
readonly face?: ContainmentFace;
|
|
187
|
+
/** The file read station's admission: an out-of-root target the gate asked about and let through, read once. */
|
|
188
|
+
readonly admitted?: (toolCallId: string | undefined, canonicalKey: string) => boolean;
|
|
189
|
+
/** Settles once the read-deny table and read roots a judgment takes are ready (a Runner compiles them per read of the
|
|
190
|
+
* person's rules): a call's own (`toolCallId`), or — with none — the run's newest; at `signal` too; never rejects;
|
|
191
|
+
* absent ⇒ ready. The tool waits for it before it takes its call's; the session's own reads before they judge. */
|
|
192
|
+
readonly ready?: (toolCallId: string | undefined, signal?: AbortSignal) => Promise<void>;
|
|
193
|
+
}
|
|
194
|
+
/** The base a relative `filePath` resolves against: the catalog declaration through the one selector the fences read it
|
|
195
|
+
* with; with no read face the env's working directory is the only base there is. */
|
|
196
|
+
export declare function lspBaseOf(readFace: LspReadFace | undefined, env?: ExecutionEnv): string | undefined;
|
|
197
|
+
/** The session gate over a read face — the bound read a user attachment makes, at the session's read: the name is judged
|
|
198
|
+
* as `Read` judges its target (a judgment that throws refuses), the object the judgment named is opened and held
|
|
199
|
+
* (`openBound`; an environment without it cannot bind and refuses), and the name, judged again, must still land on the
|
|
200
|
+
* held key. The admission carries the held object; the session reads only it. `authorizedKey` = the canonical key this
|
|
201
|
+
* call's target was authorized as (the station's one-read admission already spent on it): a spelling that still lands
|
|
202
|
+
* there passes outside the roots, nothing else does. */
|
|
203
|
+
export declare function lspReadGateOf(env: ExecutionEnv, face: LspReadFace, authorizedKey?: string): LspReadGate;
|
|
204
|
+
/** Why a file the language server named is withheld: a read-deny row reaches one of its views, or its views could not be
|
|
205
|
+
* read (`unreadable` — the judgment's absence word). */
|
|
206
|
+
export type LspListedWithholding = {
|
|
207
|
+
readonly pattern: string;
|
|
208
|
+
} | {
|
|
209
|
+
readonly unreadable: true;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* The files a language server named (result locations, symbols, drained diagnostics), judged as ONE bounded batch against
|
|
213
|
+
* the read face's deny table on every view of each file — the spelling, the canonical target, the home view and the link
|
|
214
|
+
* hops, through the same `resolveKey` a read target meets (containment is not this question: the face is judged open).
|
|
215
|
+
* Fail-closed: a file whose views cannot be read, and every file when the batch outlives the gate's deadline, is withheld.
|
|
216
|
+
* Kept files are absent from the result.
|
|
217
|
+
*/
|
|
218
|
+
export declare function lspListedWithheld(env: ExecutionEnv, face: LspReadFace, uris: readonly string[], signal?: AbortSignal): Promise<ReadonlyMap<string, LspListedWithholding>>;
|
|
152
219
|
/**
|
|
153
220
|
* A JSON-RPC transport to ONE language server, POST-`initialize`. The DEPLOYMENT provides the concrete
|
|
154
221
|
* transport; the protocol logic ({@link buildRequest}/`parseResult`, `TransportLspSession`) drives the LSP
|
|
@@ -175,7 +242,8 @@ export interface LspTransport {
|
|
|
175
242
|
* a test mock stays minimal. */
|
|
176
243
|
readonly closed?: boolean;
|
|
177
244
|
}
|
|
178
|
-
/** Reads a workspace file's CURRENT text for the session's didOpen/didChange re-sync
|
|
245
|
+
/** Reads a workspace file's CURRENT text for the session's didOpen/didChange re-sync BY NAME — the read of a caller with no
|
|
246
|
+
* gate (no read face) only; a gated read is the admitted held object's ({@link LspReadAdmission}). TOC = local fs; TOB =
|
|
179
247
|
* the sandbox ExecutionEnv. */
|
|
180
248
|
export type LspReadText = (filePath: string, signal?: AbortSignal) => Promise<string>;
|
|
181
249
|
/** CC 2.1.220's LSP file cap (10MB — `File too large for LSP analysis`, (pretty220.js:474745, historical; not relocated in 250 — see cc-250 anchors)). Single-sourced
|
|
@@ -216,6 +284,18 @@ export interface LspToolOptions {
|
|
|
216
284
|
/** The task's ExecutionEnv, passed through to {@link LspServerManager.sessionFor} (the agent's sandbox —
|
|
217
285
|
* where an in-env language server must run). The Runner wires this from the per-task env. */
|
|
218
286
|
env?: ExecutionEnv;
|
|
287
|
+
/**
|
|
288
|
+
* The read face this mount reads under (needs `env`). PRESENT ⇒ every read the tool causes passes it: the `filePath`
|
|
289
|
+
* target is resolved against the declared base (the tracked working directory, else the root) and fenced as `Read`
|
|
290
|
+
* fences its own — containment with the station's one-read admission, the read-deny table, blocked devices; the file
|
|
291
|
+
* preconditions (exists / a regular file / the size cap) are read from the object that judgment named, held at its key
|
|
292
|
+
* (the environment's `openBound`), never from the spelling again; the session reads an opened file only from the
|
|
293
|
+
* object the face admitted and held (an environment without `openBound` reads nothing) and closes one it does not
|
|
294
|
+
* admit; every location or symbol in a file the deny table reaches is withheld, and the result says how many. ABSENT ⇒
|
|
295
|
+
* no read face judged this mount (a library-direct tool with no gate): a relative `filePath` resolves against
|
|
296
|
+
* `env.cwd`, the preconditions and the session's reads go by name, nothing is fenced and nothing is withheld.
|
|
297
|
+
*/
|
|
298
|
+
readFace?: LspReadFace;
|
|
219
299
|
}
|
|
220
300
|
/** Build the `lsp` tool over a {@link LspServerManager}. Mounted only when a manager is wired (opt-in). */
|
|
221
301
|
export declare function createLspTool(manager: LspServerManager, opts?: LspToolOptions): AgentTool;
|