flavor-code 1.3.4 → 1.3.7
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/README.md +22 -19
- package/README.zh-CN.md +22 -19
- package/dist/{app-EHB3XAQY.js → app-NHRRG3YA.js} +1 -1
- package/dist/{chunk-YKSMZDUZ.js → chunk-K2AOT2BI.js} +452 -183
- package/dist/{chunk-RDBTC35M.js → chunk-VPJ7KY3T.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/desktop/main.js +678 -400
- package/dist/hooks/bus.d.ts +7 -0
- package/dist/island/control-server.d.ts +21 -0
- package/dist/production.d.ts +4 -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
|
@@ -3517,6 +3517,8 @@ var FlavorSession = class {
|
|
|
3517
3517
|
this.#activeSubmission = submission;
|
|
3518
3518
|
this.#interrupted = false;
|
|
3519
3519
|
let outcome = "completed";
|
|
3520
|
+
let assistantSummary = "";
|
|
3521
|
+
let deliverables = [];
|
|
3520
3522
|
try {
|
|
3521
3523
|
const decision = await this.#services.hooks.emit({
|
|
3522
3524
|
version: 1,
|
|
@@ -3543,6 +3545,8 @@ var FlavorSession = class {
|
|
|
3543
3545
|
...submission.initialUserMessage === void 0 ? {} : { initialUserMessage: submission.initialUserMessage }
|
|
3544
3546
|
})) {
|
|
3545
3547
|
this.#services.output(event);
|
|
3548
|
+
if (event.type === "text") assistantSummary = `${assistantSummary}${event.text}`.slice(-2e3);
|
|
3549
|
+
if (event.type === "deliverables") deliverables = event.files.slice(0, 100);
|
|
3546
3550
|
if (event.type === "error") outcome = "failed";
|
|
3547
3551
|
}
|
|
3548
3552
|
if (controller.signal.aborted) outcome = "cancelled";
|
|
@@ -3567,7 +3571,15 @@ var FlavorSession = class {
|
|
|
3567
3571
|
this.#activeSubmission = void 0;
|
|
3568
3572
|
if (submission.coWorkPlanningKey !== void 0) this.#finishActivePlanning(submission.coWorkPlanningKey);
|
|
3569
3573
|
try {
|
|
3570
|
-
await this.#services.hooks.emit({
|
|
3574
|
+
await this.#services.hooks.emit({
|
|
3575
|
+
version: 1,
|
|
3576
|
+
type: "Stop",
|
|
3577
|
+
payload: {
|
|
3578
|
+
outcome,
|
|
3579
|
+
...assistantSummary.trim() ? { summary: assistantSummary.trim() } : {},
|
|
3580
|
+
...deliverables.length === 0 ? {} : { deliverables }
|
|
3581
|
+
}
|
|
3582
|
+
});
|
|
3571
3583
|
} catch (error) {
|
|
3572
3584
|
try {
|
|
3573
3585
|
this.#services.output({ type: "error", error: { code: "unknown", message: message(error) } });
|
|
@@ -5006,9 +5018,9 @@ async function ensureOnce(options) {
|
|
|
5006
5018
|
|
|
5007
5019
|
// src/production.ts
|
|
5008
5020
|
import { readFile as readFile22 } from "fs/promises";
|
|
5009
|
-
import { createHash as
|
|
5010
|
-
import { homedir as homedir2, release as osRelease, tmpdir as
|
|
5011
|
-
import { basename as basename6, join as
|
|
5021
|
+
import { createHash as createHash18, randomUUID as randomUUID20 } from "crypto";
|
|
5022
|
+
import { homedir as homedir2, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
5023
|
+
import { basename as basename6, join as join25, relative as relative20, resolve as resolve27, sep as sep20 } from "path";
|
|
5012
5024
|
|
|
5013
5025
|
// src/agent/subagents.ts
|
|
5014
5026
|
import { z as z5 } from "zod";
|
|
@@ -6973,6 +6985,7 @@ var AgentLoop = class {
|
|
|
6973
6985
|
attempt,
|
|
6974
6986
|
messages: modelRequest.messages
|
|
6975
6987
|
});
|
|
6988
|
+
const modelStartedAt = Date.now();
|
|
6976
6989
|
yield { type: "model-start", id: modelCallId };
|
|
6977
6990
|
try {
|
|
6978
6991
|
for await (const event of adapter.stream(modelRequest)) {
|
|
@@ -7031,6 +7044,13 @@ var AgentLoop = class {
|
|
|
7031
7044
|
completed,
|
|
7032
7045
|
agent: this.#options.agent,
|
|
7033
7046
|
providerError,
|
|
7047
|
+
durationMs: Math.max(0, Date.now() - modelStartedAt),
|
|
7048
|
+
...usage === void 0 ? {} : {
|
|
7049
|
+
inputTokens: usage.inputTokens,
|
|
7050
|
+
outputTokens: usage.outputTokens,
|
|
7051
|
+
...usage.cacheReadTokens === void 0 ? {} : { cacheReadTokens: usage.cacheReadTokens },
|
|
7052
|
+
...usage.cacheCreationTokens === void 0 ? {} : { cacheCreationTokens: usage.cacheCreationTokens }
|
|
7053
|
+
},
|
|
7034
7054
|
...terminalError === void 0 ? {} : {
|
|
7035
7055
|
errorCode: terminalError.code,
|
|
7036
7056
|
errorMessage: terminalError.message
|
|
@@ -7484,11 +7504,18 @@ var DEFAULT_TOOL_OUTPUT_LIMITS = Object.freeze({
|
|
|
7484
7504
|
perTurnChars: 2e5
|
|
7485
7505
|
});
|
|
7486
7506
|
var PreToolUsePayload = z9.object({
|
|
7507
|
+
// Optional for backwards-compatible hook updatedInput payloads; runtime
|
|
7508
|
+
// emissions always populate it.
|
|
7509
|
+
toolCallId: z9.string().optional(),
|
|
7487
7510
|
tool: z9.string(),
|
|
7488
7511
|
input: z9.unknown(),
|
|
7489
7512
|
agent: z9.enum(["main", "subagent"])
|
|
7490
7513
|
});
|
|
7491
|
-
var PermissionRequestPayload = PreToolUsePayload.extend({
|
|
7514
|
+
var PermissionRequestPayload = PreToolUsePayload.extend({
|
|
7515
|
+
reason: z9.string().optional(),
|
|
7516
|
+
toolCategory: z9.string(),
|
|
7517
|
+
allowAlways: z9.boolean()
|
|
7518
|
+
});
|
|
7492
7519
|
var PostToolUsePayload = PreToolUsePayload.extend({ output: z9.unknown() });
|
|
7493
7520
|
var PostToolUseFailurePayload = PreToolUsePayload.extend({
|
|
7494
7521
|
error: z9.object({ code: z9.string(), message: z9.string() })
|
|
@@ -7605,30 +7632,31 @@ var ToolRuntime = class {
|
|
|
7605
7632
|
const tool = this.#tools.get(call.name);
|
|
7606
7633
|
if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
|
|
7607
7634
|
const signal = context.signal ?? new AbortController().signal;
|
|
7635
|
+
const toolCallId = call.id ?? randomUUID7();
|
|
7608
7636
|
const validation = this.validate(call);
|
|
7609
7637
|
if (!validation.ok) {
|
|
7610
|
-
return this.#fail(call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
7638
|
+
return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
7611
7639
|
}
|
|
7612
7640
|
let input = validation.input;
|
|
7613
7641
|
try {
|
|
7614
7642
|
const pre = await this.#hooks.emit({
|
|
7615
7643
|
version: 1,
|
|
7616
7644
|
type: "PreToolUse",
|
|
7617
|
-
payload: { tool: tool.name, input, agent: context.agent }
|
|
7645
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent }
|
|
7618
7646
|
});
|
|
7619
7647
|
if (pre.updatedInput !== void 0) {
|
|
7620
7648
|
const payload = PreToolUsePayload.parse(pre.updatedInput);
|
|
7621
7649
|
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");
|
|
7650
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PreToolUse cannot change the tool or agent");
|
|
7623
7651
|
}
|
|
7624
7652
|
try {
|
|
7625
7653
|
input = tool.inputSchema.parse(payload.input);
|
|
7626
7654
|
} catch (error) {
|
|
7627
|
-
return this.#fail(tool.name, payload.input, context.agent, "invalid_input", message(error));
|
|
7655
|
+
return this.#fail(toolCallId, tool.name, payload.input, context.agent, "invalid_input", message(error));
|
|
7628
7656
|
}
|
|
7629
7657
|
}
|
|
7630
7658
|
if (pre.decision === "deny") {
|
|
7631
|
-
return this.#fail(tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
|
|
7659
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "hook_denied", pre.reason ?? "Tool use denied by hook");
|
|
7632
7660
|
}
|
|
7633
7661
|
const request = {
|
|
7634
7662
|
agent: context.agent,
|
|
@@ -7639,13 +7667,13 @@ var ToolRuntime = class {
|
|
|
7639
7667
|
let permission = this.#permissions.decide(request);
|
|
7640
7668
|
const nonCacheableApproval = request.allowAlways === false || permission.allowAlways === false;
|
|
7641
7669
|
if (permission.decision === "deny") {
|
|
7642
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
|
|
7670
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", permission.reason ?? "Tool use denied");
|
|
7643
7671
|
}
|
|
7644
7672
|
if (permission.decision === "ask" && context.agent === "main" && !nonCacheableApproval && this.#permissions.mode === "auto" && this.#permissions.profile === "standard" && this.#classify !== void 0) {
|
|
7645
7673
|
try {
|
|
7646
7674
|
const classified = await this.#classify(request, signal);
|
|
7647
7675
|
if (classified.decision === "deny") {
|
|
7648
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
|
|
7676
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", classified.reason ?? "Auto classifier denied tool use");
|
|
7649
7677
|
}
|
|
7650
7678
|
if (classified.decision === "allow") permission = classified;
|
|
7651
7679
|
else {
|
|
@@ -7662,13 +7690,21 @@ var ToolRuntime = class {
|
|
|
7662
7690
|
const requestDecision = await this.#hooks.emit({
|
|
7663
7691
|
version: 1,
|
|
7664
7692
|
type: "PermissionRequest",
|
|
7665
|
-
payload: {
|
|
7693
|
+
payload: {
|
|
7694
|
+
toolCallId,
|
|
7695
|
+
tool: tool.name,
|
|
7696
|
+
input: tool.permissionInput?.(input) ?? input,
|
|
7697
|
+
agent: context.agent,
|
|
7698
|
+
reason,
|
|
7699
|
+
toolCategory: getToolCategory(tool.name),
|
|
7700
|
+
allowAlways
|
|
7701
|
+
}
|
|
7666
7702
|
});
|
|
7667
7703
|
if (requestDecision.decision === "deny") {
|
|
7668
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
|
|
7704
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", requestDecision.reason ?? "Permission request denied");
|
|
7669
7705
|
}
|
|
7670
7706
|
if (requestDecision.updatedInput !== void 0) {
|
|
7671
|
-
return this.#fail(tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
|
|
7707
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_input", "PermissionRequest cannot modify an already-authorized tool call");
|
|
7672
7708
|
}
|
|
7673
7709
|
if (this.#hooks.hasListeners("PermissionRequest") && requestDecision.decision === "allow") {
|
|
7674
7710
|
const ctx = requestDecision.additionalContext ?? "";
|
|
@@ -7677,18 +7713,18 @@ var ToolRuntime = class {
|
|
|
7677
7713
|
if (allowAlways && category !== "destructive") this.#alwaysAllowed.add(category);
|
|
7678
7714
|
}
|
|
7679
7715
|
} else if (context.agent !== "main" && this.#permissions.mode !== "bubble") {
|
|
7680
|
-
return this.#fail(tool.name, input, context.agent, "approval_required", reason);
|
|
7716
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "approval_required", reason);
|
|
7681
7717
|
} else if (signal.aborted) {
|
|
7682
7718
|
throw signal.reason;
|
|
7683
7719
|
} else {
|
|
7684
7720
|
const category = getToolCategory(tool.name);
|
|
7685
7721
|
if (allowAlways && this.#alwaysAllowed.has(category)) {
|
|
7686
7722
|
} else if (this.#approve === void 0) {
|
|
7687
|
-
return this.#fail(tool.name, input, context.agent, "permission_denied", reason);
|
|
7723
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "permission_denied", reason);
|
|
7688
7724
|
} else {
|
|
7689
7725
|
const decision = await this.#approve({ ...request, reason, ...allowAlways ? {} : { allowAlways: false } }, signal);
|
|
7690
7726
|
if (decision === "deny") {
|
|
7691
|
-
return this.#fail(tool.name, input, context.agent, "user_denied", reason);
|
|
7727
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "user_denied", reason);
|
|
7692
7728
|
}
|
|
7693
7729
|
if (decision === "always" && allowAlways && category !== "destructive") {
|
|
7694
7730
|
this.#alwaysAllowed.add(category);
|
|
@@ -7702,7 +7738,7 @@ var ToolRuntime = class {
|
|
|
7702
7738
|
try {
|
|
7703
7739
|
rawOutput = tool.outputSchema === void 0 ? executed : tool.outputSchema.parse(executed);
|
|
7704
7740
|
} catch (error) {
|
|
7705
|
-
return this.#fail(tool.name, input, context.agent, "invalid_output", message(error));
|
|
7741
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
|
|
7706
7742
|
}
|
|
7707
7743
|
const declaredPresentation = tool.presentResult?.(rawOutput, input);
|
|
7708
7744
|
const presentation = declaredPresentation ?? getToolPresentation(rawOutput);
|
|
@@ -7713,7 +7749,7 @@ var ToolRuntime = class {
|
|
|
7713
7749
|
await this.#hooks.emit({
|
|
7714
7750
|
version: 1,
|
|
7715
7751
|
type: "PostToolUse",
|
|
7716
|
-
payload: { tool: tool.name, input, agent: context.agent, output }
|
|
7752
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
|
|
7717
7753
|
});
|
|
7718
7754
|
return {
|
|
7719
7755
|
ok: true,
|
|
@@ -7723,7 +7759,7 @@ var ToolRuntime = class {
|
|
|
7723
7759
|
...additionalContext === void 0 || additionalContext.length === 0 ? {} : { additionalContext }
|
|
7724
7760
|
};
|
|
7725
7761
|
} catch (error) {
|
|
7726
|
-
return this.#fail(tool.name, input, context.agent, "tool_error", message(error));
|
|
7762
|
+
return this.#fail(toolCallId, tool.name, input, context.agent, "tool_error", message(error));
|
|
7727
7763
|
}
|
|
7728
7764
|
}
|
|
7729
7765
|
async #limitOutput(tool, output) {
|
|
@@ -7771,13 +7807,13 @@ var ToolRuntime = class {
|
|
|
7771
7807
|
const limited = await this.#limitOutput(tool, content);
|
|
7772
7808
|
return typeof limited === "string" ? limited : serializeToolOutput(limited);
|
|
7773
7809
|
}
|
|
7774
|
-
async #fail(tool, input, agent, code, errorMessage2) {
|
|
7810
|
+
async #fail(toolCallId, tool, input, agent, code, errorMessage2) {
|
|
7775
7811
|
const result = { ok: false, error: { code, message: errorMessage2 } };
|
|
7776
7812
|
try {
|
|
7777
7813
|
await this.#hooks.emit({
|
|
7778
7814
|
version: 1,
|
|
7779
7815
|
type: "PostToolUseFailure",
|
|
7780
|
-
payload: { tool, input, agent, error: result.error }
|
|
7816
|
+
payload: { toolCallId, tool, input, agent, error: result.error }
|
|
7781
7817
|
});
|
|
7782
7818
|
} catch {
|
|
7783
7819
|
}
|
|
@@ -8398,6 +8434,7 @@ function isWithin2(root, target) {
|
|
|
8398
8434
|
|
|
8399
8435
|
// src/hooks/bus.ts
|
|
8400
8436
|
import { spawn as spawn4 } from "child_process";
|
|
8437
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
8401
8438
|
|
|
8402
8439
|
// src/hooks/types.ts
|
|
8403
8440
|
import { z as z11 } from "zod";
|
|
@@ -8464,6 +8501,11 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1048576;
|
|
|
8464
8501
|
var HookBus = class {
|
|
8465
8502
|
#handlers = /* @__PURE__ */ new Map();
|
|
8466
8503
|
#payloadSchemas = /* @__PURE__ */ new Map();
|
|
8504
|
+
#options;
|
|
8505
|
+
#sequence = 0;
|
|
8506
|
+
constructor(options = {}) {
|
|
8507
|
+
this.#options = options;
|
|
8508
|
+
}
|
|
8467
8509
|
registerPayloadSchema(type, schema) {
|
|
8468
8510
|
const registrations = this.#payloadSchemas.get(type) ?? [];
|
|
8469
8511
|
const registration = { schema };
|
|
@@ -8500,7 +8542,9 @@ var HookBus = class {
|
|
|
8500
8542
|
async emit(rawEvent, externalSignal) {
|
|
8501
8543
|
externalSignal?.throwIfAborted();
|
|
8502
8544
|
let event = HookEventSchema.parse(rawEvent);
|
|
8503
|
-
|
|
8545
|
+
const validatedPayload = this.#validatePayload(event.type, event.payload);
|
|
8546
|
+
const envelope = this.#eventEnvelope();
|
|
8547
|
+
event = { ...event, payload: decoratePayload(validatedPayload, envelope) };
|
|
8504
8548
|
let aggregate2 = { decision: "allow" };
|
|
8505
8549
|
for (const registered of this.#handlers.get(event.type) ?? []) {
|
|
8506
8550
|
externalSignal?.throwIfAborted();
|
|
@@ -8515,8 +8559,8 @@ var HookBus = class {
|
|
|
8515
8559
|
externalSignal?.throwIfAborted();
|
|
8516
8560
|
if (decision.updatedInput !== void 0) {
|
|
8517
8561
|
const payload = this.#validatePayload(event.type, decision.updatedInput);
|
|
8518
|
-
event = HookEventSchema.parse({ ...event, payload });
|
|
8519
|
-
aggregate2 = { ...aggregate2, updatedInput:
|
|
8562
|
+
event = HookEventSchema.parse({ ...event, payload: decoratePayload(payload, envelope) });
|
|
8563
|
+
aggregate2 = { ...aggregate2, updatedInput: payload };
|
|
8520
8564
|
}
|
|
8521
8565
|
if (decision.additionalContext !== void 0) {
|
|
8522
8566
|
aggregate2 = {
|
|
@@ -8533,6 +8577,15 @@ var HookBus = class {
|
|
|
8533
8577
|
const schema = this.#payloadSchemas.get(type)?.at(-1)?.schema;
|
|
8534
8578
|
return schema ? schema.parse(payload) : zodRecord(payload);
|
|
8535
8579
|
}
|
|
8580
|
+
#eventEnvelope() {
|
|
8581
|
+
if (this.#options.eventContext === void 0) return void 0;
|
|
8582
|
+
return {
|
|
8583
|
+
...this.#options.eventContext(),
|
|
8584
|
+
eventId: (this.#options.eventId ?? randomUUID9)(),
|
|
8585
|
+
sequence: ++this.#sequence,
|
|
8586
|
+
timestamp: (this.#options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))()
|
|
8587
|
+
};
|
|
8588
|
+
}
|
|
8536
8589
|
async #invoke(registered, event, externalSignal) {
|
|
8537
8590
|
const timeoutSignal = AbortSignal.timeout(registered.timeoutMs);
|
|
8538
8591
|
const signal = externalSignal === void 0 ? timeoutSignal : AbortSignal.any([timeoutSignal, externalSignal]);
|
|
@@ -8544,6 +8597,9 @@ var HookBus = class {
|
|
|
8544
8597
|
return HookDecisionSchema.parse(await Promise.race([invocation, timeout]));
|
|
8545
8598
|
}
|
|
8546
8599
|
};
|
|
8600
|
+
function decoratePayload(payload, envelope) {
|
|
8601
|
+
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
8602
|
+
}
|
|
8547
8603
|
function runShellHandler(descriptor, event, signal) {
|
|
8548
8604
|
return new Promise((resolve28, reject) => {
|
|
8549
8605
|
const child = spawn4(descriptor.command, [...descriptor.args ?? []], {
|
|
@@ -8736,19 +8792,155 @@ function createIncidentReporter(options) {
|
|
|
8736
8792
|
};
|
|
8737
8793
|
}
|
|
8738
8794
|
|
|
8795
|
+
// src/island/control-server.ts
|
|
8796
|
+
import { createHash as createHash7, randomUUID as randomUUID10 } from "crypto";
|
|
8797
|
+
import { chmod as chmod3, unlink as unlink4 } from "fs/promises";
|
|
8798
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
8799
|
+
import { join as join9 } from "path";
|
|
8800
|
+
import { createServer as createServer2 } from "net";
|
|
8801
|
+
var MAX_REQUEST_BYTES = 64 * 1024;
|
|
8802
|
+
var SOCKET_TIMEOUT_MS = 1e4;
|
|
8803
|
+
function islandControlEndpoint(sessionId, platform = process.platform) {
|
|
8804
|
+
const digest = createHash7("sha256").update(`${process.pid}\0${sessionId}`).digest("hex").slice(0, 20);
|
|
8805
|
+
if (platform === "win32") return `\\\\.\\pipe\\flavor-island-control-${digest}`;
|
|
8806
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
8807
|
+
return join9(tmpdir2(), `flavor-island-control-${uid}-${digest}.sock`);
|
|
8808
|
+
}
|
|
8809
|
+
async function createIslandControlServer(options) {
|
|
8810
|
+
const platform = options.platform ?? process.platform;
|
|
8811
|
+
const endpoint = options.endpoint ?? islandControlEndpoint(options.sessionId, platform);
|
|
8812
|
+
const token = options.token ?? randomUUID10();
|
|
8813
|
+
const capabilities = [
|
|
8814
|
+
"abort",
|
|
8815
|
+
"steer",
|
|
8816
|
+
"follow_up",
|
|
8817
|
+
...options.focus === void 0 ? [] : ["focus"]
|
|
8818
|
+
];
|
|
8819
|
+
if (platform !== "win32") await unlink4(endpoint).catch(() => void 0);
|
|
8820
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
8821
|
+
const server = createServer2((socket) => {
|
|
8822
|
+
sockets.add(socket);
|
|
8823
|
+
socket.setTimeout(SOCKET_TIMEOUT_MS, () => socket.destroy());
|
|
8824
|
+
socket.on("close", () => sockets.delete(socket));
|
|
8825
|
+
socket.on("error", () => void 0);
|
|
8826
|
+
let buffer = Buffer.alloc(0);
|
|
8827
|
+
let handled = false;
|
|
8828
|
+
socket.on("data", (chunk) => {
|
|
8829
|
+
if (handled) return;
|
|
8830
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
8831
|
+
if (buffer.length + bytes.length > MAX_REQUEST_BYTES) {
|
|
8832
|
+
handled = true;
|
|
8833
|
+
write(socket, { ok: false, error: "request_too_large" });
|
|
8834
|
+
return;
|
|
8835
|
+
}
|
|
8836
|
+
buffer = Buffer.concat([buffer, bytes]);
|
|
8837
|
+
const newline = buffer.indexOf(10);
|
|
8838
|
+
if (newline < 0) return;
|
|
8839
|
+
handled = true;
|
|
8840
|
+
const line = buffer.subarray(0, newline).toString("utf8");
|
|
8841
|
+
void dispatch(line, socket, options, token);
|
|
8842
|
+
});
|
|
8843
|
+
});
|
|
8844
|
+
await new Promise((resolve28, reject) => {
|
|
8845
|
+
const onError = (error) => {
|
|
8846
|
+
server.removeListener("listening", onListening);
|
|
8847
|
+
reject(error);
|
|
8848
|
+
};
|
|
8849
|
+
const onListening = () => {
|
|
8850
|
+
server.removeListener("error", onError);
|
|
8851
|
+
resolve28();
|
|
8852
|
+
};
|
|
8853
|
+
server.once("error", onError);
|
|
8854
|
+
server.once("listening", onListening);
|
|
8855
|
+
server.listen(endpoint);
|
|
8856
|
+
});
|
|
8857
|
+
if (platform !== "win32") await chmod3(endpoint, 384).catch(() => void 0);
|
|
8858
|
+
let closed = false;
|
|
8859
|
+
return {
|
|
8860
|
+
endpoint,
|
|
8861
|
+
token,
|
|
8862
|
+
capabilities,
|
|
8863
|
+
async close() {
|
|
8864
|
+
if (closed) return;
|
|
8865
|
+
closed = true;
|
|
8866
|
+
for (const socket of sockets) socket.destroy();
|
|
8867
|
+
await closeServer(server);
|
|
8868
|
+
if (platform !== "win32") await unlink4(endpoint).catch(() => void 0);
|
|
8869
|
+
}
|
|
8870
|
+
};
|
|
8871
|
+
}
|
|
8872
|
+
async function dispatch(line, socket, options, token) {
|
|
8873
|
+
let request;
|
|
8874
|
+
try {
|
|
8875
|
+
const parsed = JSON.parse(line);
|
|
8876
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("invalid request");
|
|
8877
|
+
request = parsed;
|
|
8878
|
+
} catch {
|
|
8879
|
+
write(socket, { ok: false, error: "invalid_json" });
|
|
8880
|
+
return;
|
|
8881
|
+
}
|
|
8882
|
+
if (request.token !== token) {
|
|
8883
|
+
write(socket, { ok: false, error: "unauthorized" });
|
|
8884
|
+
return;
|
|
8885
|
+
}
|
|
8886
|
+
const command = request.command;
|
|
8887
|
+
try {
|
|
8888
|
+
if (command === "abort") {
|
|
8889
|
+
write(socket, { ok: true, result: options.session.interrupt() });
|
|
8890
|
+
} else if (command === "steer" || command === "follow_up") {
|
|
8891
|
+
const message2 = typeof request.message === "string" ? request.message.trim() : "";
|
|
8892
|
+
if (!message2 || message2.length > 1e5) {
|
|
8893
|
+
write(socket, { ok: false, error: "invalid_message" });
|
|
8894
|
+
return;
|
|
8895
|
+
}
|
|
8896
|
+
if (command === "steer") options.session.steer(message2);
|
|
8897
|
+
else options.session.followUp(message2);
|
|
8898
|
+
write(socket, { ok: true });
|
|
8899
|
+
} else if (command === "focus") {
|
|
8900
|
+
if (options.focus === void 0) {
|
|
8901
|
+
write(socket, { ok: false, error: "focus_unavailable" });
|
|
8902
|
+
return;
|
|
8903
|
+
}
|
|
8904
|
+
await options.focus();
|
|
8905
|
+
write(socket, { ok: true });
|
|
8906
|
+
} else {
|
|
8907
|
+
write(socket, { ok: false, error: "unknown_command" });
|
|
8908
|
+
}
|
|
8909
|
+
} catch (error) {
|
|
8910
|
+
write(socket, { ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
8911
|
+
}
|
|
8912
|
+
}
|
|
8913
|
+
function write(socket, payload) {
|
|
8914
|
+
try {
|
|
8915
|
+
socket.end(`${JSON.stringify(payload)}
|
|
8916
|
+
`);
|
|
8917
|
+
} catch {
|
|
8918
|
+
socket.destroy();
|
|
8919
|
+
}
|
|
8920
|
+
}
|
|
8921
|
+
function closeServer(server) {
|
|
8922
|
+
return new Promise((resolve28) => {
|
|
8923
|
+
try {
|
|
8924
|
+
server.close(() => resolve28());
|
|
8925
|
+
} catch {
|
|
8926
|
+
resolve28();
|
|
8927
|
+
}
|
|
8928
|
+
});
|
|
8929
|
+
}
|
|
8930
|
+
|
|
8739
8931
|
// src/evolve/service.ts
|
|
8740
8932
|
import { spawn as spawn5 } from "child_process";
|
|
8741
8933
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
8742
|
-
import { join as
|
|
8934
|
+
import { join as join12 } from "path";
|
|
8743
8935
|
import { z as z14 } from "zod";
|
|
8744
8936
|
|
|
8745
8937
|
// src/evolve/loader.ts
|
|
8746
8938
|
import { cp, mkdir as mkdir8, mkdtemp, readdir as readdir4, rm as rm2, stat as stat4, writeFile as writeFile6 } from "fs/promises";
|
|
8747
|
-
import { tmpdir as
|
|
8748
|
-
import { join as
|
|
8939
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
8940
|
+
import { join as join10, resolve as resolve10 } from "path";
|
|
8749
8941
|
|
|
8750
8942
|
// src/plugins/host.ts
|
|
8751
|
-
import { createHash as
|
|
8943
|
+
import { createHash as createHash8 } from "crypto";
|
|
8752
8944
|
import { lstatSync, realpathSync as realpathSync3 } from "fs";
|
|
8753
8945
|
import { lstat as lstat5, open as open5, readFile as readFile7, readdir as readdir3, realpath as realpath4, stat as stat3, writeFile as writeFile5 } from "fs/promises";
|
|
8754
8946
|
import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve9, sep as sep8 } from "path";
|
|
@@ -9109,7 +9301,7 @@ var PluginHost = class {
|
|
|
9109
9301
|
const manifest = PluginManifestSchema.parse(rawManifest);
|
|
9110
9302
|
const entry2 = resolveContained(root, manifest.main, "Plugin entry");
|
|
9111
9303
|
const entrySnapshot = await snapshotEntry(root, entry2);
|
|
9112
|
-
const fingerprint2 =
|
|
9304
|
+
const fingerprint2 = createHash8("sha256").update(JSON.stringify(manifest)).update(await readFile7(entrySnapshot.physicalPath)).digest("hex");
|
|
9113
9305
|
return { manifest, root, source, entry: entry2, entrySnapshot, fingerprint: fingerprint2 };
|
|
9114
9306
|
} catch (error) {
|
|
9115
9307
|
this.#diagnose(diagnosticLabel, error, inputRoot);
|
|
@@ -9468,8 +9660,8 @@ function compare(left, right) {
|
|
|
9468
9660
|
}
|
|
9469
9661
|
|
|
9470
9662
|
// src/evolve/loader.ts
|
|
9471
|
-
var PLUGIN_DIR =
|
|
9472
|
-
var VERSIONS_DIR =
|
|
9663
|
+
var PLUGIN_DIR = join10(".flavor", "plugins");
|
|
9664
|
+
var VERSIONS_DIR = join10(".flavor", "plugins", ".versions");
|
|
9473
9665
|
function sanitizePluginName(tool) {
|
|
9474
9666
|
const base = String(tool ?? "fix").replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
9475
9667
|
return base.startsWith("fix-") ? base : `fix-${base}`;
|
|
@@ -9513,8 +9705,8 @@ export function activate(ctx) {
|
|
|
9513
9705
|
async function scaffoldFixPlugin(workspace, name) {
|
|
9514
9706
|
const dir = fixPluginDir(workspace, name);
|
|
9515
9707
|
await mkdir8(dir, { recursive: true });
|
|
9516
|
-
const manifestPath =
|
|
9517
|
-
const entryPath =
|
|
9708
|
+
const manifestPath = join10(dir, "flavor-plugin.json");
|
|
9709
|
+
const entryPath = join10(dir, "index.js");
|
|
9518
9710
|
try {
|
|
9519
9711
|
await writeFile6(manifestPath, SCAFFOLD_MANIFEST(name), { encoding: "utf8", flag: "wx" });
|
|
9520
9712
|
await writeFile6(entryPath, SCAFFOLD_ENTRY(name), { encoding: "utf8", flag: "wx" });
|
|
@@ -9534,9 +9726,9 @@ async function verifyFixPlugin(workspace, name) {
|
|
|
9534
9726
|
return { ok: false, provided: [], tools: [], commands: [], error: `Plugin "${name}" not found.` };
|
|
9535
9727
|
}
|
|
9536
9728
|
const collected = { tools: [], commands: [] };
|
|
9537
|
-
const sandbox = await mkdtemp(
|
|
9729
|
+
const sandbox = await mkdtemp(join10(tmpdir3(), "flavor-evolve-verify-"));
|
|
9538
9730
|
try {
|
|
9539
|
-
await cp(pluginRoot,
|
|
9731
|
+
await cp(pluginRoot, join10(sandbox, name), { recursive: true });
|
|
9540
9732
|
const host = new PluginHost({
|
|
9541
9733
|
projectPluginDirs: [sandbox],
|
|
9542
9734
|
sandbox: true,
|
|
@@ -9587,7 +9779,7 @@ async function verifyFixPlugin(workspace, name) {
|
|
|
9587
9779
|
}
|
|
9588
9780
|
async function snapshotFixPlugin(workspace, name) {
|
|
9589
9781
|
const pluginRoot = fixPluginDir(workspace, name);
|
|
9590
|
-
const snapshotRoot =
|
|
9782
|
+
const snapshotRoot = join10(versionsDirFor(workspace, name), timestamp());
|
|
9591
9783
|
await mkdir8(snapshotRoot, { recursive: true });
|
|
9592
9784
|
await cp(pluginRoot, snapshotRoot, { recursive: true });
|
|
9593
9785
|
return snapshotRoot;
|
|
@@ -9608,7 +9800,7 @@ async function revertFixPlugin(workspace, name) {
|
|
|
9608
9800
|
}
|
|
9609
9801
|
const snapshots = entries.filter((entry2) => !entry2.startsWith(".")).sort();
|
|
9610
9802
|
if (snapshots.length === 0) throw new Error(`Plugin "${name}" has no snapshot to restore.`);
|
|
9611
|
-
const latest =
|
|
9803
|
+
const latest = join10(versionsRoot, snapshots[snapshots.length - 1]);
|
|
9612
9804
|
await rm2(pluginRoot, { recursive: true, force: true });
|
|
9613
9805
|
await mkdir8(pluginRoot, { recursive: true });
|
|
9614
9806
|
await cp(latest, pluginRoot, { recursive: true });
|
|
@@ -9616,14 +9808,14 @@ async function revertFixPlugin(workspace, name) {
|
|
|
9616
9808
|
}
|
|
9617
9809
|
|
|
9618
9810
|
// src/evolve/store.ts
|
|
9619
|
-
import { createHash as
|
|
9811
|
+
import { createHash as createHash9 } from "crypto";
|
|
9620
9812
|
import { appendFile, mkdir as mkdir9, readFile as readFile8, rm as rm3, writeFile as writeFile7 } from "fs/promises";
|
|
9621
|
-
import { dirname as dirname11, join as
|
|
9813
|
+
import { dirname as dirname11, join as join11 } from "path";
|
|
9622
9814
|
function normalizeError(message2) {
|
|
9623
9815
|
return String(message2 ?? "").replace(/\s+/g, " ").replace(/"[^"]*"/g, '"\u2026"').replace(/`[^`]*`/g, '"\u2026"').trim().slice(0, 160);
|
|
9624
9816
|
}
|
|
9625
9817
|
function fingerprint(tool, errorCode, error) {
|
|
9626
|
-
return
|
|
9818
|
+
return createHash9("sha1").update(`${tool}::${errorCode ?? ""}::${normalizeError(error)}`).digest("hex").slice(0, 12);
|
|
9627
9819
|
}
|
|
9628
9820
|
function argKeys(args) {
|
|
9629
9821
|
if (args === null || typeof args !== "object") return [];
|
|
@@ -9691,12 +9883,12 @@ var EvolveStore = class {
|
|
|
9691
9883
|
#queue = Promise.resolve();
|
|
9692
9884
|
constructor(options) {
|
|
9693
9885
|
const { workspace, maxSignals = 400, maxRules = 20 } = options;
|
|
9694
|
-
this.dir =
|
|
9695
|
-
this.signalsFile =
|
|
9696
|
-
this.reflectionsFile =
|
|
9697
|
-
this.doneFile =
|
|
9698
|
-
this.verifiedFile =
|
|
9699
|
-
this.rulesFile =
|
|
9886
|
+
this.dir = join11(workspace, ".flavor", "evolve");
|
|
9887
|
+
this.signalsFile = join11(this.dir, "signals.jsonl");
|
|
9888
|
+
this.reflectionsFile = join11(this.dir, "reflections.jsonl");
|
|
9889
|
+
this.doneFile = join11(this.dir, "done.json");
|
|
9890
|
+
this.verifiedFile = join11(this.dir, "verified.json");
|
|
9891
|
+
this.rulesFile = join11(this.dir, "rules.json");
|
|
9700
9892
|
this.maxSignals = maxSignals;
|
|
9701
9893
|
this.maxRules = maxRules;
|
|
9702
9894
|
}
|
|
@@ -10235,7 +10427,7 @@ function createEvolveService(options) {
|
|
|
10235
10427
|
if (suggestion === void 0) return `No open suggestion with id "${suggestionId}". Use /evolve suggest to list them.`;
|
|
10236
10428
|
const name = sanitizePluginName(suggestion.tool);
|
|
10237
10429
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
10238
|
-
await writeFile8(
|
|
10430
|
+
await writeFile8(join12(dir, "PLAN.md"), buildPlan(suggestion, name, "See the repeated failure below; describe your fix in PLAN.md."), "utf8");
|
|
10239
10431
|
await snapshotFixPlugin(workspace, name);
|
|
10240
10432
|
return [
|
|
10241
10433
|
`suggestion ${suggestion.id}: ${suggestion.tool} x${suggestion.count} \u2014 ${suggestion.error}`,
|
|
@@ -10326,7 +10518,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10326
10518
|
}
|
|
10327
10519
|
const name = sanitizePluginName(suggestion.tool);
|
|
10328
10520
|
const dir = await scaffoldFixPlugin(workspace, name);
|
|
10329
|
-
await writeFile8(
|
|
10521
|
+
await writeFile8(join12(dir, "PLAN.md"), buildPlan(suggestion, name, implementation), "utf8");
|
|
10330
10522
|
await snapshotFixPlugin(workspace, name);
|
|
10331
10523
|
return [
|
|
10332
10524
|
`Scaffolded fix plugin at ${dir} for suggestion [${suggestion.id}] (${suggestion.tool} x${suggestion.count}).`,
|
|
@@ -10747,9 +10939,9 @@ ${evidence.summary}`;
|
|
|
10747
10939
|
};
|
|
10748
10940
|
|
|
10749
10941
|
// src/goal/orchestrator.ts
|
|
10750
|
-
import { createHash as
|
|
10942
|
+
import { createHash as createHash11 } from "crypto";
|
|
10751
10943
|
import { writeFile as writeFile9, mkdir as mkdir10 } from "fs/promises";
|
|
10752
|
-
import { join as
|
|
10944
|
+
import { join as join14 } from "path";
|
|
10753
10945
|
|
|
10754
10946
|
// src/goal/types.ts
|
|
10755
10947
|
import { z as z16 } from "zod";
|
|
@@ -10912,9 +11104,9 @@ function extractJson(text) {
|
|
|
10912
11104
|
}
|
|
10913
11105
|
|
|
10914
11106
|
// src/goal/classifier.ts
|
|
10915
|
-
import { createHash as
|
|
11107
|
+
import { createHash as createHash10 } from "crypto";
|
|
10916
11108
|
import { readFile as readFile9 } from "fs/promises";
|
|
10917
|
-
import { join as
|
|
11109
|
+
import { join as join13 } from "path";
|
|
10918
11110
|
function buildSkepticPrompt(evidence, plan, index) {
|
|
10919
11111
|
const criteriaList = plan.criteria.map((c) => ` ${c.id}. [${c.type}] ${c.description}`).join("\n");
|
|
10920
11112
|
return [
|
|
@@ -11087,11 +11279,11 @@ function deduplicateGaps(gaps) {
|
|
|
11087
11279
|
}
|
|
11088
11280
|
function computeFingerprint(gaps) {
|
|
11089
11281
|
const content = gaps.map((g) => `${g.criterion}:${g.description}`).sort().join("\n");
|
|
11090
|
-
return
|
|
11282
|
+
return createHash10("sha256").update(content).digest("hex").slice(0, 16);
|
|
11091
11283
|
}
|
|
11092
|
-
async function collectEvidence(workspace, objective, finalResponse, priorGaps, contractHash2 =
|
|
11284
|
+
async function collectEvidence(workspace, objective, finalResponse, priorGaps, contractHash2 = createHash10("sha256").update(objective).digest("hex"), hostVerification) {
|
|
11093
11285
|
const workspaceState = await collectWorkspaceState(workspace);
|
|
11094
|
-
const planPath =
|
|
11286
|
+
const planPath = join13(workspace, ".flavor", "goal-plan.md");
|
|
11095
11287
|
let planFile = null;
|
|
11096
11288
|
try {
|
|
11097
11289
|
planFile = await readFile9(planPath, "utf8");
|
|
@@ -11118,7 +11310,7 @@ async function collectWorkspaceState(workspace) {
|
|
|
11118
11310
|
return {
|
|
11119
11311
|
changedFiles: [],
|
|
11120
11312
|
status: ["not-a-git-worktree"],
|
|
11121
|
-
diffHash:
|
|
11313
|
+
diffHash: createHash10("sha256").update("not-a-git-worktree").digest("hex")
|
|
11122
11314
|
};
|
|
11123
11315
|
}
|
|
11124
11316
|
const status = statusResult.stdout.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean);
|
|
@@ -11135,7 +11327,7 @@ async function collectWorkspaceState(workspace) {
|
|
|
11135
11327
|
return {
|
|
11136
11328
|
changedFiles,
|
|
11137
11329
|
status,
|
|
11138
|
-
diffHash:
|
|
11330
|
+
diffHash: createHash10("sha256").update(diff.stdout).update("\nUNTRACKED\n").update(untracked).digest("hex")
|
|
11139
11331
|
};
|
|
11140
11332
|
}
|
|
11141
11333
|
|
|
@@ -11267,7 +11459,7 @@ var GoalOrchestrator = class {
|
|
|
11267
11459
|
type: "not_achieved",
|
|
11268
11460
|
gaps,
|
|
11269
11461
|
summary: hostVerification.summary,
|
|
11270
|
-
fingerprint:
|
|
11462
|
+
fingerprint: createHash11("sha256").update(hostVerification.summary).digest("hex").slice(0, 16)
|
|
11271
11463
|
};
|
|
11272
11464
|
}
|
|
11273
11465
|
} else try {
|
|
@@ -11341,11 +11533,11 @@ var GoalOrchestrator = class {
|
|
|
11341
11533
|
}
|
|
11342
11534
|
};
|
|
11343
11535
|
function contractHash(objective, plan) {
|
|
11344
|
-
return
|
|
11536
|
+
return createHash11("sha256").update(JSON.stringify({ objective, plan })).digest("hex");
|
|
11345
11537
|
}
|
|
11346
11538
|
async function writePlanFile(workspace, plan) {
|
|
11347
|
-
const dir =
|
|
11348
|
-
const planPath =
|
|
11539
|
+
const dir = join14(workspace, ".flavor");
|
|
11540
|
+
const planPath = join14(dir, "goal-plan.md");
|
|
11349
11541
|
await mkdir10(dir, { recursive: true });
|
|
11350
11542
|
const criteriaText = plan.criteria.map((c) => `- [${c.type}] AC-${c.id}: ${c.description}`).join("\n");
|
|
11351
11543
|
const content = [
|
|
@@ -11421,9 +11613,9 @@ function formatPriorGaps(gaps) {
|
|
|
11421
11613
|
|
|
11422
11614
|
// src/goal/store.ts
|
|
11423
11615
|
import { constants as constants2 } from "fs";
|
|
11424
|
-
import { randomUUID as
|
|
11616
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
11425
11617
|
import { lstat as lstat6, mkdir as mkdir11, open as open6, rename as rename4, rm as rm4 } from "fs/promises";
|
|
11426
|
-
import { dirname as dirname12, join as
|
|
11618
|
+
import { dirname as dirname12, join as join15, relative as relative9, resolve as resolve11, sep as sep9 } from "path";
|
|
11427
11619
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
11428
11620
|
var GoalStore = class {
|
|
11429
11621
|
#workspace;
|
|
@@ -11431,7 +11623,7 @@ var GoalStore = class {
|
|
|
11431
11623
|
#maxBytes;
|
|
11432
11624
|
constructor(options) {
|
|
11433
11625
|
this.#workspace = resolve11(options.workspace);
|
|
11434
|
-
this.#root =
|
|
11626
|
+
this.#root = join15(this.#workspace, ".flavor", "goals");
|
|
11435
11627
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
11436
11628
|
}
|
|
11437
11629
|
async save(input) {
|
|
@@ -11439,13 +11631,13 @@ var GoalStore = class {
|
|
|
11439
11631
|
await assertNoSymlink(this.#workspace, dirname12(this.#root));
|
|
11440
11632
|
await mkdir11(this.#root, { recursive: true, mode: 448 });
|
|
11441
11633
|
await assertNoSymlink(this.#workspace, this.#root);
|
|
11442
|
-
const target =
|
|
11634
|
+
const target = join15(this.#root, `${state.id}.json`);
|
|
11443
11635
|
const body = `${JSON.stringify(state, null, 2)}
|
|
11444
11636
|
`;
|
|
11445
11637
|
if (Buffer.byteLength(body) > this.#maxBytes) {
|
|
11446
11638
|
throw new Error(`Goal state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
11447
11639
|
}
|
|
11448
|
-
const temporary =
|
|
11640
|
+
const temporary = join15(this.#root, `.${state.id}.${process.pid}.${randomUUID11()}.tmp`);
|
|
11449
11641
|
let handle;
|
|
11450
11642
|
try {
|
|
11451
11643
|
handle = await open6(temporary, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
|
|
@@ -11486,7 +11678,7 @@ function isCode3(error, code) {
|
|
|
11486
11678
|
|
|
11487
11679
|
// src/loop/isolation.ts
|
|
11488
11680
|
import { appendFile as appendFile2, mkdir as mkdir12, readFile as readFile10 } from "fs/promises";
|
|
11489
|
-
import { join as
|
|
11681
|
+
import { join as join16, resolve as resolve12 } from "path";
|
|
11490
11682
|
var CODE_CHANGE = /\b(?:fix|implement|add|update|change|modify|refactor|build|create|remove|delete|write|migrate)\b|修复|实现|添加|更新|修改|重构|创建|删除|迁移/i;
|
|
11491
11683
|
var READ_ONLY = /\b(?:review|analy[sz]e|inspect|explain|research|investigate|audit|summari[sz]e|report)\b|调研|分析|审查|检查|解释|研究|总结|报告/i;
|
|
11492
11684
|
function inferGoalIntent(goal) {
|
|
@@ -11509,8 +11701,8 @@ async function prepareLoopWorkspace(options) {
|
|
|
11509
11701
|
return { kind: "needs_human", reason: "The workspace has uncommitted changes that a new worktree would omit." };
|
|
11510
11702
|
}
|
|
11511
11703
|
options.signal?.throwIfAborted();
|
|
11512
|
-
const worktrees =
|
|
11513
|
-
const path =
|
|
11704
|
+
const worktrees = join16(root, ".worktrees");
|
|
11705
|
+
const path = join16(worktrees, `loop-${options.loopId}`);
|
|
11514
11706
|
const branch = `loop/${options.loopId}`;
|
|
11515
11707
|
try {
|
|
11516
11708
|
await ensureWorktreeDirectoryIgnored(root);
|
|
@@ -11540,8 +11732,8 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
11540
11732
|
const commonDirectory = await git(root, ["rev-parse", "--git-common-dir"]);
|
|
11541
11733
|
if (commonDirectory.code !== 0) throw new Error(commonDirectory.stderr.trim() || "Git common directory is unavailable");
|
|
11542
11734
|
const gitDirectory = resolve12(root, commonDirectory.stdout.trim());
|
|
11543
|
-
const infoDirectory =
|
|
11544
|
-
const excludePath =
|
|
11735
|
+
const infoDirectory = join16(gitDirectory, "info");
|
|
11736
|
+
const excludePath = join16(infoDirectory, "exclude");
|
|
11545
11737
|
await mkdir12(infoDirectory, { recursive: true });
|
|
11546
11738
|
let existing = "";
|
|
11547
11739
|
try {
|
|
@@ -11557,8 +11749,8 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
11557
11749
|
// src/loop/store.ts
|
|
11558
11750
|
import { constants as constants3 } from "fs";
|
|
11559
11751
|
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";
|
|
11560
|
-
import { dirname as dirname13, join as
|
|
11561
|
-
import { randomUUID as
|
|
11752
|
+
import { dirname as dirname13, join as join17, relative as relative10, resolve as resolve13, sep as sep10 } from "path";
|
|
11753
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
11562
11754
|
var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
11563
11755
|
var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
|
|
11564
11756
|
var LoopStore = class {
|
|
@@ -11568,7 +11760,7 @@ var LoopStore = class {
|
|
|
11568
11760
|
#appendTails = /* @__PURE__ */ new Map();
|
|
11569
11761
|
constructor(options) {
|
|
11570
11762
|
this.#workspace = resolve13(options.workspace);
|
|
11571
|
-
this.#root =
|
|
11763
|
+
this.#root = join17(this.#workspace, ".flavor", "loops");
|
|
11572
11764
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES2;
|
|
11573
11765
|
}
|
|
11574
11766
|
async create(state) {
|
|
@@ -11585,11 +11777,11 @@ var LoopStore = class {
|
|
|
11585
11777
|
const state = LoopStateSchema.parse(input);
|
|
11586
11778
|
await this.#assertWorkspace(state.workspace);
|
|
11587
11779
|
const directory = await this.#prepare(state.loopId);
|
|
11588
|
-
const target =
|
|
11780
|
+
const target = join17(directory, "state.json");
|
|
11589
11781
|
const body = `${JSON.stringify(state, null, 2)}
|
|
11590
11782
|
`;
|
|
11591
11783
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Loop state exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
11592
|
-
const temporary =
|
|
11784
|
+
const temporary = join17(directory, `.state.${process.pid}.${randomUUID12()}.tmp`);
|
|
11593
11785
|
let handle;
|
|
11594
11786
|
try {
|
|
11595
11787
|
handle = await open7(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
|
|
@@ -11605,7 +11797,7 @@ var LoopStore = class {
|
|
|
11605
11797
|
}
|
|
11606
11798
|
async load(loopId) {
|
|
11607
11799
|
const directory = await this.#safeExisting(loopId);
|
|
11608
|
-
const target =
|
|
11800
|
+
const target = join17(directory, "state.json");
|
|
11609
11801
|
try {
|
|
11610
11802
|
const metadata = await stat5(target);
|
|
11611
11803
|
if (!metadata.isFile() || metadata.size > this.#maxBytes) throw new Error("Loop state is not a bounded regular file");
|
|
@@ -11615,7 +11807,7 @@ var LoopStore = class {
|
|
|
11615
11807
|
return state;
|
|
11616
11808
|
} catch (error) {
|
|
11617
11809
|
if (isCode4(error, "ENOENT")) throw new Error(`Loop "${loopId}" was not found`);
|
|
11618
|
-
const quarantine = `${target}.corrupt-${Date.now()}-${
|
|
11810
|
+
const quarantine = `${target}.corrupt-${Date.now()}-${randomUUID12().slice(0, 8)}`;
|
|
11619
11811
|
await rename5(target, quarantine).catch(() => void 0);
|
|
11620
11812
|
throw new Error(`Loop "${loopId}" is corrupt and was quarantined: ${message(error)}`);
|
|
11621
11813
|
}
|
|
@@ -11625,7 +11817,7 @@ var LoopStore = class {
|
|
|
11625
11817
|
const previous = this.#appendTails.get(event.loopId) ?? Promise.resolve();
|
|
11626
11818
|
const next = previous.catch(() => void 0).then(async () => {
|
|
11627
11819
|
const directory = await this.#prepare(event.loopId);
|
|
11628
|
-
const path =
|
|
11820
|
+
const path = join17(directory, "events.jsonl");
|
|
11629
11821
|
const handle = await open7(path, constants3.O_CREAT | constants3.O_APPEND | constants3.O_WRONLY, 384);
|
|
11630
11822
|
try {
|
|
11631
11823
|
await handle.writeFile(`${JSON.stringify(event)}
|
|
@@ -11642,7 +11834,7 @@ var LoopStore = class {
|
|
|
11642
11834
|
await assertNoSymlink2(this.#workspace, dirname13(this.#root));
|
|
11643
11835
|
await mkdir13(this.#root, { recursive: true, mode: 448 });
|
|
11644
11836
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
11645
|
-
const directory =
|
|
11837
|
+
const directory = join17(this.#root, loopId);
|
|
11646
11838
|
await mkdir13(directory, { recursive: true, mode: 448 });
|
|
11647
11839
|
await assertNoSymlink2(this.#workspace, directory);
|
|
11648
11840
|
return directory;
|
|
@@ -11650,7 +11842,7 @@ var LoopStore = class {
|
|
|
11650
11842
|
async #safeExisting(loopId) {
|
|
11651
11843
|
validateId(loopId);
|
|
11652
11844
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
11653
|
-
const directory =
|
|
11845
|
+
const directory = join17(this.#root, loopId);
|
|
11654
11846
|
await assertNoSymlink2(this.#workspace, directory);
|
|
11655
11847
|
return directory;
|
|
11656
11848
|
}
|
|
@@ -11699,7 +11891,7 @@ function isCode4(error, code) {
|
|
|
11699
11891
|
|
|
11700
11892
|
// src/loop/verifier.ts
|
|
11701
11893
|
import { readFile as readFile12 } from "fs/promises";
|
|
11702
|
-
import { join as
|
|
11894
|
+
import { join as join18 } from "path";
|
|
11703
11895
|
|
|
11704
11896
|
// src/tools/shell.ts
|
|
11705
11897
|
import { spawn as spawn6, spawnSync } from "child_process";
|
|
@@ -12257,13 +12449,13 @@ var SCRIPT_PRIORITY = ["test", "typecheck", "lint", "build", "smoke:install"];
|
|
|
12257
12449
|
async function inferVerificationPlan(workspace) {
|
|
12258
12450
|
const commands = [];
|
|
12259
12451
|
const seen = /* @__PURE__ */ new Set();
|
|
12260
|
-
const packageJson = await optionalJson(
|
|
12452
|
+
const packageJson = await optionalJson(join18(workspace, "package.json"));
|
|
12261
12453
|
const scripts = isRecord2(packageJson?.scripts) ? packageJson.scripts : {};
|
|
12262
12454
|
for (const name of SCRIPT_PRIORITY) {
|
|
12263
12455
|
if (typeof scripts[name] !== "string" || isUnconditionalPassThrough(scripts[name])) continue;
|
|
12264
12456
|
add(commands, seen, npmScript(name));
|
|
12265
12457
|
}
|
|
12266
|
-
const flavor = await optionalText(
|
|
12458
|
+
const flavor = await optionalText(join18(workspace, "FLAVOR.md"));
|
|
12267
12459
|
if (flavor !== void 0) {
|
|
12268
12460
|
for (const match of flavor.matchAll(/^- `([^`]+)`\s*$/gm)) {
|
|
12269
12461
|
const parsed = parseTrustedCommand(match[1] ?? "");
|
|
@@ -12966,7 +13158,7 @@ var ModelRegistry = class {
|
|
|
12966
13158
|
};
|
|
12967
13159
|
|
|
12968
13160
|
// src/mcp/client.ts
|
|
12969
|
-
import { createHash as
|
|
13161
|
+
import { createHash as createHash12 } from "crypto";
|
|
12970
13162
|
import { z as z19 } from "zod";
|
|
12971
13163
|
var McpManager = class {
|
|
12972
13164
|
#workspace;
|
|
@@ -13133,7 +13325,7 @@ function mcpToolName(serverName, remoteName) {
|
|
|
13133
13325
|
const safe = remoteName.replace(/[^A-Za-z0-9_-]/g, "_") || "tool";
|
|
13134
13326
|
const direct = `${prefix}${safe}`;
|
|
13135
13327
|
if (direct.length <= 64 && safe === remoteName) return direct;
|
|
13136
|
-
const suffix = `__${
|
|
13328
|
+
const suffix = `__${createHash12("sha256").update(remoteName).digest("hex").slice(0, 8)}`;
|
|
13137
13329
|
return `${prefix}${safe.slice(0, Math.max(1, 64 - prefix.length - suffix.length))}${suffix}`;
|
|
13138
13330
|
}
|
|
13139
13331
|
function mcpErrorText(content) {
|
|
@@ -13199,14 +13391,14 @@ async function connectSdkMcpClient(input, dependencies = DEFAULT_DEPENDENCIES) {
|
|
|
13199
13391
|
}
|
|
13200
13392
|
|
|
13201
13393
|
// src/auth/oauth.ts
|
|
13202
|
-
import { randomBytes as randomBytes2, createHash as
|
|
13203
|
-
import { createServer as
|
|
13394
|
+
import { randomBytes as randomBytes2, createHash as createHash14 } from "crypto";
|
|
13395
|
+
import { createServer as createServer3 } from "http";
|
|
13204
13396
|
import { URL as URL2, URLSearchParams } from "url";
|
|
13205
13397
|
|
|
13206
13398
|
// src/auth/oauth-config.ts
|
|
13207
|
-
import { createHash as
|
|
13399
|
+
import { createHash as createHash13 } from "crypto";
|
|
13208
13400
|
function oauthCredentialId(tokenUrl, clientId) {
|
|
13209
|
-
const digest =
|
|
13401
|
+
const digest = createHash13("sha256").update(`${tokenUrl}\0${clientId}`, "utf8").digest("base64url");
|
|
13210
13402
|
return `oauth:${digest}`;
|
|
13211
13403
|
}
|
|
13212
13404
|
function parseOAuthLlmConfig(value) {
|
|
@@ -13383,7 +13575,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13383
13575
|
serverResolve = resolve28;
|
|
13384
13576
|
serverReject = reject;
|
|
13385
13577
|
});
|
|
13386
|
-
const server =
|
|
13578
|
+
const server = createServer3((req, res) => {
|
|
13387
13579
|
const url = new URL2(req.url ?? "/", `http://${this.#callbackHost}:${server.address().port}`);
|
|
13388
13580
|
if (url.searchParams.has("error")) {
|
|
13389
13581
|
const error = url.searchParams.get("error") ?? "access_denied";
|
|
@@ -13463,7 +13655,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
13463
13655
|
return randomBytes2(32).toString("base64url");
|
|
13464
13656
|
}
|
|
13465
13657
|
#computeCodeChallenge(verifier) {
|
|
13466
|
-
return
|
|
13658
|
+
return createHash14("sha256").update(verifier).digest("base64url");
|
|
13467
13659
|
}
|
|
13468
13660
|
#generateState() {
|
|
13469
13661
|
return randomBytes2(32).toString("base64url");
|
|
@@ -13574,7 +13766,7 @@ function isStoredToken(value) {
|
|
|
13574
13766
|
|
|
13575
13767
|
// src/permissions/policy.ts
|
|
13576
13768
|
import { readFile as readFile15 } from "fs/promises";
|
|
13577
|
-
import { join as
|
|
13769
|
+
import { join as join19, resolve as resolve16 } from "path";
|
|
13578
13770
|
import { z as z20 } from "zod";
|
|
13579
13771
|
var DecisionSchema2 = z20.enum(["allow", "ask", "deny"]);
|
|
13580
13772
|
var PrefixSchema = z20.array(z20.string().min(1).max(1024)).min(1).max(64);
|
|
@@ -13611,9 +13803,9 @@ var CompiledPermissionPolicy = class {
|
|
|
13611
13803
|
async function loadPermissionPolicy(options) {
|
|
13612
13804
|
const sources = [
|
|
13613
13805
|
...options.managedPath === void 0 ? [] : [{ tier: "managed", path: resolve16(options.managedPath) }],
|
|
13614
|
-
{ tier: "user", path:
|
|
13615
|
-
{ tier: "project", path:
|
|
13616
|
-
{ tier: "local", path:
|
|
13806
|
+
{ tier: "user", path: join19(resolve16(options.home), ".flavor-code", "permissions.json") },
|
|
13807
|
+
{ tier: "project", path: join19(resolve16(options.workspace), ".flavor", "permissions.json") },
|
|
13808
|
+
{ tier: "local", path: join19(resolve16(options.workspace), ".flavor", "permissions.local.json") }
|
|
13617
13809
|
];
|
|
13618
13810
|
const rules = [];
|
|
13619
13811
|
for (const source of sources) {
|
|
@@ -14382,8 +14574,8 @@ function createSkillResourceTool(registry) {
|
|
|
14382
14574
|
// src/session/store.ts
|
|
14383
14575
|
import { constants as constants5 } from "fs";
|
|
14384
14576
|
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";
|
|
14385
|
-
import { basename as basename3, dirname as dirname15, join as
|
|
14386
|
-
import { randomUUID as
|
|
14577
|
+
import { basename as basename3, dirname as dirname15, join as join20, relative as relative13, resolve as resolve18, sep as sep13 } from "path";
|
|
14578
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
14387
14579
|
import { z as z22 } from "zod";
|
|
14388
14580
|
var SESSION_VERSION = 4;
|
|
14389
14581
|
var DEFAULT_MAX_SESSION_BYTES = 5 * 1024 * 1024;
|
|
@@ -14509,7 +14701,7 @@ var SessionStore = class {
|
|
|
14509
14701
|
#maxSessions;
|
|
14510
14702
|
constructor(options) {
|
|
14511
14703
|
this.#workspace = resolve18(options.workspace);
|
|
14512
|
-
this.#sessions =
|
|
14704
|
+
this.#sessions = join20(this.#workspace, ".flavor", "sessions");
|
|
14513
14705
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_SESSION_BYTES;
|
|
14514
14706
|
if (!Number.isSafeInteger(this.#maxBytes) || this.#maxBytes < 256) throw new Error("maxBytes must be an integer of at least 256");
|
|
14515
14707
|
this.#maxSessions = options.maxSessions ?? 50;
|
|
@@ -14541,7 +14733,7 @@ var SessionStore = class {
|
|
|
14541
14733
|
const body = `${lines.join("\n")}
|
|
14542
14734
|
`;
|
|
14543
14735
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
14544
|
-
const temporary =
|
|
14736
|
+
const temporary = join20(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID13()}.tmp`);
|
|
14545
14737
|
let handle;
|
|
14546
14738
|
try {
|
|
14547
14739
|
handle = await open8(temporary, constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY, 384);
|
|
@@ -14658,7 +14850,7 @@ var SessionStore = class {
|
|
|
14658
14850
|
const metadata = await lstat9(path);
|
|
14659
14851
|
if (!metadata.isFile()) throw new Error("Session path is not a regular file");
|
|
14660
14852
|
await rm6(path);
|
|
14661
|
-
await rm6(
|
|
14853
|
+
await rm6(join20(this.#sessions, `${sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
|
|
14662
14854
|
} catch (error) {
|
|
14663
14855
|
if (!isCode7(error, "ENOENT")) throw error;
|
|
14664
14856
|
}
|
|
@@ -14675,7 +14867,7 @@ var SessionStore = class {
|
|
|
14675
14867
|
const excess = entries.slice(this.#maxSessions);
|
|
14676
14868
|
for (const entry2 of excess) {
|
|
14677
14869
|
await rm6(this.#path(entry2.sessionId), { force: true }).catch(() => void 0);
|
|
14678
|
-
await rm6(
|
|
14870
|
+
await rm6(join20(this.#sessions, `${entry2.sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
|
|
14679
14871
|
}
|
|
14680
14872
|
}
|
|
14681
14873
|
async #assertWorkspace(stored) {
|
|
@@ -14697,7 +14889,7 @@ var SessionStore = class {
|
|
|
14697
14889
|
if (!isWithin5(canonicalRoot, canonicalSessions)) throw new Error("Session directory escapes the workspace");
|
|
14698
14890
|
}
|
|
14699
14891
|
async #quarantine(path) {
|
|
14700
|
-
const target = `${path}.corrupt-${Date.now()}-${
|
|
14892
|
+
const target = `${path}.corrupt-${Date.now()}-${randomUUID13().slice(0, 8)}`;
|
|
14701
14893
|
await rename6(path, target);
|
|
14702
14894
|
return target;
|
|
14703
14895
|
}
|
|
@@ -14917,9 +15109,9 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
14917
15109
|
|
|
14918
15110
|
// src/sleep/organizer.ts
|
|
14919
15111
|
import { constants as constants6 } from "fs";
|
|
14920
|
-
import { randomUUID as
|
|
15112
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
14921
15113
|
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";
|
|
14922
|
-
import { isAbsolute as isAbsolute8, join as
|
|
15114
|
+
import { isAbsolute as isAbsolute8, join as join21, relative as relative14, resolve as resolve19, sep as sep14 } from "path";
|
|
14923
15115
|
import { z as z23 } from "zod";
|
|
14924
15116
|
var DateKeySchema = z23.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
14925
15117
|
var ReviewSchema = z23.object({
|
|
@@ -14974,7 +15166,7 @@ var ProjectSleepOrganizer = class {
|
|
|
14974
15166
|
#generate;
|
|
14975
15167
|
constructor(options) {
|
|
14976
15168
|
this.#workspace = resolve19(options.workspace);
|
|
14977
|
-
this.#sleepDirectory =
|
|
15169
|
+
this.#sleepDirectory = join21(this.#workspace, ".flavor", "sleep");
|
|
14978
15170
|
this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
|
|
14979
15171
|
this.#generate = options.generate;
|
|
14980
15172
|
}
|
|
@@ -14983,7 +15175,7 @@ var ProjectSleepOrganizer = class {
|
|
|
14983
15175
|
signal.throwIfAborted();
|
|
14984
15176
|
await this.#prepareDirectory();
|
|
14985
15177
|
if (await this.#hasReport(date)) return { status: "exists", date };
|
|
14986
|
-
const lockPath =
|
|
15178
|
+
const lockPath = join21(this.#sleepDirectory, `.${date}.lock`);
|
|
14987
15179
|
let lock;
|
|
14988
15180
|
try {
|
|
14989
15181
|
lock = await open9(lockPath, constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY, 384);
|
|
@@ -15018,7 +15210,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
15018
15210
|
}
|
|
15019
15211
|
if (review === void 0) throw lastError;
|
|
15020
15212
|
const filename2 = `${date}-${filenameSummary(review.title)}.md`;
|
|
15021
|
-
const path =
|
|
15213
|
+
const path = join21(this.#sleepDirectory, filename2);
|
|
15022
15214
|
const markdown = renderSleepReport(date, review, documents);
|
|
15023
15215
|
await this.#atomicWrite(path, markdown);
|
|
15024
15216
|
return { status: "written", date, path, sessionCount: documents.length };
|
|
@@ -15060,7 +15252,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
15060
15252
|
});
|
|
15061
15253
|
}
|
|
15062
15254
|
async #prepareDirectory() {
|
|
15063
|
-
const flavorDirectory =
|
|
15255
|
+
const flavorDirectory = join21(this.#workspace, ".flavor");
|
|
15064
15256
|
await assertNotSymlink(flavorDirectory);
|
|
15065
15257
|
await mkdir15(flavorDirectory, { recursive: true, mode: 448 });
|
|
15066
15258
|
await assertNotSymlink(this.#sleepDirectory);
|
|
@@ -15074,7 +15266,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
15074
15266
|
return (await readdir7(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
|
|
15075
15267
|
}
|
|
15076
15268
|
async #atomicWrite(path, content) {
|
|
15077
|
-
const temporary =
|
|
15269
|
+
const temporary = join21(this.#sleepDirectory, `.${randomUUID14()}.tmp`);
|
|
15078
15270
|
let handle;
|
|
15079
15271
|
try {
|
|
15080
15272
|
handle = await open9(temporary, constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY, 384);
|
|
@@ -15450,7 +15642,7 @@ function computeSessionStats(documents) {
|
|
|
15450
15642
|
import { constants as constants7 } from "fs";
|
|
15451
15643
|
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";
|
|
15452
15644
|
import { dirname as dirname16, isAbsolute as isAbsolute9, relative as relative15, resolve as resolve20, sep as sep15 } from "path";
|
|
15453
|
-
import { randomUUID as
|
|
15645
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
15454
15646
|
import { z as z24 } from "zod";
|
|
15455
15647
|
|
|
15456
15648
|
// src/tools/file-diff.ts
|
|
@@ -15803,7 +15995,7 @@ function createPathGuard(workspace) {
|
|
|
15803
15995
|
async function atomicWrite(path, content, signal, expectedVersion) {
|
|
15804
15996
|
abortIfNeeded(signal);
|
|
15805
15997
|
await mkdir16(dirname16(path), { recursive: true });
|
|
15806
|
-
const temporary = `${path}.${
|
|
15998
|
+
const temporary = `${path}.${randomUUID15()}.tmp`;
|
|
15807
15999
|
try {
|
|
15808
16000
|
const handle = await open10(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
15809
16001
|
try {
|
|
@@ -17200,18 +17392,40 @@ function hookAnswersFromUpdatedInput(updatedInput, questions) {
|
|
|
17200
17392
|
var QuestionBridge = class {
|
|
17201
17393
|
#pending;
|
|
17202
17394
|
#onChange;
|
|
17203
|
-
|
|
17395
|
+
#relay;
|
|
17396
|
+
#active = false;
|
|
17397
|
+
constructor(onChange, relay) {
|
|
17204
17398
|
this.#onChange = onChange;
|
|
17399
|
+
this.#relay = relay;
|
|
17205
17400
|
}
|
|
17206
17401
|
get pending() {
|
|
17207
17402
|
return this.#pending?.questions;
|
|
17208
17403
|
}
|
|
17209
17404
|
ask(questions, signal) {
|
|
17210
|
-
if (this.#
|
|
17405
|
+
if (this.#active) return Promise.reject(new Error("A question is already pending"));
|
|
17211
17406
|
if (signal.aborted) return Promise.reject(signal.reason);
|
|
17407
|
+
this.#active = true;
|
|
17408
|
+
if (this.#relay !== void 0) return this.#askRelayed(questions, signal);
|
|
17409
|
+
return this.#askLocally(questions, signal);
|
|
17410
|
+
}
|
|
17411
|
+
async #askRelayed(questions, signal) {
|
|
17412
|
+
try {
|
|
17413
|
+
const answers = await this.#relay(questions, signal);
|
|
17414
|
+
if (answers !== void 0) {
|
|
17415
|
+
this.#active = false;
|
|
17416
|
+
return answers;
|
|
17417
|
+
}
|
|
17418
|
+
return await this.#askLocally(questions, signal);
|
|
17419
|
+
} catch (error) {
|
|
17420
|
+
this.#active = false;
|
|
17421
|
+
throw error;
|
|
17422
|
+
}
|
|
17423
|
+
}
|
|
17424
|
+
#askLocally(questions, signal) {
|
|
17212
17425
|
return new Promise((resolve28, reject) => {
|
|
17213
17426
|
const onAbort = () => {
|
|
17214
17427
|
this.#pending = void 0;
|
|
17428
|
+
this.#active = false;
|
|
17215
17429
|
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
|
|
17216
17430
|
};
|
|
17217
17431
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -17220,12 +17434,14 @@ var QuestionBridge = class {
|
|
|
17220
17434
|
resolve: (answers) => {
|
|
17221
17435
|
signal.removeEventListener("abort", onAbort);
|
|
17222
17436
|
this.#pending = void 0;
|
|
17437
|
+
this.#active = false;
|
|
17223
17438
|
this.#onChange?.();
|
|
17224
17439
|
resolve28(answers);
|
|
17225
17440
|
},
|
|
17226
17441
|
reject: (error) => {
|
|
17227
17442
|
signal.removeEventListener("abort", onAbort);
|
|
17228
17443
|
this.#pending = void 0;
|
|
17444
|
+
this.#active = false;
|
|
17229
17445
|
this.#onChange?.();
|
|
17230
17446
|
reject(error);
|
|
17231
17447
|
}
|
|
@@ -17311,9 +17527,9 @@ function createTodoWriteTool() {
|
|
|
17311
17527
|
}
|
|
17312
17528
|
|
|
17313
17529
|
// src/tools/managed.ts
|
|
17314
|
-
import { randomUUID as
|
|
17315
|
-
import { link as link2, lstat as lstat11, mkdir as mkdir17, readFile as readFile19, readdir as readdir8, unlink as
|
|
17316
|
-
import { dirname as dirname18, join as
|
|
17530
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
17531
|
+
import { link as link2, lstat as lstat11, mkdir as mkdir17, readFile as readFile19, readdir as readdir8, unlink as unlink5, writeFile as writeFile10 } from "fs/promises";
|
|
17532
|
+
import { dirname as dirname18, join as join22, resolve as resolve22 } from "path";
|
|
17317
17533
|
import { z as z31 } from "zod";
|
|
17318
17534
|
var MANAGED_TOOL_VERSION = 1;
|
|
17319
17535
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -17356,8 +17572,8 @@ var ManagedToolStore = class {
|
|
|
17356
17572
|
#diagnostics = [];
|
|
17357
17573
|
constructor(options) {
|
|
17358
17574
|
this.#workspace = resolve22(options.workspace);
|
|
17359
|
-
this.#projectRoot =
|
|
17360
|
-
this.#globalRoot =
|
|
17575
|
+
this.#projectRoot = join22(this.#workspace, ".flavor", "tools");
|
|
17576
|
+
this.#globalRoot = join22(resolve22(options.home), ".flavor-code", "tools");
|
|
17361
17577
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
17362
17578
|
}
|
|
17363
17579
|
get diagnostics() {
|
|
@@ -17404,7 +17620,7 @@ var ManagedToolStore = class {
|
|
|
17404
17620
|
version: MANAGED_TOOL_VERSION,
|
|
17405
17621
|
createdAt: this.#now()
|
|
17406
17622
|
});
|
|
17407
|
-
const path =
|
|
17623
|
+
const path = join22(this.directory(input.scope), filename(input.name));
|
|
17408
17624
|
const definition = compileDefinition(record, input.scope, this.#workspace);
|
|
17409
17625
|
await writeExclusive(path, `${JSON.stringify(record, void 0, 2)}
|
|
17410
17626
|
`);
|
|
@@ -17420,7 +17636,7 @@ var ManagedToolStore = class {
|
|
|
17420
17636
|
const entry2 = matches[0];
|
|
17421
17637
|
const info = await lstat11(entry2.path);
|
|
17422
17638
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error(`Managed tool path is no longer a regular file: ${entry2.path}`);
|
|
17423
|
-
await
|
|
17639
|
+
await unlink5(entry2.path);
|
|
17424
17640
|
this.#entries.delete(scopedKey(entry2.scope, entry2.record.name));
|
|
17425
17641
|
return summary(entry2, false);
|
|
17426
17642
|
}
|
|
@@ -17429,7 +17645,7 @@ var ManagedToolStore = class {
|
|
|
17429
17645
|
if (!parsed.success) return [];
|
|
17430
17646
|
const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
|
|
17431
17647
|
if (matches.length > 0) return matches.map((entry2) => entry2.path);
|
|
17432
|
-
return [
|
|
17648
|
+
return [join22(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
17433
17649
|
}
|
|
17434
17650
|
async #loadDirectory(scope2) {
|
|
17435
17651
|
const root = this.directory(scope2);
|
|
@@ -17442,7 +17658,7 @@ var ManagedToolStore = class {
|
|
|
17442
17658
|
return;
|
|
17443
17659
|
}
|
|
17444
17660
|
for (const name of names) {
|
|
17445
|
-
const path =
|
|
17661
|
+
const path = join22(root, name);
|
|
17446
17662
|
try {
|
|
17447
17663
|
const info = await lstat11(path);
|
|
17448
17664
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
@@ -17598,13 +17814,13 @@ ${sourceUrl}`
|
|
|
17598
17814
|
}
|
|
17599
17815
|
async function writeExclusive(path, content) {
|
|
17600
17816
|
await mkdir17(dirname18(path), { recursive: true });
|
|
17601
|
-
const temporary = `${path}.${
|
|
17817
|
+
const temporary = `${path}.${randomUUID16()}.tmp`;
|
|
17602
17818
|
try {
|
|
17603
17819
|
await writeFile10(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
17604
17820
|
await link2(temporary, path);
|
|
17605
17821
|
} finally {
|
|
17606
17822
|
try {
|
|
17607
|
-
await
|
|
17823
|
+
await unlink5(temporary);
|
|
17608
17824
|
} catch {
|
|
17609
17825
|
}
|
|
17610
17826
|
}
|
|
@@ -17988,7 +18204,7 @@ function buildWarningMessages(report, languageTag) {
|
|
|
17988
18204
|
}
|
|
17989
18205
|
|
|
17990
18206
|
// src/hallucination/sliding-window.ts
|
|
17991
|
-
import { createHash as
|
|
18207
|
+
import { createHash as createHash15 } from "crypto";
|
|
17992
18208
|
var SlidingWindow = class {
|
|
17993
18209
|
#windowSize;
|
|
17994
18210
|
#threshold;
|
|
@@ -18004,7 +18220,7 @@ var SlidingWindow = class {
|
|
|
18004
18220
|
}
|
|
18005
18221
|
hash(toolName, params) {
|
|
18006
18222
|
const sorted = JSON.stringify(params, sortedKeys(params));
|
|
18007
|
-
return
|
|
18223
|
+
return createHash15("sha256").update(`${toolName}:${sorted}`).digest("hex");
|
|
18008
18224
|
}
|
|
18009
18225
|
push(toolName, params) {
|
|
18010
18226
|
const hash = this.hash(toolName, params);
|
|
@@ -18359,9 +18575,9 @@ ${total}`;
|
|
|
18359
18575
|
}
|
|
18360
18576
|
|
|
18361
18577
|
// src/memory/store.ts
|
|
18362
|
-
import { createHash as
|
|
18578
|
+
import { createHash as createHash16 } from "crypto";
|
|
18363
18579
|
import { readFile as readFile20, rm as rm9 } from "fs/promises";
|
|
18364
|
-
import { resolve as resolve23, join as
|
|
18580
|
+
import { resolve as resolve23, join as join23, relative as relative17, sep as sep17 } from "path";
|
|
18365
18581
|
|
|
18366
18582
|
// src/memory/similarity.ts
|
|
18367
18583
|
function normalizeForSimilarity(value) {
|
|
@@ -18464,9 +18680,9 @@ var MemoryStore = class {
|
|
|
18464
18680
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
18465
18681
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
18466
18682
|
this.workspace = resolve23(options.workspace);
|
|
18467
|
-
this.#memoryRoot =
|
|
18468
|
-
this.path =
|
|
18469
|
-
this.#behaviorPath =
|
|
18683
|
+
this.#memoryRoot = join23(this.workspace, ".flavor", "memory");
|
|
18684
|
+
this.path = join23(this.#memoryRoot, "MEMORY.md");
|
|
18685
|
+
this.#behaviorPath = join23(this.#memoryRoot, "behavior.json");
|
|
18470
18686
|
this.#maxEntries = options.maxEntries;
|
|
18471
18687
|
this.#maxEntryChars = options.maxEntryChars;
|
|
18472
18688
|
}
|
|
@@ -18845,7 +19061,7 @@ function sanitizeTopicKey(value) {
|
|
|
18845
19061
|
return value.normalize("NFKC").toLocaleLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 128);
|
|
18846
19062
|
}
|
|
18847
19063
|
function memoryId(type, content) {
|
|
18848
|
-
return
|
|
19064
|
+
return createHash16("sha256").update(`${type}\0${normalizeMemoryContent(content).toLocaleLowerCase()}`, "utf8").digest("hex").slice(0, 12);
|
|
18849
19065
|
}
|
|
18850
19066
|
function assertTaskId(taskId) {
|
|
18851
19067
|
if (!TASK_ID.test(taskId)) throw new Error(`Invalid memory task id: ${taskId}`);
|
|
@@ -19161,7 +19377,7 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
19161
19377
|
|
|
19162
19378
|
// src/ide/client.ts
|
|
19163
19379
|
import { readFile as readFile21, readdir as readdir9, stat as stat8 } from "fs/promises";
|
|
19164
|
-
import { basename as basename5, join as
|
|
19380
|
+
import { basename as basename5, join as join24, relative as relative18, resolve as resolve24, sep as sep18 } from "path";
|
|
19165
19381
|
var FlavorIdeClient = class {
|
|
19166
19382
|
#workspace;
|
|
19167
19383
|
#home;
|
|
@@ -19257,7 +19473,7 @@ var FlavorIdeClient = class {
|
|
|
19257
19473
|
};
|
|
19258
19474
|
if (await this.#healthy(configured)) return configured;
|
|
19259
19475
|
}
|
|
19260
|
-
const directory =
|
|
19476
|
+
const directory = join24(this.#home, ".flavor-code", "ide");
|
|
19261
19477
|
let entries;
|
|
19262
19478
|
try {
|
|
19263
19479
|
entries = (await readdir9(directory)).filter((name) => name.endsWith(".lock"));
|
|
@@ -19265,7 +19481,7 @@ var FlavorIdeClient = class {
|
|
|
19265
19481
|
return void 0;
|
|
19266
19482
|
}
|
|
19267
19483
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
19268
|
-
const path =
|
|
19484
|
+
const path = join24(directory, name);
|
|
19269
19485
|
try {
|
|
19270
19486
|
const [contents, info] = await Promise.all([readFile21(path, "utf8"), stat8(path)]);
|
|
19271
19487
|
const lock = parseLockfile(contents);
|
|
@@ -19385,7 +19601,7 @@ function relativeDisplay(workspace, filePath) {
|
|
|
19385
19601
|
}
|
|
19386
19602
|
|
|
19387
19603
|
// src/jobs/registry.ts
|
|
19388
|
-
import { randomUUID as
|
|
19604
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
19389
19605
|
var JobRegistry = class {
|
|
19390
19606
|
#jobs = /* @__PURE__ */ new Map();
|
|
19391
19607
|
#maxOutputChars;
|
|
@@ -19399,7 +19615,7 @@ var JobRegistry = class {
|
|
|
19399
19615
|
}
|
|
19400
19616
|
create(input) {
|
|
19401
19617
|
this.#pruneCompleted();
|
|
19402
|
-
const id = `job-${
|
|
19618
|
+
const id = `job-${randomUUID17().slice(0, 12)}`;
|
|
19403
19619
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19404
19620
|
let settle;
|
|
19405
19621
|
const done = new Promise((resolve28) => {
|
|
@@ -19525,7 +19741,7 @@ function snapshot2(job) {
|
|
|
19525
19741
|
}
|
|
19526
19742
|
|
|
19527
19743
|
// src/terminal/service.ts
|
|
19528
|
-
import { randomUUID as
|
|
19744
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
19529
19745
|
import { createRequire as nodeCreateRequire } from "module";
|
|
19530
19746
|
import { isAbsolute as isAbsolute11, relative as relative19, resolve as resolve25, sep as sep19 } from "path";
|
|
19531
19747
|
var TerminalService = class {
|
|
@@ -19543,7 +19759,7 @@ var TerminalService = class {
|
|
|
19543
19759
|
open(input) {
|
|
19544
19760
|
const cwd = workspacePath(this.#root, input.cwd ?? ".");
|
|
19545
19761
|
const shell = input.shell ?? defaultShell();
|
|
19546
|
-
const id = `term-${
|
|
19762
|
+
const id = `term-${randomUUID18().slice(0, 12)}`;
|
|
19547
19763
|
const pty = this.#factory(shell, input.args ?? [], {
|
|
19548
19764
|
cwd,
|
|
19549
19765
|
cols: input.columns ?? 100,
|
|
@@ -19753,10 +19969,10 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
19753
19969
|
}
|
|
19754
19970
|
|
|
19755
19971
|
// src/pals/address.ts
|
|
19756
|
-
import { createHash as
|
|
19972
|
+
import { createHash as createHash17 } from "crypto";
|
|
19757
19973
|
function opaqueUserScope(userScope) {
|
|
19758
19974
|
if (userScope.length === 0) throw new Error("User scope must not be empty");
|
|
19759
|
-
const digest =
|
|
19975
|
+
const digest = createHash17("sha256").update(userScope, "utf8").digest("hex").slice(0, 16);
|
|
19760
19976
|
return `u-${digest}`;
|
|
19761
19977
|
}
|
|
19762
19978
|
function palSocketAddress(options) {
|
|
@@ -19769,7 +19985,7 @@ function palSocketAddress(options) {
|
|
|
19769
19985
|
}
|
|
19770
19986
|
|
|
19771
19987
|
// src/pals/client.ts
|
|
19772
|
-
import { randomUUID as
|
|
19988
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
19773
19989
|
import { Socket as Socket3 } from "net";
|
|
19774
19990
|
var DEFAULT_REQUEST_TIMEOUT_MS = 5e3;
|
|
19775
19991
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -19845,7 +20061,7 @@ var PalClient = class {
|
|
|
19845
20061
|
return this.starting;
|
|
19846
20062
|
}
|
|
19847
20063
|
async list() {
|
|
19848
|
-
const data2 = await this.request({ version: 1, type: "list", requestId:
|
|
20064
|
+
const data2 = await this.request({ version: 1, type: "list", requestId: randomUUID19() });
|
|
19849
20065
|
return PalPresenceListSchema.parse(data2);
|
|
19850
20066
|
}
|
|
19851
20067
|
async rename(alias) {
|
|
@@ -19858,38 +20074,38 @@ var PalClient = class {
|
|
|
19858
20074
|
throw error;
|
|
19859
20075
|
}
|
|
19860
20076
|
}
|
|
19861
|
-
async sendTask(target, message2, messageId =
|
|
19862
|
-
const data2 = await this.request({ version: 1, type: "task", requestId:
|
|
20077
|
+
async sendTask(target, message2, messageId = randomUUID19()) {
|
|
20078
|
+
const data2 = await this.request({ version: 1, type: "task", requestId: randomUUID19(), messageId, target, goal: message2 });
|
|
19863
20079
|
return DeliveryReceiptSchema.parse(data2);
|
|
19864
20080
|
}
|
|
19865
|
-
async sendChat(target, message2, messageId =
|
|
19866
|
-
const data2 = await this.request({ version: 1, type: "chat", requestId:
|
|
20081
|
+
async sendChat(target, message2, messageId = randomUUID19()) {
|
|
20082
|
+
const data2 = await this.request({ version: 1, type: "chat", requestId: randomUUID19(), messageId, target, message: message2 });
|
|
19867
20083
|
return DeliveryReceiptSchema.parse(data2);
|
|
19868
20084
|
}
|
|
19869
20085
|
async startCoWork(input) {
|
|
19870
20086
|
const data2 = await this.request({
|
|
19871
20087
|
version: 1,
|
|
19872
20088
|
type: "cowork-propose",
|
|
19873
|
-
requestId:
|
|
19874
|
-
coWorkId: input.coWorkId ??
|
|
20089
|
+
requestId: randomUUID19(),
|
|
20090
|
+
coWorkId: input.coWorkId ?? randomUUID19(),
|
|
19875
20091
|
goal: input.goal,
|
|
19876
20092
|
participants: input.participants
|
|
19877
20093
|
});
|
|
19878
20094
|
return CoWorkSnapshotSchema.parse(data2);
|
|
19879
20095
|
}
|
|
19880
20096
|
async coWorkAction(action) {
|
|
19881
|
-
const request = { version: 1, requestId:
|
|
20097
|
+
const request = { version: 1, requestId: randomUUID19(), ...action };
|
|
19882
20098
|
return CoWorkSnapshotSchema.parse(await this.request(request));
|
|
19883
20099
|
}
|
|
19884
20100
|
async coWorkStatus(coWorkId) {
|
|
19885
|
-
const data2 = await this.request({ version: 1, type: "cowork-get", requestId:
|
|
20101
|
+
const data2 = await this.request({ version: 1, type: "cowork-get", requestId: randomUUID19(), coWorkId });
|
|
19886
20102
|
return CoWorkSnapshotSchema.parse(data2);
|
|
19887
20103
|
}
|
|
19888
20104
|
async integrateCoWork(input) {
|
|
19889
20105
|
return this.coWorkAction({ type: "cowork-integration", ...input });
|
|
19890
20106
|
}
|
|
19891
20107
|
async cancelCoWork(coWorkId, reason) {
|
|
19892
|
-
const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId:
|
|
20108
|
+
const data2 = await this.request({ version: 1, type: "cowork-cancel", requestId: randomUUID19(), coWorkId, reason });
|
|
19893
20109
|
return CoWorkSnapshotSchema.parse(data2);
|
|
19894
20110
|
}
|
|
19895
20111
|
subscribe(listener) {
|
|
@@ -19901,7 +20117,7 @@ var PalClient = class {
|
|
|
19901
20117
|
if (this.registered && this.socket !== void 0) {
|
|
19902
20118
|
let disconnectTimer;
|
|
19903
20119
|
await Promise.race([
|
|
19904
|
-
this.request({ version: 1, type: "disconnect", requestId:
|
|
20120
|
+
this.request({ version: 1, type: "disconnect", requestId: randomUUID19() }).catch(() => void 0),
|
|
19905
20121
|
new Promise((resolve28) => {
|
|
19906
20122
|
disconnectTimer = setTimeout(resolve28, Math.min(this.requestTimeoutMs, 250));
|
|
19907
20123
|
})
|
|
@@ -19992,7 +20208,7 @@ var PalClient = class {
|
|
|
19992
20208
|
const data2 = await this.request({
|
|
19993
20209
|
version: 1,
|
|
19994
20210
|
type: "register",
|
|
19995
|
-
requestId:
|
|
20211
|
+
requestId: randomUUID19(),
|
|
19996
20212
|
authToken: this.authToken,
|
|
19997
20213
|
presence: { version: 1, ...this.registration, connectedAt: now, lastSeenAt: now }
|
|
19998
20214
|
});
|
|
@@ -20104,7 +20320,7 @@ var PalClient = class {
|
|
|
20104
20320
|
if (!this.registered) return;
|
|
20105
20321
|
if (this.heartbeatPending) return;
|
|
20106
20322
|
this.heartbeatPending = true;
|
|
20107
|
-
void this.request({ version: 1, type: "heartbeat", requestId:
|
|
20323
|
+
void this.request({ version: 1, type: "heartbeat", requestId: randomUUID19() }).catch(() => void 0).finally(() => {
|
|
20108
20324
|
this.heartbeatPending = false;
|
|
20109
20325
|
});
|
|
20110
20326
|
}, this.heartbeatIntervalMs);
|
|
@@ -20391,7 +20607,7 @@ var ApprovalBridge = class {
|
|
|
20391
20607
|
request(request, signal = new AbortController().signal) {
|
|
20392
20608
|
if (this.#settle !== void 0) return Promise.resolve("deny");
|
|
20393
20609
|
if (signal.aborted) return Promise.resolve("deny");
|
|
20394
|
-
this.#pending = { id:
|
|
20610
|
+
this.#pending = { id: randomUUID20(), ...request };
|
|
20395
20611
|
this.#onChange?.();
|
|
20396
20612
|
return new Promise((resolvePromise) => {
|
|
20397
20613
|
this.#settle = resolvePromise;
|
|
@@ -20510,7 +20726,20 @@ async function createProductionRuntime(options) {
|
|
|
20510
20726
|
environment.OPENAI_API_KEY,
|
|
20511
20727
|
environment.ANTHROPIC_API_KEY
|
|
20512
20728
|
].filter((value) => typeof value === "string" && value.length > 0);
|
|
20513
|
-
|
|
20729
|
+
let hookSessionId;
|
|
20730
|
+
let islandControlMetadata;
|
|
20731
|
+
const hooks = new HookBus({
|
|
20732
|
+
eventContext: () => ({
|
|
20733
|
+
protocolVersion: 2,
|
|
20734
|
+
workspace,
|
|
20735
|
+
...hookSessionId === void 0 ? {} : { sessionId: hookSessionId },
|
|
20736
|
+
...islandControlMetadata === void 0 ? {} : {
|
|
20737
|
+
islandControlEndpoint: islandControlMetadata.endpoint,
|
|
20738
|
+
islandControlToken: islandControlMetadata.token,
|
|
20739
|
+
islandControlCapabilities: islandControlMetadata.capabilities
|
|
20740
|
+
}
|
|
20741
|
+
})
|
|
20742
|
+
});
|
|
20514
20743
|
hooks.on(
|
|
20515
20744
|
"PostToolUseFailure",
|
|
20516
20745
|
createIncidentReporter({
|
|
@@ -20530,8 +20759,7 @@ async function createProductionRuntime(options) {
|
|
|
20530
20759
|
diagnostics.push(...permissionPolicy.diagnostics);
|
|
20531
20760
|
const approvals = new ApprovalBridge(options.onApprovalChange);
|
|
20532
20761
|
const resolveToolApproval = options.approvalPolicy === "deny" && options.rpcToolApprovals !== true ? () => "deny" : (request, signal) => approvals.request(request, signal);
|
|
20533
|
-
const
|
|
20534
|
-
const askUserQuestionHandler = async (qs, signal) => {
|
|
20762
|
+
const relayQuestions = async (qs, signal) => {
|
|
20535
20763
|
if (options.approvalPolicy === "deny") throw new Error("AskUserQuestion is not available in non-interactive mode");
|
|
20536
20764
|
if (hooks.hasListeners("PermissionRequest")) {
|
|
20537
20765
|
let decision;
|
|
@@ -20549,8 +20777,10 @@ async function createProductionRuntime(options) {
|
|
|
20549
20777
|
const hookAnswers = hookAnswersFromUpdatedInput(decision.updatedInput, qs);
|
|
20550
20778
|
if (hookAnswers !== void 0) return hookAnswers;
|
|
20551
20779
|
}
|
|
20552
|
-
return
|
|
20780
|
+
return void 0;
|
|
20553
20781
|
};
|
|
20782
|
+
const questions = new QuestionBridge(options.onApprovalChange, relayQuestions);
|
|
20783
|
+
const askUserQuestionHandler = (qs, signal) => questions.ask(qs, signal);
|
|
20554
20784
|
const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
|
|
20555
20785
|
const jobs = new JobRegistry();
|
|
20556
20786
|
const terminals = new TerminalService(workspace, { jobs });
|
|
@@ -20622,8 +20852,8 @@ async function createProductionRuntime(options) {
|
|
|
20622
20852
|
const registeredProviders = registration.registered;
|
|
20623
20853
|
let effectiveLlm = registration.effectiveLlm;
|
|
20624
20854
|
const pluginHost = new PluginHost({
|
|
20625
|
-
globalPluginDirs: [
|
|
20626
|
-
projectPluginDirs: [
|
|
20855
|
+
globalPluginDirs: [join25(home, ".flavor-code", "plugins")],
|
|
20856
|
+
projectPluginDirs: [join25(workspace, ".flavor", "plugins")],
|
|
20627
20857
|
config,
|
|
20628
20858
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
20629
20859
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -20697,6 +20927,7 @@ async function createProductionRuntime(options) {
|
|
|
20697
20927
|
const pendingCollaborationEvents = [];
|
|
20698
20928
|
let collaborationEventPump;
|
|
20699
20929
|
let collaborationEventsOpen = true;
|
|
20930
|
+
let islandControlServer;
|
|
20700
20931
|
const pumpCollaborationEvents = () => {
|
|
20701
20932
|
if (collaborationEventPump !== void 0 || collaborationSession === void 0 || deliverCollaborationEvent === void 0) return;
|
|
20702
20933
|
collaborationEventPump = (async () => {
|
|
@@ -20730,7 +20961,7 @@ async function createProductionRuntime(options) {
|
|
|
20730
20961
|
const address = palSocketAddress({
|
|
20731
20962
|
platform: process.platform,
|
|
20732
20963
|
userScope: home,
|
|
20733
|
-
runtimeDir:
|
|
20964
|
+
runtimeDir: join25(tmpdir4(), `flavor-code-pals-${createHash18("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
20734
20965
|
});
|
|
20735
20966
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
20736
20967
|
instanceId: collaboration.instanceId,
|
|
@@ -20841,8 +21072,8 @@ async function createProductionRuntime(options) {
|
|
|
20841
21072
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
20842
21073
|
});
|
|
20843
21074
|
const skills = new SkillRegistry({
|
|
20844
|
-
globalRoots: [
|
|
20845
|
-
projectRoots: [
|
|
21075
|
+
globalRoots: [join25(home, ".flavor-code", "skills")],
|
|
21076
|
+
projectRoots: [join25(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
20846
21077
|
authorizeResource: async () => true,
|
|
20847
21078
|
disabledNames: config.skills.disabled
|
|
20848
21079
|
});
|
|
@@ -20850,7 +21081,7 @@ async function createProductionRuntime(options) {
|
|
|
20850
21081
|
skillsReady.catch(() => void 0);
|
|
20851
21082
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
20852
21083
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
20853
|
-
const flavor = await optionalText2(
|
|
21084
|
+
const flavor = await optionalText2(join25(workspace, "FLAVOR.md"));
|
|
20854
21085
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
20855
21086
|
let memoryContext;
|
|
20856
21087
|
if (memoryStore !== void 0) {
|
|
@@ -20874,7 +21105,8 @@ async function createProductionRuntime(options) {
|
|
|
20874
21105
|
let taskResults = { ...recovered?.tasks.results ?? {} };
|
|
20875
21106
|
const subagentStartedAt = {};
|
|
20876
21107
|
const subagentElapsedMs = {};
|
|
20877
|
-
let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
21108
|
+
let sessionId = recovered?.sessionId ?? `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID20().slice(0, 8)}`;
|
|
21109
|
+
hookSessionId = sessionId;
|
|
20878
21110
|
let harnessJournal = new HarnessJournal({ workspace, sessionId });
|
|
20879
21111
|
const harnessRecovery = harnessJournal.recover();
|
|
20880
21112
|
if (harnessRecovery.queue.length > 0 || harnessRecovery.incompleteTools.length > 0 || harnessRecovery.incompleteModelIds.length > 0 || harnessRecovery.interruptedTurnIds.length > 0) {
|
|
@@ -20958,7 +21190,13 @@ async function createProductionRuntime(options) {
|
|
|
20958
21190
|
const publishTaskState = async () => {
|
|
20959
21191
|
harness.main.context.updateTaskState(serializedTaskState());
|
|
20960
21192
|
await persist();
|
|
20961
|
-
|
|
21193
|
+
const snapshot4 = taskSnapshot();
|
|
21194
|
+
emitOutput({ type: "tasks", snapshot: snapshot4 });
|
|
21195
|
+
await hooks.emit({
|
|
21196
|
+
version: 1,
|
|
21197
|
+
type: "Notification",
|
|
21198
|
+
payload: { kind: "task_snapshot", taskSnapshot: snapshot4 }
|
|
21199
|
+
}).catch(() => void 0);
|
|
20962
21200
|
};
|
|
20963
21201
|
for (const tool of createTaskPlanTools({
|
|
20964
21202
|
getPlan: () => taskPlan,
|
|
@@ -21377,7 +21615,7 @@ async function createProductionRuntime(options) {
|
|
|
21377
21615
|
evolveService.toolDefinition(),
|
|
21378
21616
|
...mcpTools
|
|
21379
21617
|
];
|
|
21380
|
-
const loopFlavor = await optionalText2(
|
|
21618
|
+
const loopFlavor = await optionalText2(join25(input.workspace, "FLAVOR.md"));
|
|
21381
21619
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
21382
21620
|
const loopEnvironment = createPromptEnvironment({
|
|
21383
21621
|
now: /* @__PURE__ */ new Date(),
|
|
@@ -21516,7 +21754,7 @@ async function createProductionRuntime(options) {
|
|
|
21516
21754
|
return answers[0] === "Continue" ? "approved" : "rejected";
|
|
21517
21755
|
},
|
|
21518
21756
|
fingerprint: workspaceFingerprint,
|
|
21519
|
-
idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
21757
|
+
idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID20().slice(0, 8)}`
|
|
21520
21758
|
});
|
|
21521
21759
|
const goalStore = new GoalStore({ workspace });
|
|
21522
21760
|
const goalOrchestrator = new GoalOrchestrator({
|
|
@@ -21535,7 +21773,7 @@ async function createProductionRuntime(options) {
|
|
|
21535
21773
|
executionEnvironment
|
|
21536
21774
|
),
|
|
21537
21775
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
21538
|
-
idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
21776
|
+
idFactory: () => `goal-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID20().slice(0, 8)}`,
|
|
21539
21777
|
runWorker: ({ workspace: goalWorkspace, prompt, signal }) => runLoopWorker({ workspace: goalWorkspace, prompt, signal })
|
|
21540
21778
|
});
|
|
21541
21779
|
let sessionHistory = await SessionHistory.open({
|
|
@@ -21648,7 +21886,7 @@ async function createProductionRuntime(options) {
|
|
|
21648
21886
|
runExplicitSkill(harness, skills, skill, prompt, signal, selectedModels.mainError),
|
|
21649
21887
|
persist
|
|
21650
21888
|
),
|
|
21651
|
-
runLoop: (goal, signal) => runLoopSession(loopOrchestrator, goal, signal),
|
|
21889
|
+
runLoop: (goal, signal) => runLoopSession(loopOrchestrator, hooks, goal, signal),
|
|
21652
21890
|
runGoal: (goal, signal) => persistEach(runGoalSession(goalOrchestrator, goal, signal), persist),
|
|
21653
21891
|
mcp: async (command, signal) => {
|
|
21654
21892
|
signal.throwIfAborted();
|
|
@@ -21806,7 +22044,8 @@ ${summary2}`;
|
|
|
21806
22044
|
for (const key of Object.keys(subagentStartedAt)) delete subagentStartedAt[key];
|
|
21807
22045
|
for (const key of Object.keys(subagentElapsedMs)) delete subagentElapsedMs[key];
|
|
21808
22046
|
const previousIdeSessionId = ideSessionId;
|
|
21809
|
-
sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${
|
|
22047
|
+
sessionId = `session-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID20().slice(0, 8)}`;
|
|
22048
|
+
hookSessionId = sessionId;
|
|
21810
22049
|
harnessJournal = new HarnessJournal({ workspace, sessionId });
|
|
21811
22050
|
setUsageSession(sessionId);
|
|
21812
22051
|
ideSessionId = sessionId;
|
|
@@ -21906,7 +22145,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
21906
22145
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
21907
22146
|
}
|
|
21908
22147
|
try {
|
|
21909
|
-
const tokenStore = createFileTokenStore(
|
|
22148
|
+
const tokenStore = createFileTokenStore(join25(home, ".flavor-code", "auth.json"));
|
|
21910
22149
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
21911
22150
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
21912
22151
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -21949,7 +22188,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
21949
22188
|
}
|
|
21950
22189
|
},
|
|
21951
22190
|
async logout() {
|
|
21952
|
-
const tokenStore = createFileTokenStore(
|
|
22191
|
+
const tokenStore = createFileTokenStore(join25(home, ".flavor-code", "auth.json"));
|
|
21953
22192
|
const tokens = await tokenStore.load();
|
|
21954
22193
|
const hadCredentials = Object.keys(tokens).length > 0 || effectiveLlm !== void 0;
|
|
21955
22194
|
if (!hadCredentials) {
|
|
@@ -22006,6 +22245,22 @@ ${renderMemoryDocument(entries)}`;
|
|
|
22006
22245
|
sleepScheduler.start();
|
|
22007
22246
|
}
|
|
22008
22247
|
const session = new FlavorSession(services);
|
|
22248
|
+
if (pluginHost.loadedPlugins.some((plugin) => plugin.name === "flavor-island")) {
|
|
22249
|
+
try {
|
|
22250
|
+
islandControlServer = await createIslandControlServer({
|
|
22251
|
+
sessionId,
|
|
22252
|
+
session,
|
|
22253
|
+
...options.islandControl?.focus === void 0 ? {} : { focus: options.islandControl.focus }
|
|
22254
|
+
});
|
|
22255
|
+
islandControlMetadata = {
|
|
22256
|
+
endpoint: islandControlServer.endpoint,
|
|
22257
|
+
token: islandControlServer.token,
|
|
22258
|
+
capabilities: islandControlServer.capabilities
|
|
22259
|
+
};
|
|
22260
|
+
} catch (error) {
|
|
22261
|
+
diagnostics.push(`Flavor Island control channel unavailable: ${message(error)}`);
|
|
22262
|
+
}
|
|
22263
|
+
}
|
|
22009
22264
|
collaborationSession = session;
|
|
22010
22265
|
pumpCollaborationEvents();
|
|
22011
22266
|
const authorization = {
|
|
@@ -22041,6 +22296,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
22041
22296
|
if (persistTail !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "persist-tail", persistTail);
|
|
22042
22297
|
if (ideSessionId !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "ide-end-session", ide.endSession(ideSessionId));
|
|
22043
22298
|
if (executionEnvironment !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "execution-environment", executionEnvironment.dispose());
|
|
22299
|
+
if (islandControlServer !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "island-control", islandControlServer.close());
|
|
22044
22300
|
terminals.dispose();
|
|
22045
22301
|
await boundedStep(stepTimeoutMs, diagnostics, "jobs", jobs.dispose());
|
|
22046
22302
|
auditLogger.close();
|
|
@@ -22054,6 +22310,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
22054
22310
|
collaborationEventsOpen = false;
|
|
22055
22311
|
mcpDiscarded = true;
|
|
22056
22312
|
memoryReviews.dispose();
|
|
22313
|
+
await islandControlServer?.close().catch(() => void 0);
|
|
22057
22314
|
try {
|
|
22058
22315
|
const stepTimeoutMs = options.shutdownStepTimeoutMs ?? DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS;
|
|
22059
22316
|
if (ideSessionId !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "ide-end-session", ide.endSession(ideSessionId));
|
|
@@ -22105,9 +22362,10 @@ function formatMcpReconnect(server) {
|
|
|
22105
22362
|
if (server.status === "disabled") return `MCP server "${server.name}" is disabled. Enable it before reconnecting.`;
|
|
22106
22363
|
return `Failed to reconnect MCP server "${server.name}": ${server.error ?? "unknown error"}`;
|
|
22107
22364
|
}
|
|
22108
|
-
async function* runLoopSession(orchestrator, goal, signal) {
|
|
22365
|
+
async function* runLoopSession(orchestrator, hooks, goal, signal) {
|
|
22109
22366
|
let totalInputTokens = 0;
|
|
22110
22367
|
let totalOutputTokens = 0;
|
|
22368
|
+
let latestVerification;
|
|
22111
22369
|
for await (const event of orchestrator.run({ goal, signal })) {
|
|
22112
22370
|
if (event.type === "worker-event") {
|
|
22113
22371
|
if (event.event.type === "usage") {
|
|
@@ -22121,8 +22379,19 @@ async function* runLoopSession(orchestrator, goal, signal) {
|
|
|
22121
22379
|
} else if (event.event.type !== "done") yield event.event;
|
|
22122
22380
|
continue;
|
|
22123
22381
|
}
|
|
22382
|
+
if (event.type === "loop-verification") latestVerification = event.evidence;
|
|
22124
22383
|
yield loopProgressEvent(event);
|
|
22125
22384
|
if (event.type === "loop-terminal") {
|
|
22385
|
+
await hooks.emit({
|
|
22386
|
+
version: 1,
|
|
22387
|
+
type: "LoopEnd",
|
|
22388
|
+
payload: {
|
|
22389
|
+
loopId: event.loopId,
|
|
22390
|
+
outcome: event.status,
|
|
22391
|
+
reason: event.reason,
|
|
22392
|
+
verification: latestVerification ?? null
|
|
22393
|
+
}
|
|
22394
|
+
}).catch(() => void 0);
|
|
22126
22395
|
yield { type: "done", usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } };
|
|
22127
22396
|
}
|
|
22128
22397
|
}
|
|
@@ -22179,7 +22448,7 @@ function terminalProgressState(status) {
|
|
|
22179
22448
|
return "failed";
|
|
22180
22449
|
}
|
|
22181
22450
|
async function workspaceFingerprint(workspace) {
|
|
22182
|
-
const hash =
|
|
22451
|
+
const hash = createHash18("sha256");
|
|
22183
22452
|
const diff = await execFileNoThrow(
|
|
22184
22453
|
"git",
|
|
22185
22454
|
["-C", workspace, "diff", "--no-ext-diff", "--binary", "HEAD"],
|
|
@@ -22414,7 +22683,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
22414
22683
|
const configured = { ...providers };
|
|
22415
22684
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
22416
22685
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
22417
|
-
const oauthTokenStore = createFileTokenStore(
|
|
22686
|
+
const oauthTokenStore = createFileTokenStore(join25(home, ".flavor-code", "auth.json"));
|
|
22418
22687
|
const registered = [];
|
|
22419
22688
|
let effectiveLlm;
|
|
22420
22689
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -22833,10 +23102,10 @@ async function* runGoalSession(orchestrator, goal, signal) {
|
|
|
22833
23102
|
}
|
|
22834
23103
|
function memoryTranscriptHash(messages) {
|
|
22835
23104
|
const visible = messages.filter((item) => item.role === "user" || item.role === "assistant").map((item) => `${item.role}\0${modelContentText(item.content).trim()}`).join("\n");
|
|
22836
|
-
return
|
|
23105
|
+
return createHash18("sha256").update(visible, "utf8").digest("hex");
|
|
22837
23106
|
}
|
|
22838
23107
|
function createMemoryTaskId() {
|
|
22839
|
-
return `memory-${
|
|
23108
|
+
return `memory-${randomUUID20()}`;
|
|
22840
23109
|
}
|
|
22841
23110
|
|
|
22842
23111
|
export {
|