@sema-agent/core 2.13.0 → 4.0.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 +2345 -0
- package/dist/agents/cascade.d.ts +6 -1
- package/dist/agents/cascade.js +76 -32
- package/dist/agents/cumulative-stats.d.ts +29 -0
- package/dist/agents/cumulative-stats.js +29 -0
- package/dist/agents/observer.d.ts +4 -0
- package/dist/agents/observer.js +11 -10
- package/dist/agents/repair-loop.d.ts +2 -0
- package/dist/agents/repair-loop.js +110 -3
- package/dist/agents/retain-ledger.d.ts +20 -2
- package/dist/agents/retain-ledger.js +38 -8
- package/dist/agents/roster-store.d.ts +4 -0
- package/dist/agents/roster-store.js +20 -1
- package/dist/agents/subagent.js +66 -64
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +23 -24
- package/dist/agents/team.js +7 -4
- package/dist/agents/verify.d.ts +15 -2
- package/dist/agents/verify.js +39 -3
- package/dist/bin/sema-tb.js +2 -2
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/circuit-breaker.d.ts +1 -0
- package/dist/brain/circuit-breaker.js +26 -9
- package/dist/brain/degrading.js +13 -1
- package/dist/brain/failover.js +2 -0
- package/dist/brain/openai.js +24 -21
- package/dist/brain/repetition.js +10 -1
- package/dist/brain/routing.js +5 -1
- package/dist/brain/status-sink.js +5 -1
- package/dist/brain/stream-engine.js +5 -2
- package/dist/brain/stream-shared.d.ts +1 -0
- package/dist/brain/stream-shared.js +14 -0
- package/dist/brain/terminal-cause.d.ts +4 -0
- package/dist/brain/terminal-cause.js +12 -0
- package/dist/brain/tool-call-id.d.ts +1 -0
- package/dist/brain/tool-call-id.js +3 -0
- package/dist/brain/tool-call-repair.js +34 -15
- package/dist/core/auto-compaction.js +33 -32
- package/dist/core/background-agent-store.d.ts +22 -22
- package/dist/core/background-agent-store.js +19 -23
- package/dist/core/checkpoint-store.d.ts +7 -13
- package/dist/core/checkpoint-store.js +39 -15
- package/dist/core/consolidate-scope.js +4 -3
- package/dist/core/context-edit.js +3 -0
- package/dist/core/context-guard.js +3 -0
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/git-worktree-env.d.ts +6 -1
- package/dist/core/git-worktree-env.js +12 -1
- package/dist/core/hooks.d.ts +4 -1
- package/dist/core/hooks.js +35 -7
- package/dist/core/lsp-diagnostics.js +13 -7
- package/dist/core/lsp-protocol.d.ts +1 -1
- package/dist/core/lsp-protocol.js +20 -8
- package/dist/core/lsp-session.d.ts +12 -2
- package/dist/core/lsp-session.js +114 -49
- package/dist/core/lsp.d.ts +13 -1
- package/dist/core/lsp.js +65 -9
- package/dist/core/mailbox-store.d.ts +7 -10
- package/dist/core/mcp.d.ts +7 -10
- package/dist/core/mcp.js +7 -3
- package/dist/core/memory-engine/data-plane.js +4 -0
- package/dist/core/memory-engine/engine.d.ts +2 -2
- package/dist/core/memory-engine/engine.js +96 -31
- package/dist/core/memory-engine/file-backend.d.ts +2 -2
- package/dist/core/memory-engine/file-backend.js +38 -39
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +71 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/memory.js +3 -0
- package/dist/core/permission-rules.d.ts +2 -1
- package/dist/core/permission-rules.js +24 -3
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +1 -15
- package/dist/core/runner/assemble-result.d.ts +3 -8
- package/dist/core/runner/assemble-result.js +3 -5
- package/dist/core/runner/compaction-call-options.d.ts +7 -0
- package/dist/core/runner/compaction-call-options.js +30 -0
- package/dist/core/runner/prepare-task.d.ts +6 -23
- package/dist/core/runner/prepare-task.js +31 -53
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +86 -178
- package/dist/core/runner/session-rule-policy.js +4 -1
- package/dist/core/runner/teardown-bounded.d.ts +7 -0
- package/dist/core/runner/teardown-bounded.js +36 -0
- package/dist/core/runner/tool-disclosure.d.ts +2 -5
- package/dist/core/runner/turn-attachments.d.ts +5 -15
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/usage-accounting.d.ts +6 -0
- package/dist/core/runner/usage-accounting.js +9 -8
- package/dist/core/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/secret-env.js +5 -1
- package/dist/core/sensitive-path-policy.js +11 -4
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/store-contracts/session-repo-contract.js +40 -0
- package/dist/core/stub-env.d.ts +2 -6
- package/dist/core/surrogate-safe-slice.d.ts +4 -0
- package/dist/core/surrogate-safe-slice.js +18 -0
- package/dist/core/task-registry-monitor.js +15 -14
- package/dist/core/task-registry-shared.d.ts +22 -0
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +40 -57
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.js +2 -1
- package/dist/core/tool-policy.js +72 -19
- package/dist/core/tool-result-store.d.ts +1 -0
- package/dist/core/tool-result-store.js +17 -1
- package/dist/core/trace.d.ts +3 -9
- package/dist/core/types.d.ts +16 -12
- package/dist/core/untrusted-egress.js +7 -5
- package/dist/core/workflow-journal-store.d.ts +9 -20
- package/dist/core/workflow-run-store.d.ts +11 -10
- package/dist/core/workflow-run-store.js +22 -16
- package/dist/engine/compaction/compaction.d.ts +1 -0
- package/dist/engine/compaction/compaction.js +14 -5
- package/dist/engine/compaction/utils.js +4 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +13 -33
- package/dist/engine/harness/types.d.ts +29 -67
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +11 -4
- package/dist/engine/loop/types.d.ts +11 -9
- package/dist/engine/lsp/frame-decoder.js +6 -3
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -0
- package/dist/engine/lsp/node-lsp-manager.js +103 -26
- package/dist/engine/lsp/stdio-lsp-transport.js +16 -7
- package/dist/engine/session/memory-repo.d.ts +2 -6
- package/dist/engine/session/session.d.ts +5 -30
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/run-spec.js +7 -4
- package/dist/orchestration/run-workflow-tool.js +3 -35
- package/dist/orchestration/workflow-governance.js +43 -9
- package/dist/orchestration/workflow-sandbox-conformance.js +85 -51
- package/dist/orchestration/workflow-script-store.js +34 -4
- package/dist/orchestration/workflow.d.ts +3 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/artifact-store.d.ts +2 -0
- package/dist/prompt-assembly/artifact-store.js +39 -24
- package/dist/prompt-assembly/assemble.js +38 -7
- package/dist/prompt-assembly/epoch.js +19 -2
- package/dist/prompt-assembly/event-registry.js +3 -3
- package/dist/prompt-assembly/explain.js +1 -1
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/tool-catalog.js +16 -1
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/prompt-assembly/turn-snapshot.js +4 -1
- package/dist/stores/file/background-agent-store.d.ts +3 -12
- package/dist/stores/file/background-agent-store.js +3 -41
- package/dist/stores/file/checkpoint-store.js +6 -6
- package/dist/stores/file/fs-atomic.d.ts +3 -0
- package/dist/stores/file/fs-atomic.js +53 -8
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +7 -0
- package/dist/stores/file/mailbox-store.d.ts +2 -6
- package/dist/stores/file/session-policy-store.js +15 -1
- package/dist/stores/file/session-store.d.ts +4 -6
- package/dist/stores/file/session-store.js +28 -1
- package/dist/stores/file/workflow-journal-store.d.ts +3 -11
- package/dist/stores/file/workflow-run-store.d.ts +3 -7
- package/dist/stores/file/workflow-run-store.js +4 -17
- package/dist/tools/fs/bash-readonly-classifier.d.ts +2 -0
- package/dist/tools/fs/bash-readonly-classifier.js +73 -21
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +407 -91
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/notebook.d.ts +2 -5
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +43 -14
- package/dist/tools/fs/search.js +5 -5
- package/dist/tools/loop-tick.js +1 -1
- package/dist/tools/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +57 -9
- package/dist/tools/worktree.d.ts +4 -7
- package/dist/tools/worktree.js +235 -143
- package/package.json +9 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ExecutionEnv, ExecutionError, FileError, type FileInfo, type Result } from "../harness/types.js";
|
|
1
|
+
import { type ExecResult, type ExecutionEnv, ExecutionError, type ExecutionEnvExecOptions, FileError, type FileInfo, type Result } from "../harness/types.js";
|
|
2
2
|
import type { BackgroundShellCapability, BackgroundShellId, BackgroundPoll, BackgroundSpawnOptions } from "../../core/background-shell.js";
|
|
3
3
|
import { BackgroundShellError } from "../../core/background-shell.js";
|
|
4
4
|
import type { SchedulerCapability, ScheduledIntent, SchedulerContext, ScheduledTaskId, ScheduledTaskSummary } from "../../core/scheduler.js";
|
|
@@ -54,29 +54,7 @@ export declare class NodeExecutionEnv implements ExecutionEnv, BackgroundShellCa
|
|
|
54
54
|
list(ctx: SchedulerContext): Promise<Result<ScheduledTaskSummary[], SchedulerError>>;
|
|
55
55
|
absolutePath(path: string): Promise<Result<string, FileError>>;
|
|
56
56
|
joinPath(parts: string[]): Promise<Result<string, FileError>>;
|
|
57
|
-
exec(command: string, options?:
|
|
58
|
-
cwd?: string;
|
|
59
|
-
env?: Record<string, string>;
|
|
60
|
-
timeout?: number;
|
|
61
|
-
abortSignal?: AbortSignal;
|
|
62
|
-
onStdout?: (chunk: string) => void;
|
|
63
|
-
onStderr?: (chunk: string) => void;
|
|
64
|
-
detachSignal?: AbortSignal;
|
|
65
|
-
onSpawn?: (handle: {
|
|
66
|
-
pid: number;
|
|
67
|
-
kill: () => void;
|
|
68
|
-
}) => void;
|
|
69
|
-
onDetachAdopted?: () => void;
|
|
70
|
-
autoBackgroundOnTimeout?: boolean;
|
|
71
|
-
}): Promise<Result<{
|
|
72
|
-
stdout: string;
|
|
73
|
-
stderr: string;
|
|
74
|
-
exitCode: number;
|
|
75
|
-
detached?: {
|
|
76
|
-
shellId: string;
|
|
77
|
-
cause?: "timeout";
|
|
78
|
-
};
|
|
79
|
-
}, ExecutionError>>;
|
|
57
|
+
exec(command: string, options?: ExecutionEnvExecOptions): Promise<Result<ExecResult, ExecutionError>>;
|
|
80
58
|
readTextFile(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
81
59
|
readTextLines(path: string, options?: {
|
|
82
60
|
maxLines?: number;
|
|
@@ -92,6 +92,7 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
|
|
|
92
92
|
images?: ImageContent[];
|
|
93
93
|
} & UserMessageProvenance): Promise<AssistantMessage>;
|
|
94
94
|
private static emptyInjection;
|
|
95
|
+
private enqueueInjection;
|
|
95
96
|
steer(text: string, options?: {
|
|
96
97
|
images?: ImageContent[];
|
|
97
98
|
} & UserMessageProvenance): Promise<void>;
|
|
@@ -478,14 +478,7 @@ export class AgentHarness {
|
|
|
478
478
|
details: result.details,
|
|
479
479
|
isError,
|
|
480
480
|
});
|
|
481
|
-
return patch
|
|
482
|
-
? {
|
|
483
|
-
content: patch.content,
|
|
484
|
-
details: patch.details,
|
|
485
|
-
isError: patch.isError,
|
|
486
|
-
terminate: patch.terminate,
|
|
487
|
-
}
|
|
488
|
-
: undefined;
|
|
481
|
+
return patch;
|
|
489
482
|
},
|
|
490
483
|
prepareNextTurn: async () => {
|
|
491
484
|
await this.flushPendingSessionWrites();
|
|
@@ -725,42 +718,29 @@ export class AgentHarness {
|
|
|
725
718
|
static emptyInjection(text, options) {
|
|
726
719
|
return text.trim().length === 0 && (options?.images === undefined || options.images.length === 0);
|
|
727
720
|
}
|
|
721
|
+
async enqueueInjection(queue, text, options) {
|
|
722
|
+
if (AgentHarness.emptyInjection(text, options))
|
|
723
|
+
return;
|
|
724
|
+
const m = createUserMessage(text, options?.images, options);
|
|
725
|
+
if (options?.enginePayload !== undefined)
|
|
726
|
+
engineNotePayloads.set(m, options.enginePayload);
|
|
727
|
+
queue.push(m);
|
|
728
|
+
await this.emitQueueUpdate();
|
|
729
|
+
}
|
|
728
730
|
async steer(text, options) {
|
|
729
731
|
if (this.phase === "idle") {
|
|
730
732
|
throw new AgentHarnessError("invalid_state", "Cannot steer while idle");
|
|
731
733
|
}
|
|
732
|
-
|
|
733
|
-
return;
|
|
734
|
-
{
|
|
735
|
-
const m = createUserMessage(text, options?.images, options);
|
|
736
|
-
if (options?.enginePayload !== undefined)
|
|
737
|
-
engineNotePayloads.set(m, options.enginePayload);
|
|
738
|
-
this.steerQueue.push(m);
|
|
739
|
-
}
|
|
740
|
-
await this.emitQueueUpdate();
|
|
734
|
+
await this.enqueueInjection(this.steerQueue, text, options);
|
|
741
735
|
}
|
|
742
736
|
async followUp(text, options) {
|
|
743
737
|
if (this.phase === "idle") {
|
|
744
738
|
throw new AgentHarnessError("invalid_state", "Cannot follow up while idle");
|
|
745
739
|
}
|
|
746
|
-
|
|
747
|
-
return;
|
|
748
|
-
{
|
|
749
|
-
const m = createUserMessage(text, options?.images, options);
|
|
750
|
-
if (options?.enginePayload !== undefined)
|
|
751
|
-
engineNotePayloads.set(m, options.enginePayload);
|
|
752
|
-
this.followUpQueue.push(m);
|
|
753
|
-
}
|
|
754
|
-
await this.emitQueueUpdate();
|
|
740
|
+
await this.enqueueInjection(this.followUpQueue, text, options);
|
|
755
741
|
}
|
|
756
742
|
async nextTurn(text, options) {
|
|
757
|
-
|
|
758
|
-
return;
|
|
759
|
-
const m = createUserMessage(text, options?.images, options);
|
|
760
|
-
if (options?.enginePayload !== undefined)
|
|
761
|
-
engineNotePayloads.set(m, options.enginePayload);
|
|
762
|
-
this.nextTurnQueue.push(m);
|
|
763
|
-
await this.emitQueueUpdate();
|
|
743
|
+
await this.enqueueInjection(this.nextTurnQueue, text, options);
|
|
764
744
|
}
|
|
765
745
|
async appendMessage(message) {
|
|
766
746
|
try {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { PromptEpochDescriptor } from "../../prompt-assembly/epoch.js";
|
|
1
2
|
import type { DocumentContent, ImageContent, Model, SimpleStreamOptions, TextContent, Transport } from "../llm/index.js";
|
|
2
|
-
import type { AgentEvent, AgentMessage, AgentTool, QueueMode, ThinkingLevel } from "../loop/types.js";
|
|
3
|
+
import type { AfterToolCallResult, AgentEvent, AgentMessage, AgentTool, BeforeToolCallResult, QueueMode, ThinkingLevel } from "../loop/types.js";
|
|
3
4
|
import type { AgentCoreRuntimeDeps } from "../loop/runtime-deps.js";
|
|
4
5
|
export type Result<TValue, TError> = {
|
|
5
6
|
ok: true;
|
|
@@ -129,16 +130,17 @@ export interface FileSystem {
|
|
|
129
130
|
}): Promise<Result<string, FileError>>;
|
|
130
131
|
cleanup(): Promise<void>;
|
|
131
132
|
}
|
|
133
|
+
export interface ExecResult {
|
|
134
|
+
stdout: string;
|
|
135
|
+
stderr: string;
|
|
136
|
+
exitCode: number;
|
|
137
|
+
detached?: {
|
|
138
|
+
shellId: string;
|
|
139
|
+
cause?: "timeout";
|
|
140
|
+
};
|
|
141
|
+
}
|
|
132
142
|
export interface Shell {
|
|
133
|
-
exec(command: string, options?: ExecutionEnvExecOptions): Promise<Result<
|
|
134
|
-
stdout: string;
|
|
135
|
-
stderr: string;
|
|
136
|
-
exitCode: number;
|
|
137
|
-
detached?: {
|
|
138
|
-
shellId: string;
|
|
139
|
-
cause?: "timeout";
|
|
140
|
-
};
|
|
141
|
-
}, ExecutionError>>;
|
|
143
|
+
exec(command: string, options?: ExecutionEnvExecOptions): Promise<Result<ExecResult, ExecutionError>>;
|
|
142
144
|
cleanup(): Promise<void>;
|
|
143
145
|
}
|
|
144
146
|
export interface ExecutionEnv extends FileSystem, Shell {
|
|
@@ -195,15 +197,8 @@ export interface LeafEntry extends SessionTreeEntryBase {
|
|
|
195
197
|
type: "leaf";
|
|
196
198
|
targetId: string | null;
|
|
197
199
|
}
|
|
198
|
-
export interface PromptEpochEntry extends SessionTreeEntryBase {
|
|
200
|
+
export interface PromptEpochEntry extends SessionTreeEntryBase, PromptEpochDescriptor {
|
|
199
201
|
type: "prompt_epoch";
|
|
200
|
-
epoch: number;
|
|
201
|
-
artifactDigest: string;
|
|
202
|
-
packId: string;
|
|
203
|
-
assemblyApi: number;
|
|
204
|
-
activatedBy: "session_start" | "compaction" | "legacy_migration";
|
|
205
|
-
centerArtifactDigest?: string;
|
|
206
|
-
sourceRevision?: string;
|
|
207
202
|
}
|
|
208
203
|
export interface AnnouncedListingEntry extends SessionTreeEntryBase {
|
|
209
204
|
type: "announced_listing";
|
|
@@ -211,28 +206,22 @@ export interface AnnouncedListingEntry extends SessionTreeEntryBase {
|
|
|
211
206
|
skills?: string[];
|
|
212
207
|
models?: string[];
|
|
213
208
|
}
|
|
214
|
-
export interface
|
|
215
|
-
|
|
209
|
+
export interface ActiveWorktreeSession {
|
|
210
|
+
worktreeDir: string;
|
|
211
|
+
originalCwd: string;
|
|
212
|
+
baseSha: string;
|
|
213
|
+
entered?: boolean;
|
|
214
|
+
}
|
|
215
|
+
export interface WorkspaceState {
|
|
216
216
|
taskRoot: string;
|
|
217
217
|
handsCwd?: string;
|
|
218
|
-
activeWorktree?:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
entered?: boolean;
|
|
223
|
-
};
|
|
218
|
+
activeWorktree?: ActiveWorktreeSession;
|
|
219
|
+
}
|
|
220
|
+
export interface WorkspaceStateEntry extends SessionTreeEntryBase, WorkspaceState {
|
|
221
|
+
type: "workspace_state";
|
|
224
222
|
}
|
|
225
223
|
export declare const WORKSPACE_STATE_MAX_PATH_CHARS = 4096;
|
|
226
|
-
export declare function normalizeWorkspaceState(value: unknown):
|
|
227
|
-
taskRoot: string;
|
|
228
|
-
handsCwd?: string;
|
|
229
|
-
activeWorktree?: {
|
|
230
|
-
worktreeDir: string;
|
|
231
|
-
originalCwd: string;
|
|
232
|
-
baseSha: string;
|
|
233
|
-
entered?: boolean;
|
|
234
|
-
};
|
|
235
|
-
} | undefined;
|
|
224
|
+
export declare function normalizeWorkspaceState(value: unknown): WorkspaceState | undefined;
|
|
236
225
|
export declare const ANNOUNCED_LISTING_MAX_NAMES = 500;
|
|
237
226
|
export declare const ANNOUNCED_LISTING_MAX_NAME_CHARS = 200;
|
|
238
227
|
export declare function normalizeCompactionStateCarrier(v: unknown): {
|
|
@@ -331,26 +320,8 @@ export interface Session<TMetadata extends SessionMetadata = SessionMetadata> {
|
|
|
331
320
|
skills?: string[];
|
|
332
321
|
models?: string[];
|
|
333
322
|
} | undefined>;
|
|
334
|
-
appendWorkspaceState(state:
|
|
335
|
-
|
|
336
|
-
handsCwd?: string;
|
|
337
|
-
activeWorktree?: {
|
|
338
|
-
worktreeDir: string;
|
|
339
|
-
originalCwd: string;
|
|
340
|
-
baseSha: string;
|
|
341
|
-
entered?: boolean;
|
|
342
|
-
};
|
|
343
|
-
}): Promise<string>;
|
|
344
|
-
getWorkspaceState(): Promise<{
|
|
345
|
-
taskRoot: string;
|
|
346
|
-
handsCwd?: string;
|
|
347
|
-
activeWorktree?: {
|
|
348
|
-
worktreeDir: string;
|
|
349
|
-
originalCwd: string;
|
|
350
|
-
baseSha: string;
|
|
351
|
-
entered?: boolean;
|
|
352
|
-
};
|
|
353
|
-
} | undefined>;
|
|
323
|
+
appendWorkspaceState(state: WorkspaceState): Promise<string>;
|
|
324
|
+
getWorkspaceState(): Promise<WorkspaceState | undefined>;
|
|
354
325
|
getPromptEpoch?(): Promise<import("../../prompt-assembly/epoch.js").PromptEpochDescriptor | undefined>;
|
|
355
326
|
}
|
|
356
327
|
export interface SessionCreateOptions {
|
|
@@ -487,17 +458,8 @@ export interface BeforeProviderRequestResult {
|
|
|
487
458
|
export interface BeforeProviderPayloadResult {
|
|
488
459
|
payload: unknown;
|
|
489
460
|
}
|
|
490
|
-
export
|
|
491
|
-
|
|
492
|
-
reason?: string;
|
|
493
|
-
updatedInput?: unknown;
|
|
494
|
-
}
|
|
495
|
-
export interface ToolResultPatch {
|
|
496
|
-
content?: Array<TextContent | ImageContent | DocumentContent>;
|
|
497
|
-
details?: unknown;
|
|
498
|
-
isError?: boolean;
|
|
499
|
-
terminate?: boolean;
|
|
500
|
-
}
|
|
461
|
+
export type ToolCallResult = BeforeToolCallResult;
|
|
462
|
+
export type ToolResultPatch = AfterToolCallResult;
|
|
501
463
|
export interface SessionBeforeCompactResult {
|
|
502
464
|
cancel?: boolean;
|
|
503
465
|
compaction?: CompactResult;
|
|
@@ -143,7 +143,7 @@ export interface AssistantMessage {
|
|
|
143
143
|
usageMissing?: true;
|
|
144
144
|
stopReason: StopReason;
|
|
145
145
|
errorMessage?: string;
|
|
146
|
-
errorKind?: "length_empty";
|
|
146
|
+
errorKind?: "length_empty" | "degenerate" | "walltime_cutoff";
|
|
147
147
|
partialFinalized?: true;
|
|
148
148
|
repetition?: {
|
|
149
149
|
cut?: RepetitionEvent;
|
|
@@ -647,10 +647,16 @@ function partitionToolCalls(toolCalls, tools) {
|
|
|
647
647
|
}
|
|
648
648
|
return batches;
|
|
649
649
|
}
|
|
650
|
+
function resolveToolConcurrency(configured) {
|
|
651
|
+
if (configured === undefined)
|
|
652
|
+
return 10;
|
|
653
|
+
return Number.isFinite(configured) ? Math.max(1, Math.floor(configured)) : 1;
|
|
654
|
+
}
|
|
650
655
|
async function runCapped(thunks, limit) {
|
|
651
656
|
const results = new Array(thunks.length);
|
|
652
657
|
let next = 0;
|
|
653
|
-
const
|
|
658
|
+
const width = Number.isFinite(limit) ? Math.max(1, Math.min(limit, thunks.length)) : 1;
|
|
659
|
+
const workers = Array.from({ length: width }, async () => {
|
|
654
660
|
while (next < thunks.length) {
|
|
655
661
|
const i = next++;
|
|
656
662
|
results[i] = await thunks[i]();
|
|
@@ -660,7 +666,7 @@ async function runCapped(thunks, limit) {
|
|
|
660
666
|
return results;
|
|
661
667
|
}
|
|
662
668
|
async function executeToolCallsPartitioned(currentContext, assistantMessage, toolCalls, config, signal, emit, executor) {
|
|
663
|
-
const cap = config.maxToolConcurrency
|
|
669
|
+
const cap = resolveToolConcurrency(config.maxToolConcurrency);
|
|
664
670
|
const allFinalized = [];
|
|
665
671
|
const messages = [];
|
|
666
672
|
let engineCutSeen = false;
|
|
@@ -798,7 +804,7 @@ class StreamToolExecutor {
|
|
|
798
804
|
this.signal?.aborted ||
|
|
799
805
|
partialAssistant === null ||
|
|
800
806
|
!isBatchSafeToolCall(findToolByName(this.context.tools, toolCall.name), toolCall) ||
|
|
801
|
-
this.inFlight >= (this.config.maxToolConcurrency
|
|
807
|
+
this.inFlight >= resolveToolConcurrency(this.config.maxToolConcurrency) ||
|
|
802
808
|
this.entries.has(toolCall.id)) {
|
|
803
809
|
this.barrier = true;
|
|
804
810
|
return;
|
|
@@ -927,7 +933,8 @@ function prepareToolCallArguments(tool, toolCall) {
|
|
|
927
933
|
async function prepareToolCall(currentContext, assistantMessage, toolCall, config, signal) {
|
|
928
934
|
const tool = findToolByName(currentContext.tools, toolCall.name);
|
|
929
935
|
if (!tool) {
|
|
930
|
-
const
|
|
936
|
+
const tombstoneEntry = Object.hasOwn(TOMBSTONED_TOOLS, toolCall.name) ? TOMBSTONED_TOOLS[toolCall.name] : undefined;
|
|
937
|
+
const tombstone = typeof tombstoneEntry === "string" ? tombstoneEntry : undefined;
|
|
931
938
|
if (tombstone !== undefined) {
|
|
932
939
|
return {
|
|
933
940
|
kind: "immediate",
|
|
@@ -74,21 +74,23 @@ export interface LoopRecoveryOptions {
|
|
|
74
74
|
hasLiveWriteoutWindow?: () => boolean;
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
+
export interface ToolCutInfo {
|
|
78
|
+
toolCallId: string;
|
|
79
|
+
toolName: string;
|
|
80
|
+
elapsedMs: number;
|
|
81
|
+
settled: Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
export interface LoopToolDeadline {
|
|
84
|
+
deadlineMs: () => number | undefined;
|
|
85
|
+
onCut?: (info: ToolCutInfo) => void;
|
|
86
|
+
}
|
|
77
87
|
export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
78
88
|
model: Model;
|
|
79
89
|
recovery?: LoopRecoveryOptions;
|
|
80
90
|
maxTokensPerCall?: () => number | undefined;
|
|
81
91
|
callDeadlineMsPerCall?: () => number | undefined;
|
|
82
92
|
stallTimeoutsPerCall?: () => import("../llm/types.js").StallTimeouts | undefined;
|
|
83
|
-
toolDeadline?:
|
|
84
|
-
deadlineMs: () => number | undefined;
|
|
85
|
-
onCut?: (info: {
|
|
86
|
-
toolCallId: string;
|
|
87
|
-
toolName: string;
|
|
88
|
-
elapsedMs: number;
|
|
89
|
-
settled: Promise<void>;
|
|
90
|
-
}) => void;
|
|
91
|
-
};
|
|
93
|
+
toolDeadline?: LoopToolDeadline;
|
|
92
94
|
maxToolConcurrency?: number;
|
|
93
95
|
streamingToolExecution?: boolean;
|
|
94
96
|
convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
|
|
@@ -8,8 +8,7 @@ export function createFrameDecoder(maxBytes = 50 * 1024 * 1024) {
|
|
|
8
8
|
return function decode(chunk) {
|
|
9
9
|
const frames = [];
|
|
10
10
|
buf = Buffer.concat([buf, chunk]);
|
|
11
|
-
|
|
12
|
-
buf = Buffer.alloc(0);
|
|
11
|
+
let awaiting;
|
|
13
12
|
for (;;) {
|
|
14
13
|
const he = buf.indexOf("\r\n\r\n");
|
|
15
14
|
if (he === -1)
|
|
@@ -25,11 +24,15 @@ export function createFrameDecoder(maxBytes = 50 * 1024 * 1024) {
|
|
|
25
24
|
continue;
|
|
26
25
|
}
|
|
27
26
|
const start = he + 4;
|
|
28
|
-
if (buf.length < start + len)
|
|
27
|
+
if (buf.length < start + len) {
|
|
28
|
+
awaiting = start + len;
|
|
29
29
|
break;
|
|
30
|
+
}
|
|
30
31
|
frames.push(buf.subarray(start, start + len).toString("utf8"));
|
|
31
32
|
buf = buf.subarray(start + len);
|
|
32
33
|
}
|
|
34
|
+
if (buf.length > (awaiting ?? maxBytes + HEADER_SLACK))
|
|
35
|
+
buf = Buffer.alloc(0);
|
|
33
36
|
return frames;
|
|
34
37
|
};
|
|
35
38
|
}
|
|
@@ -29,6 +29,7 @@ export declare class NodeLspManager implements LspServerManager {
|
|
|
29
29
|
private readonly cache;
|
|
30
30
|
private readonly opening;
|
|
31
31
|
private readonly failed;
|
|
32
|
+
private readonly initFailures;
|
|
32
33
|
private disposed;
|
|
33
34
|
constructor(opts?: NodeLspManagerOptions);
|
|
34
35
|
private sweepIdle;
|
|
@@ -37,6 +38,7 @@ export declare class NodeLspManager implements LspServerManager {
|
|
|
37
38
|
private evictOverCap;
|
|
38
39
|
evict(root: string): Promise<void>;
|
|
39
40
|
clearFailed(root?: string): void;
|
|
41
|
+
unavailableReason(filePath: string, env?: ExecutionEnv): string | undefined;
|
|
40
42
|
private openSession;
|
|
41
43
|
private serverCmd;
|
|
42
44
|
dispose(): Promise<void>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { killProcessTree } from "../execution-env/kill-tree.js";
|
|
3
|
-
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { open, readFile, stat } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, extname, resolve as resolvePath } from "node:path";
|
|
5
|
-
import { existsSync } from "node:fs";
|
|
5
|
+
import { constants as fsConstants, existsSync } from "node:fs";
|
|
6
6
|
import { pathToUri } from "../../core/lsp-protocol.js";
|
|
7
7
|
import { LspDiagnosticsRegistry } from "../../core/lsp-diagnostics.js";
|
|
8
8
|
import { TransportLspSession } from "../../core/lsp-session.js";
|
|
9
9
|
import { StdioLspTransport } from "./stdio-lsp-transport.js";
|
|
10
|
-
import { MAX_LSP_FILE_BYTES } from "../../core/lsp.js";
|
|
10
|
+
import { MAX_LSP_FILE_BYTES, SharedAbortScope, settleOnAbort } from "../../core/lsp.js";
|
|
11
11
|
export const DEFAULT_LSP_SERVERS = {
|
|
12
12
|
typescript: "typescript-language-server --stdio",
|
|
13
13
|
javascript: "typescript-language-server --stdio",
|
|
@@ -46,6 +46,7 @@ const CLIENT_CAPABILITIES = {
|
|
|
46
46
|
},
|
|
47
47
|
workspace: { symbol: {}, workspaceFolders: false },
|
|
48
48
|
};
|
|
49
|
+
const INIT_FAILURE_LATCH = 2;
|
|
49
50
|
export class NodeLspManager {
|
|
50
51
|
servers;
|
|
51
52
|
spawnFn;
|
|
@@ -60,6 +61,7 @@ export class NodeLspManager {
|
|
|
60
61
|
cache = new Map();
|
|
61
62
|
opening = new Map();
|
|
62
63
|
failed = new Map();
|
|
64
|
+
initFailures = new Map();
|
|
63
65
|
disposed = false;
|
|
64
66
|
constructor(opts = {}) {
|
|
65
67
|
this.servers = { ...DEFAULT_LSP_SERVERS, ...opts.servers };
|
|
@@ -79,6 +81,10 @@ export class NodeLspManager {
|
|
|
79
81
|
sweepIdle() {
|
|
80
82
|
const cutoff = Date.now() - this.idleTtlMs;
|
|
81
83
|
for (const [key, e] of [...this.cache]) {
|
|
84
|
+
if (e.session.inFlight > 0) {
|
|
85
|
+
e.lastUsed = Date.now();
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
82
88
|
if (e.lastUsed < cutoff) {
|
|
83
89
|
this.cache.delete(key);
|
|
84
90
|
this.log("lsp_evict_idle", { key });
|
|
@@ -106,19 +112,20 @@ export class NodeLspManager {
|
|
|
106
112
|
this.cache.delete(key);
|
|
107
113
|
const carry = cached.session.openedFiles();
|
|
108
114
|
this.log("lsp_heal", { language, root, carried: carry.length });
|
|
109
|
-
|
|
110
|
-
if (healed && carry.length)
|
|
111
|
-
await healed.warmOpen(carry, signal).catch(() => undefined);
|
|
112
|
-
return healed;
|
|
115
|
+
return this.open(key, language, root, signal, env, carry);
|
|
113
116
|
}
|
|
114
117
|
return this.open(key, language, root, signal, env);
|
|
115
118
|
}
|
|
116
|
-
open(key, language, root, signal, env) {
|
|
119
|
+
open(key, language, root, signal, env, carry = []) {
|
|
117
120
|
const inflight = this.opening.get(key);
|
|
118
|
-
if (inflight)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
if (inflight && !inflight.scope.signal.aborted) {
|
|
122
|
+
inflight.scope.join(signal);
|
|
123
|
+
return settleOnAbort(inflight.job, signal, () => undefined);
|
|
124
|
+
}
|
|
125
|
+
const scope = new SharedAbortScope();
|
|
126
|
+
scope.join(signal);
|
|
127
|
+
const entry = { root, cancelled: false, scope, job: undefined };
|
|
128
|
+
entry.job = this.openSession(language, root, scope.signal, env)
|
|
122
129
|
.then(async (session) => {
|
|
123
130
|
if (!session)
|
|
124
131
|
return undefined;
|
|
@@ -126,26 +133,41 @@ export class NodeLspManager {
|
|
|
126
133
|
await session.close().catch(() => undefined);
|
|
127
134
|
return undefined;
|
|
128
135
|
}
|
|
136
|
+
if (carry.length) {
|
|
137
|
+
const carried = await session.warmOpen(carry, scope.signal).catch(() => ({ opened: 0, failed: carry.length }));
|
|
138
|
+
this.log("lsp_heal_carried", { language, root, ...carried });
|
|
139
|
+
if (this.disposed || entry.cancelled) {
|
|
140
|
+
await session.close().catch(() => undefined);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
129
144
|
this.evictOverCap();
|
|
130
145
|
this.cache.set(key, { session, root, lastUsed: Date.now() });
|
|
131
146
|
return session;
|
|
132
147
|
})
|
|
133
|
-
.finally(() =>
|
|
148
|
+
.finally(() => {
|
|
149
|
+
if (this.opening.get(key) === entry)
|
|
150
|
+
this.opening.delete(key);
|
|
151
|
+
});
|
|
134
152
|
this.opening.set(key, entry);
|
|
135
|
-
return entry.job;
|
|
153
|
+
return settleOnAbort(entry.job, signal, () => undefined);
|
|
136
154
|
}
|
|
137
155
|
evictOverCap() {
|
|
138
156
|
while (this.cache.size >= this.maxSessions) {
|
|
139
157
|
let lruKey;
|
|
140
158
|
let lruTime = Infinity;
|
|
141
159
|
for (const [k, e] of this.cache) {
|
|
160
|
+
if (e.session.inFlight > 0)
|
|
161
|
+
continue;
|
|
142
162
|
if (e.lastUsed < lruTime) {
|
|
143
163
|
lruTime = e.lastUsed;
|
|
144
164
|
lruKey = k;
|
|
145
165
|
}
|
|
146
166
|
}
|
|
147
|
-
if (lruKey === undefined)
|
|
167
|
+
if (lruKey === undefined) {
|
|
168
|
+
this.log("lsp_evict_deferred", { size: this.cache.size, maxSessions: this.maxSessions });
|
|
148
169
|
break;
|
|
170
|
+
}
|
|
149
171
|
const evicted = this.cache.get(lruKey);
|
|
150
172
|
this.cache.delete(lruKey);
|
|
151
173
|
this.log("lsp_evict_lru", { key: lruKey });
|
|
@@ -154,6 +176,7 @@ export class NodeLspManager {
|
|
|
154
176
|
}
|
|
155
177
|
async evict(root) {
|
|
156
178
|
const closing = [];
|
|
179
|
+
this.clearFailed(root);
|
|
157
180
|
for (const [key, e] of [...this.cache]) {
|
|
158
181
|
if (e.root === root) {
|
|
159
182
|
this.cache.delete(key);
|
|
@@ -174,8 +197,27 @@ export class NodeLspManager {
|
|
|
174
197
|
if (root !== undefined && e.root !== root)
|
|
175
198
|
continue;
|
|
176
199
|
this.failed.delete(key);
|
|
177
|
-
this.
|
|
200
|
+
this.initFailures.delete(key);
|
|
201
|
+
this.log("lsp_negative_cache_cleared", { key, attempts: e.attempts, kind: e.kind });
|
|
178
202
|
}
|
|
203
|
+
for (const [key, _n] of [...this.initFailures]) {
|
|
204
|
+
if (root === undefined || key.endsWith(` ${root}`))
|
|
205
|
+
this.initFailures.delete(key);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
unavailableReason(filePath, env) {
|
|
209
|
+
const language = languageFor(filePath);
|
|
210
|
+
if (!language)
|
|
211
|
+
return undefined;
|
|
212
|
+
const cmd = this.serverCmd(language);
|
|
213
|
+
if (!cmd)
|
|
214
|
+
return `no ${language} language server is configured in this environment`;
|
|
215
|
+
const e = this.failed.get(`${language} ${this.resolveRootFn(filePath, env)}`);
|
|
216
|
+
if (e?.kind === "missing_binary")
|
|
217
|
+
return `the ${language} language server ("${e.command}") is not installed on this host`;
|
|
218
|
+
if (e?.kind === "initialize")
|
|
219
|
+
return `the ${language} language server ("${e.command}") is installed but failed to initialize`;
|
|
220
|
+
return undefined;
|
|
179
221
|
}
|
|
180
222
|
async openSession(language, root, signal, _env) {
|
|
181
223
|
const cmd = this.serverCmd(language);
|
|
@@ -183,10 +225,18 @@ export class NodeLspManager {
|
|
|
183
225
|
return undefined;
|
|
184
226
|
const tokens = (cmd.match(/"[^"]*"|\S+/g) ?? []).map((t) => (t.startsWith('"') && t.endsWith('"') && t.length >= 2 ? t.slice(1, -1) : t));
|
|
185
227
|
const [command, ...args] = tokens.length > 0 ? tokens : [""];
|
|
186
|
-
const
|
|
228
|
+
const key = `${language} ${root}`;
|
|
229
|
+
let child;
|
|
230
|
+
try {
|
|
231
|
+
child = await this.spawnFn(command, args, root, signal);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
this.log("lsp_spawn_error", { language, root, command, err: e instanceof Error ? e.message : String(e) });
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
187
237
|
if (!child) {
|
|
188
238
|
if (signal?.aborted !== true)
|
|
189
|
-
this.failed.set(
|
|
239
|
+
this.failed.set(key, { root, attempts: 1, kind: "missing_binary", command, language });
|
|
190
240
|
this.log("lsp_spawn_degraded", { language, root, command });
|
|
191
241
|
return undefined;
|
|
192
242
|
}
|
|
@@ -204,9 +254,18 @@ export class NodeLspManager {
|
|
|
204
254
|
}
|
|
205
255
|
catch (e) {
|
|
206
256
|
await transport.close();
|
|
207
|
-
|
|
257
|
+
const attempt = (this.initFailures.get(key) ?? 0) + 1;
|
|
258
|
+
const latched = attempt >= INIT_FAILURE_LATCH && signal?.aborted !== true;
|
|
259
|
+
if (latched) {
|
|
260
|
+
this.failed.set(key, { root, attempts: 1, kind: "initialize", command, language });
|
|
261
|
+
this.initFailures.delete(key);
|
|
262
|
+
}
|
|
263
|
+
else
|
|
264
|
+
this.initFailures.set(key, attempt);
|
|
265
|
+
this.log("lsp_init_degraded", { language, root, err: e instanceof Error ? e.message : String(e), attempt, latched });
|
|
208
266
|
return undefined;
|
|
209
267
|
}
|
|
268
|
+
this.initFailures.delete(key);
|
|
210
269
|
this.log("lsp_open", { language, root });
|
|
211
270
|
return new TransportLspSession(transport, language, this.readTextFn, this.log, this.warmup, this.diagnostics);
|
|
212
271
|
}
|
|
@@ -249,33 +308,51 @@ export const defaultLspReadText = async (fp, signal) => {
|
|
|
249
308
|
throw new Error(`not a regular file: ${fp}`);
|
|
250
309
|
if (st.size > MAX_LSP_FILE_BYTES)
|
|
251
310
|
throw new Error(`file too large for LSP analysis (${st.size} bytes exceeds ${MAX_LSP_FILE_BYTES})`);
|
|
252
|
-
|
|
311
|
+
const flags = fsConstants.O_RDONLY | (fsConstants.O_NONBLOCK ?? 0);
|
|
312
|
+
const fh = await open(fp, flags);
|
|
313
|
+
try {
|
|
314
|
+
const fst = await fh.stat();
|
|
315
|
+
if (!fst.isFile())
|
|
316
|
+
throw new Error(`not a regular file: ${fp}`);
|
|
317
|
+
if (fst.size > MAX_LSP_FILE_BYTES)
|
|
318
|
+
throw new Error(`file too large for LSP analysis (${fst.size} bytes exceeds ${MAX_LSP_FILE_BYTES})`);
|
|
319
|
+
return await readFile(fh, { encoding: "utf8", signal });
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
await fh.close().catch(() => undefined);
|
|
323
|
+
}
|
|
253
324
|
};
|
|
254
325
|
export function lspSpawnOptions(cwd) {
|
|
255
326
|
return process.platform === "win32"
|
|
256
327
|
? { cwd, stdio: ["pipe", "pipe", "ignore"], shell: true }
|
|
257
328
|
: { cwd, stdio: ["pipe", "pipe", "ignore"] };
|
|
258
329
|
}
|
|
259
|
-
export const defaultLspSpawn = (command, args, cwd, signal) => new Promise((resolve) => {
|
|
330
|
+
export const defaultLspSpawn = (command, args, cwd, signal) => new Promise((resolve, reject) => {
|
|
260
331
|
let child;
|
|
261
332
|
try {
|
|
262
333
|
child = spawn(command, args, lspSpawnOptions(cwd));
|
|
263
334
|
}
|
|
264
|
-
catch {
|
|
265
|
-
return
|
|
335
|
+
catch (e) {
|
|
336
|
+
return reject(e instanceof Error ? e : new Error(String(e)));
|
|
266
337
|
}
|
|
338
|
+
child.on("error", () => { });
|
|
267
339
|
const onSpawn = () => {
|
|
268
340
|
cleanup();
|
|
269
341
|
resolve(child);
|
|
270
342
|
};
|
|
271
|
-
const onError = () => {
|
|
343
|
+
const onError = (err) => {
|
|
272
344
|
cleanup();
|
|
273
|
-
|
|
345
|
+
if (err?.code === "ENOENT")
|
|
346
|
+
resolve(undefined);
|
|
347
|
+
else
|
|
348
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
274
349
|
};
|
|
275
350
|
const onAbort = () => {
|
|
276
351
|
cleanup();
|
|
277
352
|
try {
|
|
278
|
-
if (
|
|
353
|
+
if (child.pid === undefined)
|
|
354
|
+
return resolve(undefined);
|
|
355
|
+
if (process.platform === "win32")
|
|
279
356
|
killProcessTree(child.pid, { force: true });
|
|
280
357
|
else
|
|
281
358
|
child.kill();
|