engineering-behavior-observatory 0.2.1
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/LICENSE +202 -0
- package/README.md +151 -0
- package/contracts/codex-app-server-0.150.1/manifest.json +25 -0
- package/contracts/codex-app-server-0.150.1/schema/InitializeParams.json +84 -0
- package/contracts/codex-app-server-0.150.1/schema/JSONRPCMessage.json +137 -0
- package/contracts/codex-app-server-0.150.1/schema/v2/ThreadReadParams.json +17 -0
- package/contracts/codex-app-server-0.150.1/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
- package/contracts/codex-app-server-0.150.1/schema/v2/TurnInterruptParams.json +17 -0
- package/contracts/codex-app-server-0.150.1/types/AskForApproval.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/ClientNotification.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/SandboxMode.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/ThreadReadParams.ts +9 -0
- package/contracts/codex-app-server-0.150.1/types/TokenUsageBreakdown.ts +5 -0
- package/contracts/codex-app-server-0.150.1/types/TurnInterruptParams.ts +5 -0
- package/contracts/codex-app-server-0.153.4/manifest.json +27 -0
- package/contracts/codex-app-server-0.153.4/schema/JSONRPCMessage.json +137 -0
- package/contracts/codex-app-server-0.153.4/schema/v1/InitializeParams.json +84 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/ThreadReadParams.json +17 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/ThreadStartParams.json +515 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/TurnInterruptParams.json +17 -0
- package/contracts/codex-app-server-0.153.4/schema/v2/TurnStartParams.json +921 -0
- package/contracts/codex-app-server-0.153.4/types/AskForApproval.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/ClientNotification.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/SandboxMode.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/ThreadReadParams.ts +12 -0
- package/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.ts +5 -0
- package/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.ts +5 -0
- package/contracts/openhands-agent-server-v1.44.1.json +65 -0
- package/contracts/openhands-agent-server-v1.46.0.json +46 -0
- package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.d.ts +9 -0
- package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.d.ts +3 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.d.ts +1 -0
- package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.d.ts +10 -0
- package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.d.ts +8 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.js +2 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.d.ts +4 -0
- package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.js +2 -0
- package/dist/src/agent-sdk-normalizer.d.ts +22 -0
- package/dist/src/agent-sdk-normalizer.js +995 -0
- package/dist/src/agent-sdk-run.d.ts +31 -0
- package/dist/src/agent-sdk-run.js +242 -0
- package/dist/src/agent-sdk-runner.d.ts +96 -0
- package/dist/src/agent-sdk-runner.js +407 -0
- package/dist/src/agent-sdk.d.ts +294 -0
- package/dist/src/agent-sdk.js +644 -0
- package/dist/src/aggregation.d.ts +183 -0
- package/dist/src/aggregation.js +513 -0
- package/dist/src/artifacts.d.ts +36 -0
- package/dist/src/artifacts.js +1372 -0
- package/dist/src/atlas-grafana.d.ts +377 -0
- package/dist/src/atlas-grafana.js +47 -0
- package/dist/src/atlas-html.d.ts +2 -0
- package/dist/src/atlas-html.js +34 -0
- package/dist/src/atlas.d.ts +130 -0
- package/dist/src/atlas.js +235 -0
- package/dist/src/behavior-assertions.d.ts +95 -0
- package/dist/src/behavior-assertions.js +154 -0
- package/dist/src/cli.d.ts +5 -0
- package/dist/src/cli.js +957 -0
- package/dist/src/codex-judge.d.ts +44 -0
- package/dist/src/codex-judge.js +214 -0
- package/dist/src/codex-run.d.ts +114 -0
- package/dist/src/codex-run.js +519 -0
- package/dist/src/codex.d.ts +187 -0
- package/dist/src/codex.js +1028 -0
- package/dist/src/contracts.d.ts +155 -0
- package/dist/src/contracts.js +866 -0
- package/dist/src/corpus.d.ts +67 -0
- package/dist/src/corpus.js +540 -0
- package/dist/src/cursor-sdk-runner.d.ts +81 -0
- package/dist/src/cursor-sdk-runner.js +362 -0
- package/dist/src/cursor-sdk.d.ts +81 -0
- package/dist/src/cursor-sdk.js +1078 -0
- package/dist/src/deepseek-adapter.d.ts +152 -0
- package/dist/src/deepseek-adapter.js +777 -0
- package/dist/src/exports.d.ts +69 -0
- package/dist/src/exports.js +966 -0
- package/dist/src/human-calibration.d.ts +167 -0
- package/dist/src/human-calibration.js +618 -0
- package/dist/src/index.d.ts +71 -0
- package/dist/src/index.js +38 -0
- package/dist/src/lifecycle.d.ts +196 -0
- package/dist/src/lifecycle.js +1889 -0
- package/dist/src/normalization-integrity.d.ts +128 -0
- package/dist/src/normalization-integrity.js +429 -0
- package/dist/src/openhands-run.d.ts +36 -0
- package/dist/src/openhands-run.js +415 -0
- package/dist/src/openhands.d.ts +130 -0
- package/dist/src/openhands.js +859 -0
- package/dist/src/pi.d.ts +203 -0
- package/dist/src/pi.js +1345 -0
- package/dist/src/process-protocol.d.ts +245 -0
- package/dist/src/process-protocol.js +1261 -0
- package/dist/src/retained-evidence.d.ts +12 -0
- package/dist/src/retained-evidence.js +201 -0
- package/dist/src/run-bundles.d.ts +193 -0
- package/dist/src/run-bundles.js +993 -0
- package/dist/src/scheduler.d.ts +109 -0
- package/dist/src/scheduler.js +1080 -0
- package/dist/src/semantic-judge.d.ts +194 -0
- package/dist/src/semantic-judge.js +875 -0
- package/dist/src/structural-observations.d.ts +124 -0
- package/dist/src/structural-observations.js +671 -0
- package/dist/src/task-packets.d.ts +115 -0
- package/dist/src/task-packets.js +683 -0
- package/dist/src/uniform-events.d.ts +120 -0
- package/dist/src/uniform-events.js +158 -0
- package/dist/src/verifiers.d.ts +116 -0
- package/dist/src/verifiers.js +819 -0
- package/dist/src/workspaces.d.ts +56 -0
- package/dist/src/workspaces.js +1314 -0
- package/docs/README.md +60 -0
- package/docs/development/README.md +71 -0
- package/docs/development/documentation-sync.md +58 -0
- package/docs/development/extension-contracts.md +307 -0
- package/docs/evaluation/README.md +48 -0
- package/docs/evaluation/aggregation.md +112 -0
- package/docs/evaluation/behavior-assertions.md +45 -0
- package/docs/evaluation/human-calibration.md +170 -0
- package/docs/evaluation/normalization-integrity.md +80 -0
- package/docs/evaluation/semantic-judge.md +188 -0
- package/docs/evaluation/structural-observations.md +84 -0
- package/docs/evaluation/uniform-events.md +131 -0
- package/docs/guides/atlas.md +144 -0
- package/docs/guides/evidence-and-sharing.md +100 -0
- package/docs/guides/operator-guide.md +424 -0
- package/docs/guides/quickstart.md +152 -0
- package/docs/guides/telemetry.md +66 -0
- package/docs/harnesses/README.md +31 -0
- package/docs/harnesses/claude-agent-sdk.md +61 -0
- package/docs/harnesses/codex-harness.md +174 -0
- package/docs/harnesses/cursor-sdk.md +149 -0
- package/docs/harnesses/deepseek-harness.md +134 -0
- package/docs/harnesses/openhands-agent-server.md +153 -0
- package/docs/harnesses/pi-sdk.md +66 -0
- package/docs/reference/README.md +19 -0
- package/docs/reference/agent-sdk-operational-runner.md +118 -0
- package/docs/reference/cli.md +114 -0
- package/docs/reference/contracts.md +222 -0
- package/docs/reference/run-bundle-contract.md +354 -0
- package/docs/reference/run-lifecycle.md +54 -0
- package/examples/cursor-sdk/README.md +11 -0
- package/examples/cursor-sdk/capture-profile.json +1 -0
- package/examples/cursor-sdk/harness.json +1 -0
- package/examples/cursor-sdk/model.json +1 -0
- package/examples/cursor-sdk/native-limits.json +1 -0
- package/examples/cursor-sdk/native-tool-policy.json +1 -0
- package/ontology/behavior-categories.v1.json +46 -0
- package/package.json +66 -0
- package/release/0.1.0/KNOWN_LIMITATIONS.md +35 -0
- package/release/0.1.0/README.md +46 -0
- package/release/0.1.0/reproducibility.json +61 -0
- package/release/0.2.0/KNOWN_LIMITATIONS.md +40 -0
- package/release/0.2.0/README.md +42 -0
- package/release/0.2.0/reproducibility.json +73 -0
- package/release/0.2.1/KNOWN_LIMITATIONS.md +18 -0
- package/release/0.2.1/README.md +63 -0
- package/release/0.2.1/reproducibility.json +73 -0
- package/release/README.md +13 -0
- package/schemas/aggregation.v1.json +293 -0
- package/schemas/behavior-assertions.v1.json +220 -0
- package/schemas/deepseek-runtime-composition.v1.schema.json +122 -0
- package/schemas/experiment.v1.schema.json +188 -0
- package/schemas/human-calibration.v1.json +336 -0
- package/schemas/normalization-integrity.v1.json +302 -0
- package/schemas/run-bundles/v1.json +1040 -0
- package/schemas/run-queue.v1.schema.json +206 -0
- package/schemas/semantic-judge.v1.json +380 -0
- package/schemas/structural-observations.v1.json +131 -0
- package/schemas/task-packet-freeze.v1.schema.json +106 -0
- package/schemas/task-packet.v1.schema.json +234 -0
- package/schemas/uniform-events/v1.json +289 -0
- package/scripts/atlas-grafana.sh +18 -0
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { HarnessClient, } from "@deepseek-ai/dsh-sdk-client";
|
|
4
|
+
import { canonicalizeMetadata, digestBytes, validateArtifact } from "./artifacts.js";
|
|
5
|
+
import { JsonlEvidenceWriter } from "./process-protocol.js";
|
|
6
|
+
export const DEEPSEEK_SDK_VERSION = "0.1.1-rc.2";
|
|
7
|
+
export const DEEPSEEK_ADAPTER_ID = "deepseek-harness-sdk";
|
|
8
|
+
export const DEEPSEEK_HARNESS_ID = "deepseek-harness";
|
|
9
|
+
export class DeepSeekNativeCapture {
|
|
10
|
+
path;
|
|
11
|
+
#writer;
|
|
12
|
+
#records = [];
|
|
13
|
+
constructor(path) {
|
|
14
|
+
this.path = resolve(path);
|
|
15
|
+
this.#writer = new JsonlEvidenceWriter(this.path, { exclusive: true });
|
|
16
|
+
}
|
|
17
|
+
async record(input) {
|
|
18
|
+
const record = {
|
|
19
|
+
schemaVersion: "ebo.deepseek-native-observation/v1",
|
|
20
|
+
sequence: this.#records.length + 1,
|
|
21
|
+
observedAt: new Date().toISOString(),
|
|
22
|
+
...structuredClone(input),
|
|
23
|
+
};
|
|
24
|
+
await this.#writer.append(record);
|
|
25
|
+
this.#records.push(record);
|
|
26
|
+
return structuredClone(record);
|
|
27
|
+
}
|
|
28
|
+
report() {
|
|
29
|
+
return structuredClone(this.#records);
|
|
30
|
+
}
|
|
31
|
+
flush() {
|
|
32
|
+
return this.#writer.flush();
|
|
33
|
+
}
|
|
34
|
+
close() {
|
|
35
|
+
return this.#writer.close();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function createDeepSeekRuntimeComposition(input) {
|
|
39
|
+
const baseDir = resolve(input.baseDir);
|
|
40
|
+
const dshBin = resolve(baseDir, input.dshBin);
|
|
41
|
+
const processCwd = resolve(baseDir, input.processCwd);
|
|
42
|
+
const workspaceCwd = resolve(baseDir, input.workspaceCwd);
|
|
43
|
+
const profile = required(input.profile, "DeepSeek profile");
|
|
44
|
+
const cordisPath = resolve(baseDir, input.cordisPath);
|
|
45
|
+
const patches = [...new Set([cordisPath, ...(input.patches ?? []).map((path) => resolve(baseDir, path))])];
|
|
46
|
+
const pluginNames = input.plugins.map((plugin) => required(plugin.name, "DeepSeek plugin name"));
|
|
47
|
+
if (new Set(pluginNames).size !== pluginNames.length)
|
|
48
|
+
throw new Error("DeepSeek plugin names must be unique.");
|
|
49
|
+
const composition = {
|
|
50
|
+
schemaVersion: "ebo.deepseek-runtime-composition/v1",
|
|
51
|
+
id: required(input.id, "DeepSeek composition ID"),
|
|
52
|
+
launch: {
|
|
53
|
+
command: process.execPath,
|
|
54
|
+
args: [dshBin, "--profile", profile, ...patches.flatMap((path) => ["--patch", path])],
|
|
55
|
+
processCwd,
|
|
56
|
+
profile,
|
|
57
|
+
...(input.dshHome === undefined ? {} : { dshHome: resolve(baseDir, input.dshHome) }),
|
|
58
|
+
runtimeArtifact: fileReference(baseDir, dshBin),
|
|
59
|
+
},
|
|
60
|
+
workspaceCwd,
|
|
61
|
+
runtime: {
|
|
62
|
+
clientPackage: "@deepseek-ai/dsh-sdk-client",
|
|
63
|
+
clientVersion: DEEPSEEK_SDK_VERSION,
|
|
64
|
+
protocolPackage: "@deepseek-ai/dsh-sdk-protocol",
|
|
65
|
+
protocolVersion: DEEPSEEK_SDK_VERSION,
|
|
66
|
+
},
|
|
67
|
+
route: {
|
|
68
|
+
provider: required(input.provider, "DeepSeek provider"),
|
|
69
|
+
model: required(input.model, "DeepSeek model"),
|
|
70
|
+
...(input.maxTokens === undefined ? {} : { maxTokens: positiveInteger(input.maxTokens, "DeepSeek max tokens") }),
|
|
71
|
+
},
|
|
72
|
+
cordis: fileReference(baseDir, cordisPath),
|
|
73
|
+
patches: patches.map((path) => fileReference(baseDir, path)),
|
|
74
|
+
plugins: input.plugins.map((plugin, index) => ({
|
|
75
|
+
name: pluginNames[index],
|
|
76
|
+
...(plugin.configurationPath === undefined ? {} : { configuration: fileReference(baseDir, plugin.configurationPath) }),
|
|
77
|
+
})),
|
|
78
|
+
environment: {
|
|
79
|
+
mode: "replace",
|
|
80
|
+
allowedKeys: [...new Set(input.environment.allowedKeys)].sort(),
|
|
81
|
+
secretKeys: [...new Set(input.environment.secretKeys)].sort(),
|
|
82
|
+
},
|
|
83
|
+
telemetry: {
|
|
84
|
+
status: input.telemetry.status,
|
|
85
|
+
nativeSpans: input.telemetry.nativeSpans,
|
|
86
|
+
...(input.telemetry.artifactPath === undefined ? {} : { artifact: fileReference(baseDir, input.telemetry.artifactPath) }),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
assertArtifact("DeepSeek runtime composition", composition);
|
|
90
|
+
if (composition.environment.secretKeys.some((key) => !composition.environment.allowedKeys.includes(key))) {
|
|
91
|
+
throw new Error("DeepSeek secret environment keys must also be allowlisted.");
|
|
92
|
+
}
|
|
93
|
+
if (composition.launch.dshHome !== undefined && !composition.environment.allowedKeys.includes("DSH_HOME")) {
|
|
94
|
+
throw new Error("DeepSeek DSH_HOME must be included in the recorded environment allowlist.");
|
|
95
|
+
}
|
|
96
|
+
return composition;
|
|
97
|
+
}
|
|
98
|
+
export function deepSeekCompositionDigest(composition) {
|
|
99
|
+
assertArtifact("DeepSeek runtime composition", composition);
|
|
100
|
+
return digestString(Buffer.from(canonicalizeMetadata(composition)));
|
|
101
|
+
}
|
|
102
|
+
export function deepSeekCapabilities(composition) {
|
|
103
|
+
return {
|
|
104
|
+
protocolVersionNegotiation: "unsupported",
|
|
105
|
+
promptCancellation: "unsupported",
|
|
106
|
+
sessionClose: "unsupported",
|
|
107
|
+
promptResult: "unsupported",
|
|
108
|
+
sessionEvents: "available",
|
|
109
|
+
sessionStatus: "available",
|
|
110
|
+
subagentNotifications: "available",
|
|
111
|
+
telemetry: composition.telemetry.status === "enabled" ? "available" : "unsupported",
|
|
112
|
+
nativeSpans: composition.telemetry.nativeSpans,
|
|
113
|
+
completionEvidence: "agent/inbox/spliced-to-session.status:idle",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export async function executeDeepSeekHarness(context, configuration, capture) {
|
|
117
|
+
validateConfiguration(configuration, context);
|
|
118
|
+
const composition = structuredClone(configuration.composition);
|
|
119
|
+
const capabilities = deepSeekCapabilities(composition);
|
|
120
|
+
const client = new HarnessClient(clientOptions(configuration));
|
|
121
|
+
const subscription = client.subscribeSessionTree(configuration.sessionId);
|
|
122
|
+
const sensitiveValues = composition.environment.secretKeys.flatMap((key) => configuration.env[key] ?? []);
|
|
123
|
+
let status = "failed";
|
|
124
|
+
let serverInfo;
|
|
125
|
+
let messageId;
|
|
126
|
+
let receiptSequence;
|
|
127
|
+
let idleSequence;
|
|
128
|
+
let failure;
|
|
129
|
+
let stderr;
|
|
130
|
+
let captureError;
|
|
131
|
+
let shutdownResult = { status: "completed" };
|
|
132
|
+
context.registerShutdown(() => client.close());
|
|
133
|
+
const closeOnAbort = () => { void client.close().catch(() => undefined); };
|
|
134
|
+
if (!context.signal.aborted)
|
|
135
|
+
context.signal.addEventListener("abort", closeOnAbort, { once: true });
|
|
136
|
+
const retainNotification = async (notification) => {
|
|
137
|
+
const sourceIdentity = notificationIdentity(notification);
|
|
138
|
+
let retained;
|
|
139
|
+
try {
|
|
140
|
+
retained = await capture.record({
|
|
141
|
+
kind: "notification",
|
|
142
|
+
method: notification.method,
|
|
143
|
+
sessionId: notificationSessionId(notification, configuration.sessionId),
|
|
144
|
+
...(sourceIdentity === undefined ? {} : { sourceIdentity }),
|
|
145
|
+
payload: notification.params,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
captureError ??= sanitizedError(error, sensitiveValues);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
if (receiptSequence === undefined && messageId !== undefined
|
|
153
|
+
&& isInboxReceipt(notification, configuration.sessionId, messageId)) {
|
|
154
|
+
receiptSequence = retained.sequence;
|
|
155
|
+
}
|
|
156
|
+
if (receiptSequence !== undefined && isRootIdle(notification, configuration.sessionId)) {
|
|
157
|
+
idleSequence = retained.sequence;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
await capture.record({ kind: "composition", payload: composition });
|
|
161
|
+
await capture.record({ kind: "capability", payload: capabilities });
|
|
162
|
+
try {
|
|
163
|
+
throwIfAborted(context.signal);
|
|
164
|
+
const initialize = {
|
|
165
|
+
cwd: composition.workspaceCwd,
|
|
166
|
+
provider: composition.route.provider,
|
|
167
|
+
model: composition.route.model,
|
|
168
|
+
...(composition.route.maxTokens === undefined ? {} : { maxTokens: composition.route.maxTokens }),
|
|
169
|
+
};
|
|
170
|
+
await capture.record({ kind: "request", method: "initialize", payload: initialize });
|
|
171
|
+
client.start();
|
|
172
|
+
({ serverInfo } = await client.initialize(initialize));
|
|
173
|
+
await capture.record({ kind: "response", method: "initialize", payload: { serverInfo } });
|
|
174
|
+
throwIfAborted(context.signal);
|
|
175
|
+
const contentBlocks = typeof configuration.input === "string"
|
|
176
|
+
? [{ type: "text", text: configuration.input }]
|
|
177
|
+
: configuration.input;
|
|
178
|
+
await capture.record({ kind: "request", method: "session/prompt", sessionId: configuration.sessionId, payload: { contentBlocks } });
|
|
179
|
+
throwIfAborted(context.signal);
|
|
180
|
+
messageId = await client.prompt(configuration.sessionId, contentBlocks);
|
|
181
|
+
await capture.record({ kind: "response", method: "session/prompt", sessionId: configuration.sessionId, sourceIdentity: messageId, payload: { messageId } });
|
|
182
|
+
const deadline = Date.now() + (configuration.activityTimeoutMs ?? context.budgetMs ?? 60_000);
|
|
183
|
+
while (idleSequence === undefined) {
|
|
184
|
+
await retainNotification(await nextNotification(subscription, context.signal, deadline));
|
|
185
|
+
}
|
|
186
|
+
status = "completed";
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
failure = error;
|
|
190
|
+
status = context.signal.aborted || error instanceof DeepSeekInterruptedError
|
|
191
|
+
? "interrupted"
|
|
192
|
+
: error instanceof DeepSeekActivityTimeoutError ? "stopped" : "failed";
|
|
193
|
+
const diagnostic = sanitizedError(error, sensitiveValues);
|
|
194
|
+
stderr = extractStderr(diagnostic);
|
|
195
|
+
try {
|
|
196
|
+
await capture.record({ kind: "error", payload: { name: errorName(error), message: withoutStderr(diagnostic) } });
|
|
197
|
+
if (stderr !== undefined)
|
|
198
|
+
await capture.record({ kind: "diagnostic", stream: "stderr", payload: { text: stderr } });
|
|
199
|
+
}
|
|
200
|
+
catch (recordError) {
|
|
201
|
+
captureError = sanitizedError(recordError, sensitiveValues);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
context.signal.removeEventListener("abort", closeOnAbort);
|
|
206
|
+
try {
|
|
207
|
+
await capture.record({ kind: "request", method: "client.close" });
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
captureError ??= sanitizedError(error, sensitiveValues);
|
|
211
|
+
}
|
|
212
|
+
let closeFailure;
|
|
213
|
+
try {
|
|
214
|
+
await client.close();
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
closeFailure = error;
|
|
218
|
+
}
|
|
219
|
+
for (let notification = subscription.tryNext(); notification !== undefined; notification = subscription.tryNext()) {
|
|
220
|
+
try {
|
|
221
|
+
await retainNotification(notification);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
captureError ??= sanitizedError(error, sensitiveValues);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
subscription.close();
|
|
228
|
+
if (closeFailure === undefined) {
|
|
229
|
+
try {
|
|
230
|
+
await capture.record({ kind: "response", method: "client.close", payload: { status: "runtime-reaped" } });
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
captureError ??= sanitizedError(error, sensitiveValues);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const error = closeFailure;
|
|
238
|
+
const message = sanitizedError(error, sensitiveValues);
|
|
239
|
+
shutdownResult = { status: "failed", error: message };
|
|
240
|
+
try {
|
|
241
|
+
await capture.record({ kind: "error", method: "client.close", payload: { name: errorName(error), message: withoutStderr(message) } });
|
|
242
|
+
}
|
|
243
|
+
catch (recordError) {
|
|
244
|
+
captureError ??= sanitizedError(recordError, sensitiveValues);
|
|
245
|
+
}
|
|
246
|
+
const tail = extractStderr(message);
|
|
247
|
+
if (tail !== undefined) {
|
|
248
|
+
stderr = tail;
|
|
249
|
+
try {
|
|
250
|
+
await capture.record({ kind: "diagnostic", method: "client.close", stream: "stderr", payload: { text: tail } });
|
|
251
|
+
}
|
|
252
|
+
catch (recordError) {
|
|
253
|
+
captureError ??= sanitizedError(recordError, sensitiveValues);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (status === "completed") {
|
|
257
|
+
status = "failed";
|
|
258
|
+
failure = error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const report = {
|
|
263
|
+
status,
|
|
264
|
+
composition,
|
|
265
|
+
capabilities,
|
|
266
|
+
records: capture.report(),
|
|
267
|
+
relatedSessionIds: relatedSessionIds(capture.report(), configuration.sessionId),
|
|
268
|
+
...(serverInfo === undefined ? {} : { serverInfo }),
|
|
269
|
+
...(messageId === undefined ? {} : { messageId }),
|
|
270
|
+
...(receiptSequence === undefined ? {} : { receiptSequence }),
|
|
271
|
+
...(idleSequence === undefined ? {} : { idleSequence }),
|
|
272
|
+
...(failure === undefined ? {} : { error: sanitizedError(failure, sensitiveValues) }),
|
|
273
|
+
...(captureError === undefined ? {} : { captureError }),
|
|
274
|
+
...(stderr === undefined ? {} : { stderr }),
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
status,
|
|
278
|
+
...(status === "failed" ? { failureClass: "infrastructure" } : {}),
|
|
279
|
+
...(status === "stopped" ? { stopReason: "budget" } : {}),
|
|
280
|
+
...(report.error === undefined ? {} : { error: report.error }),
|
|
281
|
+
...(captureError === undefined ? {} : { captureError }),
|
|
282
|
+
completionEvidence: {
|
|
283
|
+
authority: "durable-receipt-to-whole-agent-idle",
|
|
284
|
+
...(messageId === undefined ? {} : { messageId }),
|
|
285
|
+
...(receiptSequence === undefined ? {} : { receiptSequence }),
|
|
286
|
+
...(idleSequence === undefined ? {} : { idleSequence }),
|
|
287
|
+
promptResult: { status: "unsupported" },
|
|
288
|
+
},
|
|
289
|
+
evidence: report,
|
|
290
|
+
shutdownResult,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
export function qualifiedDeepSeekCapture(runId, attemptId, report, artifactId = "deepseek-session") {
|
|
294
|
+
const qualification = deepSeekCaptureQualification(report.records, report.status === "completed", report.receiptSequence, report.idleSequence, report.captureError);
|
|
295
|
+
return {
|
|
296
|
+
runId,
|
|
297
|
+
attemptId,
|
|
298
|
+
qualification,
|
|
299
|
+
records: report.records.map((record) => ({
|
|
300
|
+
reference: { artifactId, recordLocator: `line:${record.sequence}` },
|
|
301
|
+
record,
|
|
302
|
+
})),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/** Reapply the native lifecycle gate while preserving retained physical JSONL locators. */
|
|
306
|
+
export function qualifyRetainedDeepSeekCapture(input, sessionId, completed) {
|
|
307
|
+
const rootSessionId = required(sessionId ?? "", "Retained DeepSeek root session ID");
|
|
308
|
+
const records = input.records.map(({ record }) => record);
|
|
309
|
+
const children = new Map();
|
|
310
|
+
for (const observation of records) {
|
|
311
|
+
const payload = record(observation.payload) ?? {};
|
|
312
|
+
if (observation.kind === "composition" && record(payload.runtime)?.clientVersion !== DEEPSEEK_SDK_VERSION) {
|
|
313
|
+
throw new Error("Retained DeepSeek capture is unqualified: native client version differs from the pinned runtime.");
|
|
314
|
+
}
|
|
315
|
+
if (observation.kind === "notification" && ["subagent.started", "subagent.finished"].includes(observation.method ?? "")
|
|
316
|
+
&& typeof payload.parentSessionId === "string" && typeof payload.childSessionId === "string") {
|
|
317
|
+
children.set(payload.parentSessionId, [...(children.get(payload.parentSessionId) ?? []), payload.childSessionId]);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const allowed = new Set([rootSessionId]);
|
|
321
|
+
const pending = [rootSessionId];
|
|
322
|
+
for (const parent of pending)
|
|
323
|
+
for (const child of children.get(parent) ?? []) {
|
|
324
|
+
if (!allowed.has(child)) {
|
|
325
|
+
allowed.add(child);
|
|
326
|
+
pending.push(child);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (const observation of records) {
|
|
330
|
+
const payload = record(observation.payload) ?? {};
|
|
331
|
+
if ([observation.sessionId, payload.sessionId, payload.parentSessionId, payload.childSessionId]
|
|
332
|
+
.some((id) => typeof id === "string" && !allowed.has(id))
|
|
333
|
+
|| typeof payload.sessionId === "string" && observation.sessionId !== payload.sessionId) {
|
|
334
|
+
throw new Error("Retained DeepSeek capture is unqualified: unrelated or contradictory native session identity.");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
let messageId;
|
|
338
|
+
let receiptSequence;
|
|
339
|
+
let idleSequence;
|
|
340
|
+
for (const observation of records) {
|
|
341
|
+
const payload = record(observation.payload) ?? {};
|
|
342
|
+
if (observation.kind === "response" && observation.method === "session/prompt" && observation.sessionId === rootSessionId) {
|
|
343
|
+
messageId = typeof payload.messageId === "string" && payload.messageId.trim() !== "" ? payload.messageId : undefined;
|
|
344
|
+
}
|
|
345
|
+
if (observation.kind !== "notification")
|
|
346
|
+
continue;
|
|
347
|
+
const notification = { method: observation.method, params: payload };
|
|
348
|
+
if (receiptSequence === undefined && messageId !== undefined && isInboxReceipt(notification, rootSessionId, messageId))
|
|
349
|
+
receiptSequence = observation.sequence;
|
|
350
|
+
if (receiptSequence !== undefined && isRootIdle(notification, rootSessionId))
|
|
351
|
+
idleSequence = observation.sequence;
|
|
352
|
+
}
|
|
353
|
+
if (messageId === undefined || !records.some((observation) => observation.kind === "notification" && observation.method === "session.event"
|
|
354
|
+
&& allowed.has(nativeSessionId(observation) ?? ""))) {
|
|
355
|
+
throw new Error("Retained DeepSeek capture is unqualified: root prompt receipt or related durable session evidence is missing.");
|
|
356
|
+
}
|
|
357
|
+
const qualification = deepSeekCaptureQualification(records, completed, receiptSequence, idleSequence);
|
|
358
|
+
return { ...input, qualification: input.qualification === "qualified-with-gaps" ? input.qualification : qualification };
|
|
359
|
+
}
|
|
360
|
+
function deepSeekCaptureQualification(records, completed, receiptSequence, idleSequence, captureError) {
|
|
361
|
+
if (captureError !== undefined)
|
|
362
|
+
throw new Error(`DeepSeek capture is unqualified: ${captureError}`);
|
|
363
|
+
const hasSessionEvent = records.some((record) => record.method === "session.event");
|
|
364
|
+
const has = (kind, method) => records.some((record) => record.kind === kind && (method === undefined || record.method === method));
|
|
365
|
+
const requiredBoundary = has("composition") && has("capability")
|
|
366
|
+
&& has("response", "initialize") && has("response", "session/prompt") && hasSessionEvent;
|
|
367
|
+
if (!requiredBoundary)
|
|
368
|
+
throw new Error("DeepSeek capture is unqualified: runtime, prompt receipt, or durable session evidence is missing.");
|
|
369
|
+
const reap = records.find((observation) => observation.kind === "response" && observation.method === "client.close"
|
|
370
|
+
&& record(observation.payload)?.status === "runtime-reaped");
|
|
371
|
+
const complete = receiptSequence !== undefined && idleSequence !== undefined
|
|
372
|
+
&& reap !== undefined && reap.sequence > idleSequence;
|
|
373
|
+
if (completed && !complete)
|
|
374
|
+
throw new Error("Completed DeepSeek capture lacks durable receipt-to-idle or clean runtime-reap evidence.");
|
|
375
|
+
return complete ? "qualified" : "qualified-with-gaps";
|
|
376
|
+
}
|
|
377
|
+
export const DEEPSEEK_CAPABILITY_PROFILE = {
|
|
378
|
+
schemaVersion: "ebo.adapter-capability-profile/v1",
|
|
379
|
+
adapterId: DEEPSEEK_ADAPTER_ID,
|
|
380
|
+
harness: DEEPSEEK_HARNESS_ID,
|
|
381
|
+
nativeTypes: [
|
|
382
|
+
"composition", "capability", "diagnostic", "error", "initialize", "session/prompt",
|
|
383
|
+
"session.event", "session.status", "subagent.started", "subagent.finished", "client.close",
|
|
384
|
+
],
|
|
385
|
+
families: {
|
|
386
|
+
message: { status: "available" },
|
|
387
|
+
"model-request": { status: "available" },
|
|
388
|
+
tool: { status: "available" },
|
|
389
|
+
context: { status: "available" },
|
|
390
|
+
permission: { status: "unsupported" },
|
|
391
|
+
delegation: { status: "available" },
|
|
392
|
+
artifact: { status: "partial" },
|
|
393
|
+
validation: { status: "partial" },
|
|
394
|
+
runtime: { status: "available" },
|
|
395
|
+
outcome: { status: "available" },
|
|
396
|
+
},
|
|
397
|
+
evidence: {
|
|
398
|
+
nativeOrder: { status: "partial" },
|
|
399
|
+
nativeTime: { status: "partial" },
|
|
400
|
+
parentage: { status: "partial" },
|
|
401
|
+
content: { status: "partial" },
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
export function normalizeDeepSeekCapture(input) {
|
|
405
|
+
const eventBySessionSequence = new Map();
|
|
406
|
+
const mapped = input.records.flatMap(({ reference, record }) => {
|
|
407
|
+
const family = eventFamily(record);
|
|
408
|
+
if (family === undefined)
|
|
409
|
+
return [];
|
|
410
|
+
const id = `${input.attemptId}-deepseek-${record.sequence}`;
|
|
411
|
+
const event = nativeSessionEvent(record);
|
|
412
|
+
const sessionId = nativeSessionId(record);
|
|
413
|
+
const nativeSequence = nativeSessionSequence(event);
|
|
414
|
+
if (sessionId !== undefined && nativeSequence !== undefined) {
|
|
415
|
+
eventBySessionSequence.set(`${sessionId}:${nativeSequence}`, id);
|
|
416
|
+
}
|
|
417
|
+
return [{ reference, record, family, id, event, sessionId, nativeSequence }];
|
|
418
|
+
});
|
|
419
|
+
const events = mapped.map(({ reference, record, family, id, event, sessionId, nativeSequence }) => {
|
|
420
|
+
const sourceSequences = nativeSourceSequences(event);
|
|
421
|
+
const known = sessionId === undefined ? [] : sourceSequences.flatMap((sequence) => {
|
|
422
|
+
const eventId = eventBySessionSequence.get(`${sessionId}:${sequence}`);
|
|
423
|
+
return eventId === undefined ? [] : [{ kind: "caused-by", eventId }];
|
|
424
|
+
});
|
|
425
|
+
return {
|
|
426
|
+
schemaVersion: "ebo.uniform-event/v1",
|
|
427
|
+
id,
|
|
428
|
+
runId: input.runId,
|
|
429
|
+
attemptId: input.attemptId,
|
|
430
|
+
source: {
|
|
431
|
+
harness: DEEPSEEK_HARNESS_ID,
|
|
432
|
+
nativeType: record.method ?? record.kind,
|
|
433
|
+
nativeReference: reference,
|
|
434
|
+
},
|
|
435
|
+
nativeOrder: nativeSequence === undefined || sessionId === undefined
|
|
436
|
+
? { status: "known", value: record.sequence, domain: "deepseek-sdk-observations" }
|
|
437
|
+
: { status: "known", value: nativeSequence, domain: `deepseek-session:${sessionId}` },
|
|
438
|
+
nativeTime: nativeSessionTime(event),
|
|
439
|
+
actor: { kind: actorKind(record, event) },
|
|
440
|
+
family,
|
|
441
|
+
phase: eventPhase(record, event),
|
|
442
|
+
scope: sessionId === undefined
|
|
443
|
+
? { kind: "attempt", id: input.attemptId }
|
|
444
|
+
: { kind: "session", id: sessionId },
|
|
445
|
+
relations: {
|
|
446
|
+
parent: { status: "unknown", reason: "native record does not declare a uniform parent event" },
|
|
447
|
+
known,
|
|
448
|
+
},
|
|
449
|
+
attributes: eventAttributes(record, event),
|
|
450
|
+
content: hasReferencedContent(record, event)
|
|
451
|
+
? { status: "known", value: [{ nativeReference: { ...reference, recordLocator: `${reference.recordLocator}#/payload` } }] }
|
|
452
|
+
: { status: "unknown", reason: "native record carries no mapped content" },
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
const mappedReferences = new Set(mapped.map(({ reference }) => referenceKey(reference)));
|
|
456
|
+
return {
|
|
457
|
+
events,
|
|
458
|
+
unmapped: input.records.flatMap(({ reference, record }) => mappedReferences.has(referenceKey(reference))
|
|
459
|
+
? []
|
|
460
|
+
: [{ reference, reason: `native record ${record.method ?? record.kind} has no uniform mapping` }]),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
export function createDeepSeekHarnessAdapter() {
|
|
464
|
+
return {
|
|
465
|
+
capture: {
|
|
466
|
+
id: DEEPSEEK_ADAPTER_ID,
|
|
467
|
+
harness: DEEPSEEK_HARNESS_ID,
|
|
468
|
+
capture: async ({ context, configuration, capture }) => {
|
|
469
|
+
const result = await executeDeepSeekHarness(context, configuration, capture);
|
|
470
|
+
return qualifiedDeepSeekCapture(context.run.id, context.attempt.id, result.evidence);
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
normalization: {
|
|
474
|
+
id: DEEPSEEK_ADAPTER_ID,
|
|
475
|
+
harness: DEEPSEEK_HARNESS_ID,
|
|
476
|
+
capabilityProfile: DEEPSEEK_CAPABILITY_PROFILE,
|
|
477
|
+
normalize: async (input) => normalizeDeepSeekCapture(input),
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function clientOptions(configuration) {
|
|
482
|
+
const composition = configuration.composition;
|
|
483
|
+
return {
|
|
484
|
+
command: composition.launch.command,
|
|
485
|
+
args: [...composition.launch.args],
|
|
486
|
+
cwd: composition.launch.processCwd,
|
|
487
|
+
env: {
|
|
488
|
+
...structuredClone(configuration.env),
|
|
489
|
+
...(composition.launch.dshHome === undefined ? {} : { DSH_HOME: composition.launch.dshHome }),
|
|
490
|
+
},
|
|
491
|
+
...(configuration.requestTimeoutMs === undefined ? {} : { requestTimeoutMs: configuration.requestTimeoutMs }),
|
|
492
|
+
...(configuration.shutdownTimeoutMs === undefined ? {} : { shutdownTimeoutMs: configuration.shutdownTimeoutMs }),
|
|
493
|
+
...(configuration.disposeEofGraceMs === undefined ? {} : { disposeEofGraceMs: configuration.disposeEofGraceMs }),
|
|
494
|
+
...(configuration.disposeGraceMs === undefined ? {} : { disposeGraceMs: configuration.disposeGraceMs }),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function validateConfiguration(configuration, context) {
|
|
498
|
+
assertArtifact("DeepSeek runtime composition", configuration.composition);
|
|
499
|
+
if (configuration.composition.launch.command !== process.execPath
|
|
500
|
+
|| configuration.composition.runtime.clientVersion !== DEEPSEEK_SDK_VERSION
|
|
501
|
+
|| configuration.composition.runtime.protocolVersion !== DEEPSEEK_SDK_VERSION) {
|
|
502
|
+
throw new Error("DeepSeek runtime composition does not match the pinned public client runtime.");
|
|
503
|
+
}
|
|
504
|
+
const expectedArgs = [
|
|
505
|
+
configuration.composition.launch.runtimeArtifact.locator,
|
|
506
|
+
"--profile",
|
|
507
|
+
configuration.composition.launch.profile,
|
|
508
|
+
...configuration.composition.patches.flatMap(({ locator }) => ["--patch", locator]),
|
|
509
|
+
];
|
|
510
|
+
if (JSON.stringify(configuration.composition.launch.args) !== JSON.stringify(expectedArgs)) {
|
|
511
|
+
throw new Error("DeepSeek launch arguments do not match the retained runtime and patch artifacts.");
|
|
512
|
+
}
|
|
513
|
+
if (configuration.sessionId.trim() === "")
|
|
514
|
+
throw new Error("DeepSeek session ID is required.");
|
|
515
|
+
if (context.workspace?.status !== "ready" || context.workspace.path === undefined
|
|
516
|
+
|| resolve(context.workspace.path) !== resolve(configuration.composition.workspaceCwd)) {
|
|
517
|
+
throw new Error("DeepSeek runtime workspace must match the retained ready lifecycle workspace.");
|
|
518
|
+
}
|
|
519
|
+
for (const reference of [
|
|
520
|
+
configuration.composition.cordis,
|
|
521
|
+
configuration.composition.launch.runtimeArtifact,
|
|
522
|
+
...configuration.composition.patches,
|
|
523
|
+
...configuration.composition.plugins.flatMap(({ configuration: value }) => value ?? []),
|
|
524
|
+
...(configuration.composition.telemetry.artifact === undefined ? [] : [configuration.composition.telemetry.artifact]),
|
|
525
|
+
])
|
|
526
|
+
verifyFileReference(reference);
|
|
527
|
+
const keys = Object.keys(configuration.env).sort();
|
|
528
|
+
const allowed = configuration.composition.environment.allowedKeys;
|
|
529
|
+
if (keys.some((key) => !allowed.includes(key)))
|
|
530
|
+
throw new Error("DeepSeek child environment contains a key outside the recorded policy.");
|
|
531
|
+
const suppliedHome = configuration.env.DSH_HOME;
|
|
532
|
+
const retainedHome = configuration.composition.launch.dshHome;
|
|
533
|
+
if (retainedHome !== undefined && !allowed.includes("DSH_HOME")) {
|
|
534
|
+
throw new Error("DeepSeek effective child DSH_HOME is outside the recorded environment allowlist.");
|
|
535
|
+
}
|
|
536
|
+
if (retainedHome === undefined ? suppliedHome !== undefined
|
|
537
|
+
: suppliedHome !== undefined && resolve(suppliedHome) !== resolve(retainedHome)) {
|
|
538
|
+
throw new Error("DeepSeek child DSH_HOME does not match the retained runtime composition.");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function fileReference(baseDir, path) {
|
|
542
|
+
const locator = resolve(baseDir, path);
|
|
543
|
+
return { locator, digest: digestString(readFileSync(locator)) };
|
|
544
|
+
}
|
|
545
|
+
function verifyFileReference(reference) {
|
|
546
|
+
if (digestString(readFileSync(reference.locator)) !== reference.digest) {
|
|
547
|
+
throw new Error(`DeepSeek configuration digest mismatch for ${reference.locator}.`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function digestString(bytes) {
|
|
551
|
+
return `sha256:${digestBytes(bytes).value}`;
|
|
552
|
+
}
|
|
553
|
+
function assertArtifact(label, document) {
|
|
554
|
+
const errors = validateArtifact(label, document);
|
|
555
|
+
if (errors.length > 0)
|
|
556
|
+
throw new Error(errors.map(({ field, message }) => `${label} ${field}: ${message}`).join("\n"));
|
|
557
|
+
}
|
|
558
|
+
function required(value, label) {
|
|
559
|
+
if (value.trim() === "")
|
|
560
|
+
throw new Error(`${label} is required.`);
|
|
561
|
+
return value;
|
|
562
|
+
}
|
|
563
|
+
function positiveInteger(value, label) {
|
|
564
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
565
|
+
throw new Error(`${label} must be a positive safe integer.`);
|
|
566
|
+
return value;
|
|
567
|
+
}
|
|
568
|
+
class DeepSeekActivityTimeoutError extends Error {
|
|
569
|
+
}
|
|
570
|
+
class DeepSeekInterruptedError extends Error {
|
|
571
|
+
}
|
|
572
|
+
function throwIfAborted(signal) {
|
|
573
|
+
if (signal.aborted)
|
|
574
|
+
throw new DeepSeekInterruptedError("DeepSeek activity was interrupted.");
|
|
575
|
+
}
|
|
576
|
+
async function nextNotification(subscription, signal, deadline) {
|
|
577
|
+
if (signal.aborted)
|
|
578
|
+
throw new DeepSeekInterruptedError("DeepSeek activity was interrupted.");
|
|
579
|
+
const remaining = deadline - Date.now();
|
|
580
|
+
if (remaining <= 0)
|
|
581
|
+
throw new DeepSeekActivityTimeoutError("DeepSeek activity interval timed out.");
|
|
582
|
+
let timer;
|
|
583
|
+
let abort;
|
|
584
|
+
try {
|
|
585
|
+
return await Promise.race([
|
|
586
|
+
subscription.next(),
|
|
587
|
+
new Promise((_resolve, reject) => {
|
|
588
|
+
timer = setTimeout(() => reject(new DeepSeekActivityTimeoutError("DeepSeek activity interval timed out.")), remaining);
|
|
589
|
+
}),
|
|
590
|
+
new Promise((_resolve, reject) => {
|
|
591
|
+
abort = () => reject(new DeepSeekInterruptedError("DeepSeek activity was interrupted."));
|
|
592
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
593
|
+
}),
|
|
594
|
+
]);
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
if (timer !== undefined)
|
|
598
|
+
clearTimeout(timer);
|
|
599
|
+
if (abort !== undefined)
|
|
600
|
+
signal.removeEventListener("abort", abort);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function isInboxReceipt(notification, sessionId, messageId) {
|
|
604
|
+
if (notification.method !== "session.event" || notification.params.sessionId !== sessionId)
|
|
605
|
+
return false;
|
|
606
|
+
const event = record(notification.params.event);
|
|
607
|
+
const data = record(event?.data);
|
|
608
|
+
return event?.type === "agent/inbox/spliced" && Array.isArray(data?.inserted)
|
|
609
|
+
&& data.inserted.some((message) => record(message)?.id === messageId);
|
|
610
|
+
}
|
|
611
|
+
function isRootIdle(notification, sessionId) {
|
|
612
|
+
return notification.method === "session.status"
|
|
613
|
+
&& notification.params.sessionId === sessionId
|
|
614
|
+
&& notification.params.status === "idle";
|
|
615
|
+
}
|
|
616
|
+
function notificationIdentity(notification) {
|
|
617
|
+
if (notification.method === "session.event") {
|
|
618
|
+
const event = record(notification.params.event);
|
|
619
|
+
return typeof event?.seq === "number" ? String(event.seq) : undefined;
|
|
620
|
+
}
|
|
621
|
+
for (const key of ["childSessionId", "sessionId"]) {
|
|
622
|
+
if (typeof notification.params[key] === "string")
|
|
623
|
+
return notification.params[key];
|
|
624
|
+
}
|
|
625
|
+
return undefined;
|
|
626
|
+
}
|
|
627
|
+
function notificationSessionId(notification, rootSessionId) {
|
|
628
|
+
if (typeof notification.params.sessionId === "string")
|
|
629
|
+
return notification.params.sessionId;
|
|
630
|
+
if (typeof notification.params.childSessionId === "string")
|
|
631
|
+
return notification.params.childSessionId;
|
|
632
|
+
return rootSessionId;
|
|
633
|
+
}
|
|
634
|
+
function relatedSessionIds(records, rootSessionId) {
|
|
635
|
+
const ids = new Set();
|
|
636
|
+
for (const observation of records) {
|
|
637
|
+
const payload = record(observation.payload);
|
|
638
|
+
for (const value of [observation.sessionId, payload?.sessionId, payload?.childSessionId, payload?.parentSessionId]) {
|
|
639
|
+
if (typeof value === "string" && value !== rootSessionId)
|
|
640
|
+
ids.add(value);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return [...ids].sort();
|
|
644
|
+
}
|
|
645
|
+
function eventFamily(recordValue) {
|
|
646
|
+
if (["composition", "capability", "diagnostic"].includes(recordValue.kind))
|
|
647
|
+
return "runtime";
|
|
648
|
+
if (recordValue.kind === "error")
|
|
649
|
+
return "outcome";
|
|
650
|
+
if (["initialize", "session/prompt", "session.status", "client.close"].includes(recordValue.method ?? ""))
|
|
651
|
+
return "runtime";
|
|
652
|
+
if (["subagent.started", "subagent.finished"].includes(recordValue.method ?? ""))
|
|
653
|
+
return "delegation";
|
|
654
|
+
if (recordValue.method !== "session.event")
|
|
655
|
+
return undefined;
|
|
656
|
+
const type = nativeSessionEvent(recordValue)?.type;
|
|
657
|
+
if (typeof type !== "string")
|
|
658
|
+
return undefined;
|
|
659
|
+
if (type.startsWith("user/") || type.startsWith("assistant/"))
|
|
660
|
+
return "message";
|
|
661
|
+
if (type.startsWith("tool/"))
|
|
662
|
+
return "tool";
|
|
663
|
+
if (type.startsWith("request/"))
|
|
664
|
+
return "model-request";
|
|
665
|
+
if (type.startsWith("compaction/") || type === "session/end-seed")
|
|
666
|
+
return "context";
|
|
667
|
+
if (type.startsWith("validation/"))
|
|
668
|
+
return "validation";
|
|
669
|
+
if (type.startsWith("artifact/"))
|
|
670
|
+
return "artifact";
|
|
671
|
+
if (type === "turn/end")
|
|
672
|
+
return "outcome";
|
|
673
|
+
if (type.startsWith("turn/") || type.startsWith("step/") || type.startsWith("hook/") || type === "agent/inbox/spliced")
|
|
674
|
+
return "runtime";
|
|
675
|
+
return undefined;
|
|
676
|
+
}
|
|
677
|
+
function nativeSessionEvent(recordValue) {
|
|
678
|
+
if (recordValue.method !== "session.event")
|
|
679
|
+
return undefined;
|
|
680
|
+
return record(record(recordValue.payload)?.event);
|
|
681
|
+
}
|
|
682
|
+
function nativeSessionId(recordValue) {
|
|
683
|
+
const payload = record(recordValue.payload);
|
|
684
|
+
return typeof payload?.sessionId === "string" ? payload.sessionId : recordValue.sessionId;
|
|
685
|
+
}
|
|
686
|
+
function nativeSessionSequence(event) {
|
|
687
|
+
return Number.isSafeInteger(event?.seq) && event?.seq >= 0 ? event?.seq : undefined;
|
|
688
|
+
}
|
|
689
|
+
function nativeSessionTime(event) {
|
|
690
|
+
if (typeof event?.time !== "number" || !Number.isFinite(event.time)) {
|
|
691
|
+
return { status: "unknown", reason: "native record carries no valid event time" };
|
|
692
|
+
}
|
|
693
|
+
const timestamp = new Date(event.time);
|
|
694
|
+
return Number.isNaN(timestamp.valueOf())
|
|
695
|
+
? { status: "unknown", reason: "native event time is outside the supported timestamp range" }
|
|
696
|
+
: { status: "known", value: timestamp.toISOString() };
|
|
697
|
+
}
|
|
698
|
+
function nativeSourceSequences(event) {
|
|
699
|
+
return Array.isArray(event?.sourceEventSeqs)
|
|
700
|
+
? event.sourceEventSeqs.filter((value) => Number.isSafeInteger(value) && value >= 0)
|
|
701
|
+
: [];
|
|
702
|
+
}
|
|
703
|
+
function actorKind(recordValue, event) {
|
|
704
|
+
const type = typeof event?.type === "string" ? event.type : "";
|
|
705
|
+
if (type.startsWith("user/"))
|
|
706
|
+
return "user";
|
|
707
|
+
if (type.startsWith("assistant/") || type.startsWith("request/"))
|
|
708
|
+
return "model";
|
|
709
|
+
if (type.startsWith("tool/"))
|
|
710
|
+
return "tool";
|
|
711
|
+
if (recordValue.method?.startsWith("subagent."))
|
|
712
|
+
return "agent";
|
|
713
|
+
return "harness";
|
|
714
|
+
}
|
|
715
|
+
function eventPhase(recordValue, event) {
|
|
716
|
+
const type = typeof event?.type === "string" ? event.type : recordValue.method ?? "";
|
|
717
|
+
if (type.endsWith("/start") || recordValue.kind === "request")
|
|
718
|
+
return "before";
|
|
719
|
+
if (type.endsWith("/end") || type.endsWith("/result") || recordValue.kind === "response")
|
|
720
|
+
return "after";
|
|
721
|
+
if (type === "session.status")
|
|
722
|
+
return "during";
|
|
723
|
+
return "instant";
|
|
724
|
+
}
|
|
725
|
+
function eventAttributes(recordValue, event) {
|
|
726
|
+
const data = record(event?.data);
|
|
727
|
+
const attributes = {
|
|
728
|
+
recordKind: recordValue.kind,
|
|
729
|
+
...(recordValue.method === undefined ? {} : { method: recordValue.method }),
|
|
730
|
+
};
|
|
731
|
+
addAttribute(attributes, "eventType", event?.type);
|
|
732
|
+
addAttribute(attributes, "eventSeq", event?.seq);
|
|
733
|
+
addAttribute(attributes, "surface", event === undefined ? undefined : Object.hasOwn(event, "surfaceOp") ? "surface" : "log-only");
|
|
734
|
+
addAttribute(attributes, "surfaceOp", typeof event?.surfaceOp === "string" ? event.surfaceOp : record(event?.surfaceOp)?.op);
|
|
735
|
+
for (const key of ["turn", "step", "callId", "name", "status", "reason"])
|
|
736
|
+
addAttribute(attributes, key, data?.[key]);
|
|
737
|
+
return attributes;
|
|
738
|
+
}
|
|
739
|
+
function addAttribute(target, key, value) {
|
|
740
|
+
if (typeof value === "string" && value.length <= 512 || typeof value === "number" && Number.isFinite(value) || typeof value === "boolean") {
|
|
741
|
+
target[key] = value;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function hasReferencedContent(recordValue, event) {
|
|
745
|
+
if (recordValue.method === "session/prompt" && recordValue.kind === "request")
|
|
746
|
+
return true;
|
|
747
|
+
const type = typeof event?.type === "string" ? event.type : "";
|
|
748
|
+
return type.startsWith("user/") || type.startsWith("assistant/") || type.startsWith("tool/")
|
|
749
|
+
|| type.startsWith("compaction/") || type.startsWith("request/") || type.startsWith("validation/");
|
|
750
|
+
}
|
|
751
|
+
function sanitizedError(error, sensitiveValues) {
|
|
752
|
+
let message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
753
|
+
for (const value of sensitiveValues)
|
|
754
|
+
if (value !== "")
|
|
755
|
+
message = message.replaceAll(value, "[redacted]");
|
|
756
|
+
return Buffer.from(message).subarray(0, 64 * 1024).toString("utf8");
|
|
757
|
+
}
|
|
758
|
+
function extractStderr(message) {
|
|
759
|
+
const marker = "stderr tail:\n";
|
|
760
|
+
const index = message.lastIndexOf(marker);
|
|
761
|
+
return index < 0 ? undefined : message.slice(index + marker.length);
|
|
762
|
+
}
|
|
763
|
+
function withoutStderr(message) {
|
|
764
|
+
const index = message.lastIndexOf("stderr tail:\n");
|
|
765
|
+
return index < 0 ? message : message.slice(0, index).trimEnd();
|
|
766
|
+
}
|
|
767
|
+
function errorName(error) {
|
|
768
|
+
return error instanceof Error ? error.name : "Error";
|
|
769
|
+
}
|
|
770
|
+
function record(value) {
|
|
771
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
772
|
+
? value
|
|
773
|
+
: undefined;
|
|
774
|
+
}
|
|
775
|
+
function referenceKey(reference) {
|
|
776
|
+
return JSON.stringify([reference.artifactId, reference.recordLocator]);
|
|
777
|
+
}
|