flavor-code 1.3.3 → 1.3.6
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/dist/{app-NXOGWGSI.js → app-CJSBYZAD.js} +1 -1
- package/dist/{chunk-YY5H6GFN.js → chunk-BR3CWUYR.js} +299 -93
- package/dist/{chunk-JKJOQRAL.js → chunk-ZCEUFSHA.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/desktop/main.js +319 -113
- package/dist/desktop-renderer/assets/{index-CnGHSdJ6.js → index-bFGsR_uj.js} +2 -2
- package/dist/desktop-renderer/assets/{interactive-terminal-BpNgJ_fv.js → interactive-terminal-BwS6MvIe.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/hooks/bus.d.ts +7 -0
- package/dist/sdk/index.js +2 -2
- package/dist/tools/ask-user-question.d.ts +4 -1
- package/dist/tools/types.d.ts +2 -0
- package/package.json +2 -2
|
@@ -5006,7 +5006,7 @@ async function ensureOnce(options) {
|
|
|
5006
5006
|
|
|
5007
5007
|
// src/production.ts
|
|
5008
5008
|
import { readFile as readFile22 } from "fs/promises";
|
|
5009
|
-
import { createHash as createHash17, randomUUID as
|
|
5009
|
+
import { createHash as createHash17, randomUUID as randomUUID19 } from "crypto";
|
|
5010
5010
|
import { homedir as homedir2, release as osRelease, tmpdir as tmpdir3, version as osVersion } from "os";
|
|
5011
5011
|
import { basename as basename6, join as join24, relative as relative20, resolve as resolve27, sep as sep20 } from "path";
|
|
5012
5012
|
|
|
@@ -7484,11 +7484,18 @@ var DEFAULT_TOOL_OUTPUT_LIMITS = Object.freeze({
|
|
|
7484
7484
|
perTurnChars: 2e5
|
|
7485
7485
|
});
|
|
7486
7486
|
var PreToolUsePayload = z9.object({
|
|
7487
|
+
// Optional for backwards-compatible hook updatedInput payloads; runtime
|
|
7488
|
+
// emissions always populate it.
|
|
7489
|
+
toolCallId: z9.string().optional(),
|
|
7487
7490
|
tool: z9.string(),
|
|
7488
7491
|
input: z9.unknown(),
|
|
7489
7492
|
agent: z9.enum(["main", "subagent"])
|
|
7490
7493
|
});
|
|
7491
|
-
var PermissionRequestPayload = PreToolUsePayload.extend({
|
|
7494
|
+
var PermissionRequestPayload = PreToolUsePayload.extend({
|
|
7495
|
+
reason: z9.string().optional(),
|
|
7496
|
+
toolCategory: z9.string(),
|
|
7497
|
+
allowAlways: z9.boolean()
|
|
7498
|
+
});
|
|
7492
7499
|
var PostToolUsePayload = PreToolUsePayload.extend({ output: z9.unknown() });
|
|
7493
7500
|
var PostToolUseFailurePayload = PreToolUsePayload.extend({
|
|
7494
7501
|
error: z9.object({ code: z9.string(), message: z9.string() })
|
|
@@ -7605,30 +7612,31 @@ var ToolRuntime = class {
|
|
|
7605
7612
|
const tool = this.#tools.get(call.name);
|
|
7606
7613
|
if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
|
|
7607
7614
|
const signal = context.signal ?? new AbortController().signal;
|
|
7615
|
+
const toolCallId = call.id ?? randomUUID7();
|
|
7608
7616
|
const validation = this.validate(call);
|
|
7609
7617
|
if (!validation.ok) {
|
|
7610
|
-
return this.#fail(call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
7618
|
+
return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
7611
7619
|
}
|
|
7612
7620
|
let input = validation.input;
|
|
7613
7621
|
try {
|
|
7614
7622
|
const pre = await this.#hooks.emit({
|
|
7615
7623
|
version: 1,
|
|
7616
7624
|
type: "PreToolUse",
|
|
7617
|
-
payload: { tool: tool.name, input, agent: context.agent }
|
|
7625
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent }
|
|
7618
7626
|
});
|
|
7619
7627
|
if (pre.updatedInput !== void 0) {
|
|
7620
7628
|
const payload = PreToolUsePayload.parse(pre.updatedInput);
|
|
7621
7629
|
if (payload.tool !== tool.name || payload.agent !== context.agent) {
|
|
7622
|
-
return this.#fail(tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
|
|
7630
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
|
|
7623
7631
|
}
|
|
7624
7632
|
try {
|
|
7625
7633
|
input = tool.inputSchema.parse(payload.input);
|
|
7626
7634
|
} catch (error) {
|
|
7627
|
-
return this.#fail(tool.name, payload.input, context.agent, "invalid_input", message(error));
|
|
7635
|
+
return this.#fail(toolCallId, tool.name, payload.input, context.agent, "invalid_input", message(error));
|
|
7628
7636
|
}
|
|
7629
7637
|
}
|
|
7630
7638
|
if (pre.decision === "deny") {
|
|
7631
|
-
return this.#fail(tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
|
|
7639
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
|
|
7632
7640
|
}
|
|
7633
7641
|
const request = {
|
|
7634
7642
|
agent: context.agent,
|
|
@@ -7639,13 +7647,13 @@ var ToolRuntime = class {
|
|
|
7639
7647
|
let permission = this.#permissions.decide(request);
|
|
7640
7648
|
const nonCacheableApproval = request.allowAlways === false || permission.allowAlways === false;
|
|
7641
7649
|
if (permission.decision === "deny") {
|
|
7642
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
|
|
7650
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
|
|
7643
7651
|
}
|
|
7644
7652
|
if (permission.decision === "ask" && context.agent === "main" && !nonCacheableApproval && this.#permissions.mode === "auto" && this.#permissions.profile === "standard" && this.#classify !== void 0) {
|
|
7645
7653
|
try {
|
|
7646
7654
|
const classified = await this.#classify(request, signal);
|
|
7647
7655
|
if (classified.decision === "deny") {
|
|
7648
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
|
|
7656
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
|
|
7649
7657
|
}
|
|
7650
7658
|
if (classified.decision === "allow") permission = classified;
|
|
7651
7659
|
else {
|
|
@@ -7662,13 +7670,21 @@ var ToolRuntime = class {
|
|
|
7662
7670
|
const requestDecision = await this.#hooks.emit({
|
|
7663
7671
|
version: 1,
|
|
7664
7672
|
type: "PermissionRequest",
|
|
7665
|
-
payload: {
|
|
7673
|
+
payload: {
|
|
7674
|
+
toolCallId,
|
|
7675
|
+
tool: tool.name,
|
|
7676
|
+
input: tool.permissionInput?.(input) ?? input,
|
|
7677
|
+
agent: context.agent,
|
|
7678
|
+
reason,
|
|
7679
|
+
toolCategory: getToolCategory(tool.name),
|
|
7680
|
+
allowAlways
|
|
7681
|
+
}
|
|
7666
7682
|
});
|
|
7667
7683
|
if (requestDecision.decision === "deny") {
|
|
7668
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
|
|
7684
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
|
|
7669
7685
|
}
|
|
7670
7686
|
if (requestDecision.updatedInput !== void 0) {
|
|
7671
|
-
return this.#fail(tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
|
|
7687
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
|
|
7672
7688
|
}
|
|
7673
7689
|
if (this.#hooks.hasListeners("PermissionRequest") && requestDecision.decision === "allow") {
|
|
7674
7690
|
const ctx = requestDecision.additionalContext ?? "";
|
|
@@ -7677,18 +7693,18 @@ var ToolRuntime = class {
|
|
|
7677
7693
|
if (allowAlways && category !== "destructive") this.#alwaysAllowed.add(category);
|
|
7678
7694
|
}
|
|
7679
7695
|
} else if (context.agent !== "main" && this.#permissions.mode !== "bubble") {
|
|
7680
|
-
return this.#fail(tool.name, input, context.agent, "approval_required", reason);
|
|
7696
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "approval_required", reason);
|
|
7681
7697
|
} else if (signal.aborted) {
|
|
7682
7698
|
throw signal.reason;
|
|
7683
7699
|
} else {
|
|
7684
7700
|
const category = getToolCategory(tool.name);
|
|
7685
7701
|
if (allowAlways && this.#alwaysAllowed.has(category)) {
|
|
7686
7702
|
} else if (this.#approve === void 0) {
|
|
7687
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", reason);
|
|
7703
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", reason);
|
|
7688
7704
|
} else {
|
|
7689
7705
|
const decision = await this.#approve({ ...request, reason, ...allowAlways ? {} : { allowAlways: false } }, signal);
|
|
7690
7706
|
if (decision === "deny") {
|
|
7691
|
-
return this.#fail(tool.name, input, context.agent, "user_denied", reason);
|
|
7707
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "user_denied", reason);
|
|
7692
7708
|
}
|
|
7693
7709
|
if (decision === "always" && allowAlways && category !== "destructive") {
|
|
7694
7710
|
this.#alwaysAllowed.add(category);
|
|
@@ -7702,7 +7718,7 @@ var ToolRuntime = class {
|
|
|
7702
7718
|
try {
|
|
7703
7719
|
rawOutput = tool.outputSchema === void 0 ? executed : tool.outputSchema.parse(executed);
|
|
7704
7720
|
} catch (error) {
|
|
7705
|
-
return this.#fail(tool.name, input, context.agent, "invalid_output", message(error));
|
|
7721
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
|
|
7706
7722
|
}
|
|
7707
7723
|
const declaredPresentation = tool.presentResult?.(rawOutput, input);
|
|
7708
7724
|
const presentation = declaredPresentation ?? getToolPresentation(rawOutput);
|
|
@@ -7713,7 +7729,7 @@ var ToolRuntime = class {
|
|
|
7713
7729
|
await this.#hooks.emit({
|
|
7714
7730
|
version: 1,
|
|
7715
7731
|
type: "PostToolUse",
|
|
7716
|
-
payload: { tool: tool.name, input, agent: context.agent, output }
|
|
7732
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
|
|
7717
7733
|
});
|
|
7718
7734
|
return {
|
|
7719
7735
|
ok: true,
|
|
@@ -7723,7 +7739,7 @@ var ToolRuntime = class {
|
|
|
7723
7739
|
...additionalContext === void 0 || additionalContext.length === 0 ? {} : { additionalContext }
|
|
7724
7740
|
};
|
|
7725
7741
|
} catch (error) {
|
|
7726
|
-
return this.#fail(tool.name, input, context.agent, "tool_error", message(error));
|
|
7742
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "tool_error", message(error));
|
|
7727
7743
|
}
|
|
7728
7744
|
}
|
|
7729
7745
|
async #limitOutput(tool, output) {
|
|
@@ -7771,13 +7787,13 @@ var ToolRuntime = class {
|
|
|
7771
7787
|
const limited = await this.#limitOutput(tool, content);
|
|
7772
7788
|
return typeof limited === "string" ? limited : serializeToolOutput(limited);
|
|
7773
7789
|
}
|
|
7774
|
-
async #fail(tool, input, agent, code, errorMessage2) {
|
|
7790
|
+
async #fail(toolCallId, tool, input, agent, code, errorMessage2) {
|
|
7775
7791
|
const result = { ok: false, error: { code, message: errorMessage2 } };
|
|
7776
7792
|
try {
|
|
7777
7793
|
await this.#hooks.emit({
|
|
7778
7794
|
version: 1,
|
|
7779
7795
|
type: "PostToolUseFailure",
|
|
7780
|
-
payload: { tool, input, agent, error: result.error }
|
|
7796
|
+
payload: { toolCallId, tool, input, agent, error: result.error }
|
|
7781
7797
|
});
|
|
7782
7798
|
} catch {
|
|
7783
7799
|
}
|
|
@@ -7985,7 +8001,7 @@ function toModelTool(tool) {
|
|
|
7985
8001
|
|
|
7986
8002
|
// src/harness/journal.ts
|
|
7987
8003
|
import { createHash as createHash6, randomUUID as randomUUID8 } from "crypto";
|
|
7988
|
-
import { closeSync, constants, existsSync as existsSync2, fsyncSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, realpathSync as realpathSync2, statSync, truncateSync, writeSync } from "fs";
|
|
8004
|
+
import { closeSync, constants, existsSync as existsSync2, fsyncSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, realpathSync as realpathSync2, renameSync, statSync, truncateSync, unlinkSync, writeSync } from "fs";
|
|
7989
8005
|
import { join as join8, relative as relative7, resolve as resolve8, sep as sep7 } from "path";
|
|
7990
8006
|
import { z as z10 } from "zod";
|
|
7991
8007
|
|
|
@@ -7996,8 +8012,10 @@ function harnessInvariantViolations(records) {
|
|
|
7996
8012
|
const tools = /* @__PURE__ */ new Set();
|
|
7997
8013
|
const models = /* @__PURE__ */ new Set();
|
|
7998
8014
|
const turns = /* @__PURE__ */ new Set();
|
|
8015
|
+
const firstSequence = records[0]?.sequence ?? 1;
|
|
7999
8016
|
records.forEach((record, index) => {
|
|
8000
|
-
|
|
8017
|
+
const expectedSequence = firstSequence + index;
|
|
8018
|
+
if (record.sequence !== expectedSequence) violations.push(`sequence ${record.sequence} is not ${expectedSequence}`);
|
|
8001
8019
|
const id = typeof record.payload.id === "string" ? record.payload.id : "";
|
|
8002
8020
|
if (record.type === "queue.admitted") {
|
|
8003
8021
|
if (queues.has(id)) violations.push(`queue ${id} was admitted twice`);
|
|
@@ -8013,13 +8031,13 @@ function harnessInvariantViolations(records) {
|
|
|
8013
8031
|
if (!tools.delete(id)) violations.push(`${record.type} references unknown tool ${id}`);
|
|
8014
8032
|
} else if (record.type === "model.requested") {
|
|
8015
8033
|
if (models.has(id)) violations.push(`model ${id} was requested twice`);
|
|
8016
|
-
if (!
|
|
8034
|
+
if (!isHash(record.payload.messagesHash)) violations.push(`model ${id} has no valid messages hash`);
|
|
8017
8035
|
models.add(id);
|
|
8018
8036
|
} else if (record.type === "model.completed") {
|
|
8019
8037
|
if (!models.delete(id)) violations.push(`model.completed references unknown model ${id}`);
|
|
8020
8038
|
} else if (record.type === "turn.started") {
|
|
8021
8039
|
if (turns.has(id)) violations.push(`turn ${id} was started twice`);
|
|
8022
|
-
if (!(
|
|
8040
|
+
if (!isHash(record.payload.promptHash)) violations.push(`turn ${id} has no valid prompt hash`);
|
|
8023
8041
|
turns.add(id);
|
|
8024
8042
|
} else if (record.type === "turn.completed" || record.type === "turn.interrupted") {
|
|
8025
8043
|
if (!turns.delete(id)) violations.push(`${record.type} references unknown turn ${id}`);
|
|
@@ -8027,6 +8045,9 @@ function harnessInvariantViolations(records) {
|
|
|
8027
8045
|
});
|
|
8028
8046
|
return violations;
|
|
8029
8047
|
}
|
|
8048
|
+
function isHash(value) {
|
|
8049
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
8050
|
+
}
|
|
8030
8051
|
function assertHarnessInvariants(records) {
|
|
8031
8052
|
const violations = harnessInvariantViolations(records);
|
|
8032
8053
|
if (violations.length > 0) throw new Error(`Harness invariant violation: ${violations.join("; ")}`);
|
|
@@ -8107,7 +8128,7 @@ var HarnessJournal = class {
|
|
|
8107
8128
|
}
|
|
8108
8129
|
startTurn(config, prompt) {
|
|
8109
8130
|
const id = randomUUID8();
|
|
8110
|
-
this.#append("turn.started", { id,
|
|
8131
|
+
this.#append("turn.started", { id, configHash: hashJson(config), promptHash: hashJson(prompt) });
|
|
8111
8132
|
return id;
|
|
8112
8133
|
}
|
|
8113
8134
|
completeTurn(id) {
|
|
@@ -8118,7 +8139,14 @@ var HarnessJournal = class {
|
|
|
8118
8139
|
}
|
|
8119
8140
|
startModel(input) {
|
|
8120
8141
|
const id = randomUUID8();
|
|
8121
|
-
this.#append("model.requested", {
|
|
8142
|
+
this.#append("model.requested", {
|
|
8143
|
+
id,
|
|
8144
|
+
agent: input.agent,
|
|
8145
|
+
model: input.model,
|
|
8146
|
+
iteration: input.iteration,
|
|
8147
|
+
attempt: input.attempt,
|
|
8148
|
+
messagesHash: hashJson(input.messages)
|
|
8149
|
+
});
|
|
8122
8150
|
return id;
|
|
8123
8151
|
}
|
|
8124
8152
|
completeModel(id, completed, error) {
|
|
@@ -8126,11 +8154,11 @@ var HarnessJournal = class {
|
|
|
8126
8154
|
}
|
|
8127
8155
|
startTool(tool, input, retrySafe) {
|
|
8128
8156
|
const id = randomUUID8();
|
|
8129
|
-
this.#append("tool.started", { id, tool,
|
|
8157
|
+
this.#append("tool.started", { id, tool, inputHash: hashJson(input), retrySafe });
|
|
8130
8158
|
return id;
|
|
8131
8159
|
}
|
|
8132
8160
|
completeTool(id, result) {
|
|
8133
|
-
this.#append("tool.completed", { id,
|
|
8161
|
+
this.#append("tool.completed", { id, resultHash: hashJson(result) });
|
|
8134
8162
|
}
|
|
8135
8163
|
interruptTool(id, reason) {
|
|
8136
8164
|
this.#append("tool.interrupted", { id, reason });
|
|
@@ -8207,47 +8235,153 @@ var HarnessJournal = class {
|
|
|
8207
8235
|
const committed = raw.endsWith("\n") ? raw : raw.slice(0, Math.max(0, raw.lastIndexOf("\n") + 1));
|
|
8208
8236
|
if (committed.length !== raw.length) truncateSync(this.#path, Buffer.byteLength(committed));
|
|
8209
8237
|
const lines = committed.split("\n").filter(Boolean);
|
|
8210
|
-
let previousHash
|
|
8238
|
+
let previousHash;
|
|
8239
|
+
let expectedSequence;
|
|
8211
8240
|
for (const line of lines) {
|
|
8212
8241
|
const record = JournalRecordSchema.parse(JSON.parse(line));
|
|
8213
|
-
|
|
8242
|
+
expectedSequence ??= record.sequence;
|
|
8243
|
+
previousHash ??= record.previousHash;
|
|
8244
|
+
if (record.sequence !== expectedSequence) throw new Error("Harness journal sequence is not contiguous");
|
|
8214
8245
|
if (record.previousHash !== previousHash) throw new Error("Harness journal hash chain is broken");
|
|
8215
8246
|
if (record.hash !== recordHash(record)) throw new Error("Harness journal record hash is invalid");
|
|
8216
8247
|
this.#records.push(record);
|
|
8217
8248
|
previousHash = record.hash;
|
|
8249
|
+
expectedSequence += 1;
|
|
8218
8250
|
}
|
|
8219
|
-
this.#sequence = this.#records.
|
|
8220
|
-
this.#lastHash = previousHash;
|
|
8251
|
+
this.#sequence = this.#records.at(-1)?.sequence ?? 0;
|
|
8252
|
+
this.#lastHash = previousHash ?? null;
|
|
8221
8253
|
assertHarnessInvariants(this.#records);
|
|
8254
|
+
const hasRotatedPrefix = (this.#records[0]?.sequence ?? 1) !== 1;
|
|
8255
|
+
if (metadata.size > this.#maxBytes * 0.75 || hasRotatedPrefix || this.#records.some(hasLegacyPayload)) this.#compact();
|
|
8222
8256
|
}
|
|
8223
8257
|
#append(type, payload) {
|
|
8224
8258
|
const normalizedPayload = jsonSafeRecord(payload);
|
|
8259
|
+
let record = this.#createRecord(type, normalizedPayload);
|
|
8260
|
+
let line = `${JSON.stringify(record)}
|
|
8261
|
+
`;
|
|
8262
|
+
let priorBytes = existsSync2(this.#path) ? statSync(this.#path).size : 0;
|
|
8263
|
+
if (priorBytes + Buffer.byteLength(line) > this.#maxBytes) {
|
|
8264
|
+
this.#compact();
|
|
8265
|
+
record = this.#createRecord(type, normalizedPayload);
|
|
8266
|
+
line = `${JSON.stringify(record)}
|
|
8267
|
+
`;
|
|
8268
|
+
priorBytes = existsSync2(this.#path) ? statSync(this.#path).size : 0;
|
|
8269
|
+
if (priorBytes + Buffer.byteLength(line) > this.#maxBytes) {
|
|
8270
|
+
throw new Error(`Harness journal event exceeds the ${this.#maxBytes} byte bounded journal capacity`);
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
const fd = openSync(this.#path, constants.O_CREAT | constants.O_APPEND | constants.O_WRONLY, 384);
|
|
8274
|
+
try {
|
|
8275
|
+
writeSync(fd, line, void 0, "utf8");
|
|
8276
|
+
fsyncSync(fd);
|
|
8277
|
+
} finally {
|
|
8278
|
+
closeSync(fd);
|
|
8279
|
+
}
|
|
8280
|
+
this.#records.push(record);
|
|
8281
|
+
this.#sequence = record.sequence;
|
|
8282
|
+
this.#lastHash = record.hash;
|
|
8283
|
+
}
|
|
8284
|
+
#createRecord(type, payload) {
|
|
8225
8285
|
const unsigned = {
|
|
8226
8286
|
version: HARNESS_JOURNAL_VERSION,
|
|
8227
8287
|
sequence: this.#sequence + 1,
|
|
8228
8288
|
id: randomUUID8(),
|
|
8229
8289
|
timestamp: this.#now(),
|
|
8230
8290
|
type,
|
|
8231
|
-
payload
|
|
8291
|
+
payload,
|
|
8232
8292
|
previousHash: this.#lastHash
|
|
8233
8293
|
};
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
const
|
|
8238
|
-
|
|
8239
|
-
const
|
|
8294
|
+
return JournalRecordSchema.parse({ ...unsigned, hash: hashJson(unsigned) });
|
|
8295
|
+
}
|
|
8296
|
+
#compact() {
|
|
8297
|
+
const queues = /* @__PURE__ */ new Map();
|
|
8298
|
+
const claims = /* @__PURE__ */ new Map();
|
|
8299
|
+
const tools = /* @__PURE__ */ new Map();
|
|
8300
|
+
const models = /* @__PURE__ */ new Map();
|
|
8301
|
+
const turns = /* @__PURE__ */ new Map();
|
|
8302
|
+
let savepoint;
|
|
8303
|
+
for (const record of this.#records) {
|
|
8304
|
+
const id = typeof record.payload.id === "string" ? record.payload.id : "";
|
|
8305
|
+
if (record.type === "queue.admitted") queues.set(id, record);
|
|
8306
|
+
else if (record.type === "queue.claimed") claims.set(id, record);
|
|
8307
|
+
else if (record.type === "queue.released") claims.delete(id);
|
|
8308
|
+
else if (record.type === "queue.acked") {
|
|
8309
|
+
queues.delete(id);
|
|
8310
|
+
claims.delete(id);
|
|
8311
|
+
} else if (record.type === "tool.started") tools.set(id, record);
|
|
8312
|
+
else if (record.type === "tool.completed" || record.type === "tool.interrupted") tools.delete(id);
|
|
8313
|
+
else if (record.type === "model.requested") models.set(id, record);
|
|
8314
|
+
else if (record.type === "model.completed") models.delete(id);
|
|
8315
|
+
else if (record.type === "turn.started") turns.set(id, record);
|
|
8316
|
+
else if (record.type === "turn.completed" || record.type === "turn.interrupted") turns.delete(id);
|
|
8317
|
+
else if (record.type === "savepoint.created") savepoint = record;
|
|
8318
|
+
}
|
|
8319
|
+
const retained = [
|
|
8320
|
+
...queues.values(),
|
|
8321
|
+
...claims.values(),
|
|
8322
|
+
...tools.values(),
|
|
8323
|
+
...models.values(),
|
|
8324
|
+
...turns.values(),
|
|
8325
|
+
...savepoint === void 0 ? [] : [savepoint]
|
|
8326
|
+
].sort((left, right) => left.sequence - right.sequence);
|
|
8327
|
+
const compacted = [];
|
|
8328
|
+
let previousHash = null;
|
|
8329
|
+
for (const source of retained) {
|
|
8330
|
+
const unsigned = {
|
|
8331
|
+
version: HARNESS_JOURNAL_VERSION,
|
|
8332
|
+
sequence: compacted.length + 1,
|
|
8333
|
+
id: randomUUID8(),
|
|
8334
|
+
timestamp: source.timestamp,
|
|
8335
|
+
type: source.type,
|
|
8336
|
+
payload: compactPayload(source),
|
|
8337
|
+
previousHash
|
|
8338
|
+
};
|
|
8339
|
+
const record = JournalRecordSchema.parse({ ...unsigned, hash: hashJson(unsigned) });
|
|
8340
|
+
compacted.push(record);
|
|
8341
|
+
previousHash = record.hash;
|
|
8342
|
+
}
|
|
8343
|
+
const contents = compacted.map((record) => `${JSON.stringify(record)}
|
|
8344
|
+
`).join("");
|
|
8345
|
+
if (Buffer.byteLength(contents) > this.#maxBytes) return;
|
|
8346
|
+
const temporary = `${this.#path}.compact-${process.pid}-${randomUUID8()}`;
|
|
8347
|
+
const fd = openSync(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 384);
|
|
8240
8348
|
try {
|
|
8241
|
-
writeSync(fd,
|
|
8349
|
+
writeSync(fd, contents, void 0, "utf8");
|
|
8242
8350
|
fsyncSync(fd);
|
|
8243
8351
|
} finally {
|
|
8244
8352
|
closeSync(fd);
|
|
8245
8353
|
}
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8354
|
+
try {
|
|
8355
|
+
renameSync(temporary, this.#path);
|
|
8356
|
+
} catch (error) {
|
|
8357
|
+
if (existsSync2(temporary)) unlinkSync(temporary);
|
|
8358
|
+
throw error;
|
|
8359
|
+
}
|
|
8360
|
+
this.#records = compacted;
|
|
8361
|
+
this.#sequence = compacted.length;
|
|
8362
|
+
this.#lastHash = previousHash;
|
|
8249
8363
|
}
|
|
8250
8364
|
};
|
|
8365
|
+
function compactPayload(record) {
|
|
8366
|
+
const payload = record.payload;
|
|
8367
|
+
if (record.type === "queue.admitted") return payload;
|
|
8368
|
+
if (record.type === "queue.claimed") return { id: payload.id };
|
|
8369
|
+
if (record.type === "tool.started") return pick(payload, ["id", "tool", "inputHash", "retrySafe"]);
|
|
8370
|
+
if (record.type === "model.requested") return pick(payload, ["id", "agent", "model", "iteration", "attempt", "messagesHash"]);
|
|
8371
|
+
if (record.type === "turn.started") return pick(payload, ["id", "configHash", "promptHash"]);
|
|
8372
|
+
if (record.type === "savepoint.created") return pick(payload, ["id", "phase", "configHash"]);
|
|
8373
|
+
return payload;
|
|
8374
|
+
}
|
|
8375
|
+
function hasLegacyPayload(record) {
|
|
8376
|
+
if (record.type === "turn.started") return "config" in record.payload || "prompt" in record.payload;
|
|
8377
|
+
if (record.type === "model.requested") return "messages" in record.payload;
|
|
8378
|
+
if (record.type === "tool.started") return "input" in record.payload;
|
|
8379
|
+
if (record.type === "tool.completed") return "result" in record.payload;
|
|
8380
|
+
return false;
|
|
8381
|
+
}
|
|
8382
|
+
function pick(source, keys) {
|
|
8383
|
+
return Object.fromEntries(keys.flatMap((key) => key in source ? [[key, source[key]]] : []));
|
|
8384
|
+
}
|
|
8251
8385
|
function jsonSafeRecord(value) {
|
|
8252
8386
|
const json = JSON.stringify(value, (_key, item) => typeof item === "bigint" ? `${item}n` : item);
|
|
8253
8387
|
return JSON.parse(json);
|
|
@@ -8280,6 +8414,7 @@ function isWithin2(root, target) {
|
|
|
8280
8414
|
|
|
8281
8415
|
// src/hooks/bus.ts
|
|
8282
8416
|
import { spawn as spawn4 } from "child_process";
|
|
8417
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
8283
8418
|
|
|
8284
8419
|
// src/hooks/types.ts
|
|
8285
8420
|
import { z as z11 } from "zod";
|
|
@@ -8346,6 +8481,11 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1048576;
|
|
|
8346
8481
|
var HookBus = class {
|
|
8347
8482
|
#handlers = /* @__PURE__ */ new Map();
|
|
8348
8483
|
#payloadSchemas = /* @__PURE__ */ new Map();
|
|
8484
|
+
#options;
|
|
8485
|
+
#sequence = 0;
|
|
8486
|
+
constructor(options = {}) {
|
|
8487
|
+
this.#options = options;
|
|
8488
|
+
}
|
|
8349
8489
|
registerPayloadSchema(type, schema) {
|
|
8350
8490
|
const registrations = this.#payloadSchemas.get(type) ?? [];
|
|
8351
8491
|
const registration = { schema };
|
|
@@ -8382,7 +8522,9 @@ var HookBus = class {
|
|
|
8382
8522
|
async emit(rawEvent, externalSignal) {
|
|
8383
8523
|
externalSignal?.throwIfAborted();
|
|
8384
8524
|
let event = HookEventSchema.parse(rawEvent);
|
|
8385
|
-
|
|
8525
|
+
const validatedPayload = this.#validatePayload(event.type, event.payload);
|
|
8526
|
+
const envelope = this.#eventEnvelope();
|
|
8527
|
+
event = { ...event, payload: decoratePayload(validatedPayload, envelope) };
|
|
8386
8528
|
let aggregate2 = { decision: "allow" };
|
|
8387
8529
|
for (const registered of this.#handlers.get(event.type) ?? []) {
|
|
8388
8530
|
externalSignal?.throwIfAborted();
|
|
@@ -8397,8 +8539,8 @@ var HookBus = class {
|
|
|
8397
8539
|
externalSignal?.throwIfAborted();
|
|
8398
8540
|
if (decision.updatedInput !== void 0) {
|
|
8399
8541
|
const payload = this.#validatePayload(event.type, decision.updatedInput);
|
|
8400
|
-
event = HookEventSchema.parse({ ...event, payload });
|
|
8401
|
-
aggregate2 = { ...aggregate2, updatedInput:
|
|
8542
|
+
event = HookEventSchema.parse({ ...event, payload: decoratePayload(payload, envelope) });
|
|
8543
|
+
aggregate2 = { ...aggregate2, updatedInput: payload };
|
|
8402
8544
|
}
|
|
8403
8545
|
if (decision.additionalContext !== void 0) {
|
|
8404
8546
|
aggregate2 = {
|
|
@@ -8415,6 +8557,15 @@ var HookBus = class {
|
|
|
8415
8557
|
const schema = this.#payloadSchemas.get(type)?.at(-1)?.schema;
|
|
8416
8558
|
return schema ? schema.parse(payload) : zodRecord(payload);
|
|
8417
8559
|
}
|
|
8560
|
+
#eventEnvelope() {
|
|
8561
|
+
if (this.#options.eventContext === void 0) return void 0;
|
|
8562
|
+
return {
|
|
8563
|
+
...this.#options.eventContext(),
|
|
8564
|
+
eventId: (this.#options.eventId ?? randomUUID9)(),
|
|
8565
|
+
sequence: ++this.#sequence,
|
|
8566
|
+
timestamp: (this.#options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))()
|
|
8567
|
+
};
|
|
8568
|
+
}
|
|
8418
8569
|
async #invoke(registered, event, externalSignal) {
|
|
8419
8570
|
const timeoutSignal = AbortSignal.timeout(registered.timeoutMs);
|
|
8420
8571
|
const signal = externalSignal === void 0 ? timeoutSignal : AbortSignal.any([timeoutSignal, externalSignal]);
|
|
@@ -8426,6 +8577,9 @@ var HookBus = class {
|
|
|
8426
8577
|
return HookDecisionSchema.parse(await Promise.race([invocation, timeout]));
|
|
8427
8578
|
}
|
|
8428
8579
|
};
|
|
8580
|
+
function decoratePayload(payload, envelope) {
|
|
8581
|
+
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
8582
|
+
}
|
|
8429
8583
|
function runShellHandler(descriptor, event, signal) {
|
|
8430
8584
|
return new Promise((resolve28, reject) => {
|
|
8431
8585
|
const child = spawn4(descriptor.command, [...descriptor.args ?? []], {
|
|
@@ -11303,7 +11457,7 @@ function formatPriorGaps(gaps) {
|
|
|
11303
11457
|
|
|
11304
11458
|
// src/goal/store.ts
|
|
11305
11459
|
import { constants as constants2 } from "fs";
|
|
11306
|
-
import { randomUUID as
|
|
11460
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
11307
11461
|
import { lstat as lstat6, mkdir as mkdir11, open as open6, rename as rename4, rm as rm4 } from "fs/promises";
|
|
11308
11462
|
import { dirname as dirname12, join as join14, relative as relative9, resolve as resolve11, sep as sep9 } from "path";
|
|
11309
11463
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
@@ -11327,7 +11481,7 @@ var GoalStore = class {
|
|
|
11327
11481
|
if (Buffer.byteLength(body) > this.#maxBytes) {
|
|
11328
11482
|
throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
11329
11483
|
}
|
|
11330
|
-
const temporary = join14(this.#root, `.${state.id}.${process.pid}.${
|
|
11484
|
+
const temporary = join14(this.#root, `.${state.id}.${process.pid}.${randomUUID10()}.tmp`);
|
|
11331
11485
|
let handle;
|
|
11332
11486
|
try {
|
|
11333
11487
|
handle = await open6(temporary, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
|
|
@@ -11440,7 +11594,7 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
11440
11594
|
import { constants as constants3 } from "fs";
|
|
11441
11595
|
import { lstat as lstat7, mkdir as mkdir13, open as open7, readFile as readFile11, realpath as realpath5, rename as rename5, rm as rm5, stat as stat5 } from "fs/promises";
|
|
11442
11596
|
import { dirname as dirname13, join as join16, relative as relative10, resolve as resolve13, sep as sep10 } from "path";
|
|
11443
|
-
import { randomUUID as
|
|
11597
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
11444
11598
|
var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
11445
11599
|
var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
|
|
11446
11600
|
var LoopStore = class {
|
|
@@ -11471,7 +11625,7 @@ var LoopStore = class {
|
|
|
11471
11625
|
const body = `${JSON.stringify(state, null, 2)}
|
|
11472
11626
|
`;
|
|
11473
11627
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
11474
|
-
const temporary = join16(directory, `.state.${process.pid}.${
|
|
11628
|
+
const temporary = join16(directory, `.state.${process.pid}.${randomUUID11()}.tmp`);
|
|
11475
11629
|
let handle;
|
|
11476
11630
|
try {
|
|
11477
11631
|
handle = await open7(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
|
|
@@ -11497,7 +11651,7 @@ var LoopStore = class {
|
|
|
11497
11651
|
return state;
|
|
11498
11652
|
} catch (error) {
|
|
11499
11653
|
if (isCode4(error, "ENOENT")) throw new Error(`Loop "${loopId}" was not found`);
|
|
11500
|
-
const quarantine = `${target}.corrupt-${Date.now()}-${
|
|
11654
|
+
const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID11().slice(0, 8)}`;
|
|
11501
11655
|
await rename5(target, quarantine).catch(() => void 0);
|
|
11502
11656
|
throw new Error(`Loop "${loopId}" is corrupt and was quarantined: ${message(error)}`);
|
|
11503
11657
|
}
|
|
@@ -14265,7 +14419,7 @@ function createSkillResourceTool(registry) {
|
|
|
14265
14419
|
import { constants as constants5 } from "fs";
|
|
14266
14420
|
import { lstat as lstat9, mkdir as mkdir14, open as open8, readFile as readFile16, readdir as readdir6, realpath as realpath7, rename as rename6, rm as rm6, stat as stat6 } from "fs/promises";
|
|
14267
14421
|
import { basename as basename3, dirname as dirname15, join as join19, relative as relative13, resolve as resolve18, sep as sep13 } from "path";
|
|
14268
|
-
import { randomUUID as
|
|
14422
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
14269
14423
|
import { z as z22 } from "zod";
|
|
14270
14424
|
var SESSION_VERSION = 4;
|
|
14271
14425
|
var DEFAULT_MAX_SESSION_BYTES = 5 * 1024 * 1024;
|
|
@@ -14423,7 +14577,7 @@ var SessionStore = class {
|
|
|
14423
14577
|
const body = `${lines.join("\n")}
|
|
14424
14578
|
`;
|
|
14425
14579
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
14426
|
-
const temporary = join19(this.#sessions, `.${document.sessionId}.${process.pid}.${
|
|
14580
|
+
const temporary = join19(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID12()}.tmp`);
|
|
14427
14581
|
let handle;
|
|
14428
14582
|
try {
|
|
14429
14583
|
handle = await open8(temporary, constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY, 384);
|
|
@@ -14579,7 +14733,7 @@ var SessionStore = class {
|
|
|
14579
14733
|
if (!isWithin5(canonicalRoot, canonicalSessions)) throw new Error("Session directory escapes the workspace");
|
|
14580
14734
|
}
|
|
14581
14735
|
async #quarantine(path) {
|
|
14582
|
-
const target = `${path}.corrupt-${Date.now()}-${
|
|
14736
|
+
const target = `${path}.corrupt-${Date.now()}-${randomUUID12().slice(0, 8)}`;
|
|
14583
14737
|
await rename6(path, target);
|
|
14584
14738
|
return target;
|
|
14585
14739
|
}
|
|
@@ -14799,7 +14953,7 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
14799
14953
|
|
|
14800
14954
|
// src/sleep/organizer.ts
|
|
14801
14955
|
import { constants as constants6 } from "fs";
|
|
14802
|
-
import { randomUUID as
|
|
14956
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
14803
14957
|
import { lstat as lstat10, mkdir as mkdir15, open as open9, readdir as readdir7, realpath as realpath8, rename as rename7, rm as rm7 } from "fs/promises";
|
|
14804
14958
|
import { isAbsolute as isAbsolute8, join as join20, relative as relative14, resolve as resolve19, sep as sep14 } from "path";
|
|
14805
14959
|
import { z as z23 } from "zod";
|
|
@@ -14956,7 +15110,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
14956
15110
|
return (await readdir7(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
|
|
14957
15111
|
}
|
|
14958
15112
|
async #atomicWrite(path, content) {
|
|
14959
|
-
const temporary = join20(this.#sleepDirectory, `.${
|
|
15113
|
+
const temporary = join20(this.#sleepDirectory, `.${randomUUID13()}.tmp`);
|
|
14960
15114
|
let handle;
|
|
14961
15115
|
try {
|
|
14962
15116
|
handle = await open9(temporary, constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY, 384);
|
|
@@ -15332,7 +15486,7 @@ function computeSessionStats(documents) {
|
|
|
15332
15486
|
import { constants as constants7 } from "fs";
|
|
15333
15487
|
import { mkdir as mkdir16, open as open10, readFile as readFile17, realpath as realpath9, rename as rename8, rm as rm8, stat as stat7 } from "fs/promises";
|
|
15334
15488
|
import { dirname as dirname16, isAbsolute as isAbsolute9, relative as relative15, resolve as resolve20, sep as sep15 } from "path";
|
|
15335
|
-
import { randomUUID as
|
|
15489
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
15336
15490
|
import { z as z24 } from "zod";
|
|
15337
15491
|
|
|
15338
15492
|
// src/tools/file-diff.ts
|
|
@@ -15685,7 +15839,7 @@ function createPathGuard(workspace) {
|
|
|
15685
15839
|
async function atomicWrite(path, content, signal, expectedVersion) {
|
|
15686
15840
|
abortIfNeeded(signal);
|
|
15687
15841
|
await mkdir16(dirname16(path), { recursive: true });
|
|
15688
|
-
const temporary = `${path}.${
|
|
15842
|
+
const temporary = `${path}.${randomUUID14()}.tmp`;
|
|
15689
15843
|
try {
|
|
15690
15844
|
const handle = await open10(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
15691
15845
|
try {
|
|
@@ -17082,18 +17236,40 @@ function hookAnswersFromUpdatedInput(updatedInput, questions) {
|
|
|
17082
17236
|
var QuestionBridge = class {
|
|
17083
17237
|
#pending;
|
|
17084
17238
|
#onChange;
|
|
17085
|
-
|
|
17239
|
+
#relay;
|
|
17240
|
+
#active = false;
|
|
17241
|
+
constructor(onChange, relay) {
|
|
17086
17242
|
this.#onChange = onChange;
|
|
17243
|
+
this.#relay = relay;
|
|
17087
17244
|
}
|
|
17088
17245
|
get pending() {
|
|
17089
17246
|
return this.#pending?.questions;
|
|
17090
17247
|
}
|
|
17091
17248
|
ask(questions, signal) {
|
|
17092
|
-
if (this.#
|
|
17249
|
+
if (this.#active) return Promise.reject(new Error("A question is already pending"));
|
|
17093
17250
|
if (signal.aborted) return Promise.reject(signal.reason);
|
|
17251
|
+
this.#active = true;
|
|
17252
|
+
if (this.#relay !== void 0) return this.#askRelayed(questions, signal);
|
|
17253
|
+
return this.#askLocally(questions, signal);
|
|
17254
|
+
}
|
|
17255
|
+
async #askRelayed(questions, signal) {
|
|
17256
|
+
try {
|
|
17257
|
+
const answers = await this.#relay(questions, signal);
|
|
17258
|
+
if (answers !== void 0) {
|
|
17259
|
+
this.#active = false;
|
|
17260
|
+
return answers;
|
|
17261
|
+
}
|
|
17262
|
+
return await this.#askLocally(questions, signal);
|
|
17263
|
+
} catch (error) {
|
|
17264
|
+
this.#active = false;
|
|
17265
|
+
throw error;
|
|
17266
|
+
}
|
|
17267
|
+
}
|
|
17268
|
+
#askLocally(questions, signal) {
|
|
17094
17269
|
return new Promise((resolve28, reject) => {
|
|
17095
17270
|
const onAbort = () => {
|
|
17096
17271
|
this.#pending = void 0;
|
|
17272
|
+
this.#active = false;
|
|
17097
17273
|
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
|
|
17098
17274
|
};
|
|
17099
17275
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -17102,12 +17278,14 @@ var QuestionBridge = class {
|
|
|
17102
17278
|
resolve: (answers) => {
|
|
17103
17279
|
signal.removeEventListener("abort", onAbort);
|
|
17104
17280
|
this.#pending = void 0;
|
|
17281
|
+
this.#active = false;
|
|
17105
17282
|
this.#onChange?.();
|
|
17106
17283
|
resolve28(answers);
|
|
17107
17284
|
},
|
|
17108
17285
|
reject: (error) => {
|
|
17109
17286
|
signal.removeEventListener("abort", onAbort);
|
|
17110
17287
|
this.#pending = void 0;
|
|
17288
|
+
this.#active = false;
|
|
17111
17289
|
this.#onChange?.();
|
|
17112
17290
|
reject(error);
|
|
17113
17291
|
}
|
|
@@ -17193,7 +17371,7 @@ function createTodoWriteTool() {
|
|
|
17193
17371
|
}
|
|
17194
17372
|
|
|
17195
17373
|
// src/tools/managed.ts
|
|
17196
|
-
import { randomUUID as
|
|
17374
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
17197
17375
|
import { link as link2, lstat as lstat11, mkdir as mkdir17, readFile as readFile19, readdir as readdir8, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
|
|
17198
17376
|
import { dirname as dirname18, join as join21, resolve as resolve22 } from "path";
|
|
17199
17377
|
import { z as z31 } from "zod";
|
|
@@ -17480,7 +17658,7 @@ ${sourceUrl}`
|
|
|
17480
17658
|
}
|
|
17481
17659
|
async function writeExclusive(path, content) {
|
|
17482
17660
|
await mkdir17(dirname18(path), { recursive: true });
|
|
17483
|
-
const temporary = `${path}.${
|
|
17661
|
+
const temporary = `${path}.${randomUUID15()}.tmp`;
|
|
17484
17662
|
try {
|
|
17485
17663
|
await writeFile10(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
17486
17664
|
await link2(temporary, path);
|
|
@@ -19267,7 +19445,7 @@ function relativeDisplay(workspace, filePath) {
|
|
|
19267
19445
|
}
|
|
19268
19446
|
|
|
19269
19447
|
// src/jobs/registry.ts
|
|
19270
|
-
import { randomUUID as
|
|
19448
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
19271
19449
|
var JobRegistry = class {
|
|
19272
19450
|
#jobs = /* @__PURE__ */ new Map();
|
|
19273
19451
|
#maxOutputChars;
|
|
@@ -19281,7 +19459,7 @@ var JobRegistry = class {
|
|
|
19281
19459
|
}
|
|
19282
19460
|
create(input) {
|
|
19283
19461
|
this.#pruneCompleted();
|
|
19284
|
-
const id = `job-${
|
|
19462
|
+
const id = `job-${randomUUID16().slice(0, 12)}`;
|
|
19285
19463
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19286
19464
|
let settle;
|
|
19287
19465
|
const done = new Promise((resolve28) => {
|
|
@@ -19407,7 +19585,7 @@ function snapshot2(job) {
|
|
|
19407
19585
|
}
|
|
19408
19586
|
|
|
19409
19587
|
// src/terminal/service.ts
|
|
19410
|
-
import { randomUUID as
|
|
19588
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
19411
19589
|
import { createRequire as nodeCreateRequire } from "module";
|
|
19412
19590
|
import { isAbsolute as isAbsolute11, relative as relative19, resolve as resolve25, sep as sep19 } from "path";
|
|
19413
19591
|
var TerminalService = class {
|
|
@@ -19425,7 +19603,7 @@ var TerminalService = class {
|
|
|
19425
19603
|
open(input) {
|
|
19426
19604
|
const cwd = workspacePath(this.#root, input.cwd ?? ".");
|
|
19427
19605
|
const shell = input.shell ?? defaultShell();
|
|
19428
|
-
const id = `term-${
|
|
19606
|
+
const id = `term-${randomUUID17().slice(0, 12)}`;
|
|
19429
19607
|
const pty = this.#factory(shell, input.args ?? [], {
|
|
19430
19608
|
cwd,
|
|
19431
19609
|
cols: input.columns ?? 100,
|
|
@@ -19651,7 +19829,7 @@ function palSocketAddress(options) {
|
|
|
19651
19829
|
}
|
|
19652
19830
|
|
|
19653
19831
|
// src/pals/client.ts
|
|
19654
|
-
import { randomUUID as
|
|
19832
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
19655
19833
|
import { Socket as Socket3 } from "net";
|
|
19656
19834
|
var DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
|
|
19657
19835
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -19727,7 +19905,7 @@ var PalClient = class {
|
|
|
19727
19905
|
return this.starting;
|
|
19728
19906
|
}
|
|
19729
19907
|
async list() {
|
|
19730
|
-
const data2 = await this.request({ version: 1, type: "list", requestId:
|
|
19908
|
+
const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID18() });
|
|
19731
19909
|
return PalPresenceListSchema.parse(data2);
|
|
19732
19910
|
}
|
|
19733
19911
|
async rename(alias) {
|
|
@@ -19740,38 +19918,38 @@ var PalClient = class {
|
|
|
19740
19918
|
throw error;
|
|
19741
19919
|
}
|
|
19742
19920
|
}
|
|
19743
|
-
async sendTask(target, message2, messageId =
|
|
19744
|
-
const data2 = await this.request({ version: 1, type: "task", requestId:
|
|
19921
|
+
async sendTask(target, message2, messageId = randomUUID18()) {
|
|
19922
|
+
const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID18(), messageId, target, goal: message2 });
|
|
19745
19923
|
return DeliveryReceiptSchema.parse(data2);
|
|
19746
19924
|
}
|
|
19747
|
-
async sendChat(target, message2, messageId =
|
|
19748
|
-
const data2 = await this.request({ version: 1, type: "chat", requestId:
|
|
19925
|
+
async sendChat(target, message2, messageId = randomUUID18()) {
|
|
19926
|
+
const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID18(), messageId, target, message: message2 });
|
|
19749
19927
|
return DeliveryReceiptSchema.parse(data2);
|
|
19750
19928
|
}
|
|
19751
19929
|
async startCoWork(input) {
|
|
19752
19930
|
const data2 = await this.request({
|
|
19753
19931
|
version: 1,
|
|
19754
19932
|
type: "cowork-propose",
|
|
19755
|
-
requestId:
|
|
19756
|
-
coWorkId: input.coWorkId ??
|
|
19933
|
+
requestId: randomUUID18(),
|
|
19934
|
+
coWorkId: input.coWorkId ?? randomUUID18(),
|
|
19757
19935
|
goal: input.goal,
|
|
19758
19936
|
participants: input.participants
|
|
19759
19937
|
});
|
|
19760
19938
|
return CoWorkSnapshotSchema.parse(data2);
|
|
19761
19939
|
}
|
|
19762
19940
|
async coWorkAction(action) {
|
|
19763
|
-
const request = { version: 1, requestId:
|
|
19941
|
+
const request = { version: 1, requestId: randomUUID18(), ...action };
|
|
19764
19942
|
return CoWorkSnapshotSchema.parse(await this.request(request));
|
|
19765
19943
|
}
|
|
19766
19944
|
async coWorkStatus(coWorkId) {
|
|
19767
|
-
const data2 = await this.request({ version: 1, type: "cowork-get", requestId:
|
|
19945
|
+
const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID18(), coWorkId });
|
|
19768
19946
|
return CoWorkSnapshotSchema.parse(data2);
|
|
19769
19947
|
}
|
|
19770
19948
|
async integrateCoWork(input) {
|
|
19771
19949
|
return this.coWorkAction({ type: "cowork-integration", ...input });
|
|
19772
19950
|
}
|
|
19773
19951
|
async cancelCoWork(coWorkId, reason) {
|
|
19774
|
-
const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId:
|
|
19952
|
+
const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID18(), coWorkId, reason });
|
|
19775
19953
|
return CoWorkSnapshotSchema.parse(data2);
|
|
19776
19954
|
}
|
|
19777
19955
|
subscribe(listener) {
|
|
@@ -19783,7 +19961,7 @@ var PalClient = class {
|
|
|
19783
19961
|
if (this.registered && this.socket !== void 0) {
|
|
19784
19962
|
let disconnectTimer;
|
|
19785
19963
|
await Promise.race([
|
|
19786
|
-
this.request({ version: 1, type: "disconnect", requestId:
|
|
19964
|
+
this.request({ version: 1, type: "disconnect", requestId: randomUUID18() }).catch(() => void 0),
|
|
19787
19965
|
new Promise((resolve28) => {
|
|
19788
19966
|
disconnectTimer = setTimeout(resolve28, Math.min(this.requestTimeoutMs, 250));
|
|
19789
19967
|
})
|
|
@@ -19874,7 +20052,7 @@ var PalClient = class {
|
|
|
19874
20052
|
const data2 = await this.request({
|
|
19875
20053
|
version: 1,
|
|
19876
20054
|
type: "register",
|
|
19877
|
-
requestId:
|
|
20055
|
+
requestId: randomUUID18(),
|
|
19878
20056
|
authToken: this.authToken,
|
|
19879
20057
|
presence: { version: 1, ...this.registration, connectedAt: now, lastSeenAt: now }
|
|
19880
20058
|
});
|
|
@@ -19986,7 +20164,7 @@ var PalClient = class {
|
|
|
19986
20164
|
if (!this.registered) return;
|
|
19987
20165
|
if (this.heartbeatPending) return;
|
|
19988
20166
|
this.heartbeatPending = true;
|
|
19989
|
-
void this.request({ version: 1, type: "heartbeat", requestId:
|
|
20167
|
+
void this.request({ version: 1, type: "heartbeat", requestId: randomUUID18() }).catch(() => void 0).finally(() => {
|
|
19990
20168
|
this.heartbeatPending = false;
|
|
19991
20169
|
});
|
|
19992
20170
|
}, this.heartbeatIntervalMs);
|
|
@@ -20273,7 +20451,7 @@ var ApprovalBridge = class {
|
|
|
20273
20451
|
request(request, signal = new AbortController().signal) {
|
|
20274
20452
|
if (this.#settle !== void 0) return Promise.resolve("deny");
|
|
20275
20453
|
if (signal.aborted) return Promise.resolve("deny");
|
|
20276
|
-
this.#pending = { id:
|
|
20454
|
+
this.#pending = { id: randomUUID19(), ...request };
|
|
20277
20455
|
this.#onChange?.();
|
|
20278
20456
|
return new Promise((resolvePromise) => {
|
|
20279
20457
|
this.#settle = resolvePromise;
|
|
@@ -20392,7 +20570,14 @@ async function createProductionRuntime(options) {
|
|
|
20392
20570
|
environment.OPENAI_API_KEY,
|
|
20393
20571
|
environment.ANTHROPIC_API_KEY
|
|
20394
20572
|
].filter((value) => typeof value === "string" && value.length > 0);
|
|
20395
|
-
|
|
20573
|
+
let hookSessionId;
|
|
20574
|
+
const hooks = new HookBus({
|
|
20575
|
+
eventContext: () => ({
|
|
20576
|
+
protocolVersion: 2,
|
|
20577
|
+
workspace,
|
|
20578
|
+
...hookSessionId === void 0 ? {} : { sessionId: hookSessionId }
|
|
20579
|
+
})
|
|
20580
|
+
});
|
|
20396
20581
|
hooks.on(
|
|
20397
20582
|
"PostToolUseFailure",
|
|
20398
20583
|
createIncidentReporter({
|
|
@@ -20412,8 +20597,7 @@ async function createProductionRuntime(options) {
|
|
|
20412
20597
|
diagnostics.push(...permissionPolicy.diagnostics);
|
|
20413
20598
|
const approvals = new ApprovalBridge(options.onApprovalChange);
|
|
20414
20599
|
const resolveToolApproval = options.approvalPolicy === "deny" && options.rpcToolApprovals !== true ? () => "deny" : (request, signal) => approvals.request(request, signal);
|
|
20415
|
-
const
|
|
20416
|
-
const askUserQuestionHandler = async (qs, signal) => {
|
|
20600
|
+
const relayQuestions = async (qs, signal) => {
|
|
20417
20601
|
if (options.approvalPolicy === "deny") throw new Error("AskUserQuestion is not available in non-interactive mode");
|
|
20418
20602
|
if (hooks.hasListeners("PermissionRequest")) {
|
|
20419
20603
|
let decision;
|
|
@@ -20431,8 +20615,10 @@ async function createProductionRuntime(options) {
|
|
|
20431
20615
|
const hookAnswers = hookAnswersFromUpdatedInput(decision.updatedInput, qs);
|
|
20432
20616
|
if (hookAnswers !== void 0) return hookAnswers;
|
|
20433
20617
|
}
|
|
20434
|
-
return
|
|
20618
|
+
return void 0;
|
|
20435
20619
|
};
|
|
20620
|
+
const questions = new QuestionBridge(options.onApprovalChange, relayQuestions);
|
|
20621
|
+
const askUserQuestionHandler = (qs, signal) => questions.ask(qs, signal);
|
|
20436
20622
|
const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
|
|
20437
20623
|
const jobs = new JobRegistry();
|
|
20438
20624
|
const terminals = new TerminalService(workspace, { jobs });
|
|
@@ -20756,7 +20942,8 @@ async function createProductionRuntime(options) {
|
|
|
20756
20942
|
let taskResults = { ...recovered?.tasks.results ?? {} };
|
|
20757
20943
|
const subagentStartedAt = {};
|
|
20758
20944
|
const subagentElapsedMs = {};
|
|
20759
|
-
let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
20945
|
+
let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`;
|
|
20946
|
+
hookSessionId = sessionId;
|
|
20760
20947
|
let harnessJournal = new HarnessJournal({ workspace, sessionId });
|
|
20761
20948
|
const harnessRecovery = harnessJournal.recover();
|
|
20762
20949
|
if (harnessRecovery.queue.length > 0 || harnessRecovery.incompleteTools.length > 0 || harnessRecovery.incompleteModelIds.length > 0 || harnessRecovery.interruptedTurnIds.length > 0) {
|
|
@@ -20840,7 +21027,13 @@ async function createProductionRuntime(options) {
|
|
|
20840
21027
|
const publishTaskState = async () => {
|
|
20841
21028
|
harness.main.context.updateTaskState(serializedTaskState());
|
|
20842
21029
|
await persist();
|
|
20843
|
-
|
|
21030
|
+
const snapshot4 = taskSnapshot();
|
|
21031
|
+
emitOutput({ type: "tasks", snapshot: snapshot4 });
|
|
21032
|
+
await hooks.emit({
|
|
21033
|
+
version: 1,
|
|
21034
|
+
type: "Notification",
|
|
21035
|
+
payload: { kind: "task_snapshot", taskSnapshot: snapshot4 }
|
|
21036
|
+
}).catch(() => void 0);
|
|
20844
21037
|
};
|
|
20845
21038
|
for (const tool of createTaskPlanTools({
|
|
20846
21039
|
getPlan: () => taskPlan,
|
|
@@ -21398,7 +21591,7 @@ async function createProductionRuntime(options) {
|
|
|
21398
21591
|
return answers[0] === "Continue" ? "approved" : "rejected";
|
|
21399
21592
|
},
|
|
21400
21593
|
fingerprint: workspaceFingerprint,
|
|
21401
|
-
idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
21594
|
+
idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`
|
|
21402
21595
|
});
|
|
21403
21596
|
const goalStore = new GoalStore({ workspace });
|
|
21404
21597
|
const goalOrchestrator = new GoalOrchestrator({
|
|
@@ -21417,7 +21610,7 @@ async function createProductionRuntime(options) {
|
|
|
21417
21610
|
executionEnvironment
|
|
21418
21611
|
),
|
|
21419
21612
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
21420
|
-
idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
21613
|
+
idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`,
|
|
21421
21614
|
runWorker: ({ workspace: goalWorkspace, prompt, signal }) => runLoopWorker({ workspace: goalWorkspace, prompt, signal })
|
|
21422
21615
|
});
|
|
21423
21616
|
let sessionHistory = await SessionHistory.open({
|
|
@@ -21530,7 +21723,7 @@ async function createProductionRuntime(options) {
|
|
|
21530
21723
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
21531
21724
|
persist
|
|
21532
21725
|
),
|
|
21533
|
-
runLoop: (goal, signal) => runLoopSession(loopOrchestrator, goal, signal),
|
|
21726
|
+
runLoop: (goal, signal) => runLoopSession(loopOrchestrator, hooks, goal, signal),
|
|
21534
21727
|
runGoal: (goal, signal) => persistEach(runGoalSession(goalOrchestrator, goal, signal), persist),
|
|
21535
21728
|
mcp: async (command, signal) => {
|
|
21536
21729
|
signal.throwIfAborted();
|
|
@@ -21688,7 +21881,8 @@ ${summary2}`;
|
|
|
21688
21881
|
for (const key of Object.keys(subagentStartedAt)) delete subagentStartedAt[key];
|
|
21689
21882
|
for (const key of Object.keys(subagentElapsedMs)) delete subagentElapsedMs[key];
|
|
21690
21883
|
const previousIdeSessionId = ideSessionId;
|
|
21691
|
-
sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
21884
|
+
sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID19().slice(0, 8)}`;
|
|
21885
|
+
hookSessionId = sessionId;
|
|
21692
21886
|
harnessJournal = new HarnessJournal({ workspace, sessionId });
|
|
21693
21887
|
setUsageSession(sessionId);
|
|
21694
21888
|
ideSessionId = sessionId;
|
|
@@ -21987,9 +22181,10 @@ function formatMcpReconnect(server) {
|
|
|
21987
22181
|
if (server.status === "disabled") return `MCP server "${server.name}" is disabled. Enable it before reconnecting.`;
|
|
21988
22182
|
return `Failed to reconnect MCP server "${server.name}": ${server.error ?? "unknown error"}`;
|
|
21989
22183
|
}
|
|
21990
|
-
async function* runLoopSession(orchestrator, goal, signal) {
|
|
22184
|
+
async function* runLoopSession(orchestrator, hooks, goal, signal) {
|
|
21991
22185
|
let totalInputTokens = 0;
|
|
21992
22186
|
let totalOutputTokens = 0;
|
|
22187
|
+
let latestVerification;
|
|
21993
22188
|
for await (const event of orchestrator.run({ goal, signal })) {
|
|
21994
22189
|
if (event.type === "worker-event") {
|
|
21995
22190
|
if (event.event.type === "usage") {
|
|
@@ -22003,8 +22198,19 @@ async function* runLoopSession(orchestrator, goal, signal) {
|
|
|
22003
22198
|
} else if (event.event.type !== "done") yield event.event;
|
|
22004
22199
|
continue;
|
|
22005
22200
|
}
|
|
22201
|
+
if (event.type === "loop-verification") latestVerification = event.evidence;
|
|
22006
22202
|
yield loopProgressEvent(event);
|
|
22007
22203
|
if (event.type === "loop-terminal") {
|
|
22204
|
+
await hooks.emit({
|
|
22205
|
+
version: 1,
|
|
22206
|
+
type: "LoopEnd",
|
|
22207
|
+
payload: {
|
|
22208
|
+
loopId: event.loopId,
|
|
22209
|
+
outcome: event.status,
|
|
22210
|
+
reason: event.reason,
|
|
22211
|
+
verification: latestVerification ?? null
|
|
22212
|
+
}
|
|
22213
|
+
}).catch(() => void 0);
|
|
22008
22214
|
yield { type: "done", usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } };
|
|
22009
22215
|
}
|
|
22010
22216
|
}
|
|
@@ -22718,7 +22924,7 @@ function memoryTranscriptHash(messages) {
|
|
|
22718
22924
|
return createHash17("sha256").update(visible, "utf8").digest("hex");
|
|
22719
22925
|
}
|
|
22720
22926
|
function createMemoryTaskId() {
|
|
22721
|
-
return `memory-${
|
|
22927
|
+
return `memory-${randomUUID19()}`;
|
|
22722
22928
|
}
|
|
22723
22929
|
|
|
22724
22930
|
export {
|