ferix-code 0.0.2-beta.13 → 0.0.2-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +11 -8
- package/dist/index.js +446 -311
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ import { Command } from "commander";
|
|
|
71
71
|
// package.json
|
|
72
72
|
var package_default = {
|
|
73
73
|
name: "ferix-code",
|
|
74
|
-
version: "0.0.2-beta.
|
|
74
|
+
version: "0.0.2-beta.15",
|
|
75
75
|
description: "Composable RALPH loops for AI coding agents - v2 with Effect",
|
|
76
76
|
type: "module",
|
|
77
77
|
bin: {
|
|
@@ -118,7 +118,7 @@ var package_default = {
|
|
|
118
118
|
|
|
119
119
|
// src/program.ts
|
|
120
120
|
init_esm_shims();
|
|
121
|
-
import { Effect as
|
|
121
|
+
import { Effect as Effect24, Stream as Stream10 } from "effect";
|
|
122
122
|
|
|
123
123
|
// src/consumers/index.ts
|
|
124
124
|
init_esm_shims();
|
|
@@ -2376,7 +2376,7 @@ ${errorText}
|
|
|
2376
2376
|
|
|
2377
2377
|
// src/layers/index.ts
|
|
2378
2378
|
init_esm_shims();
|
|
2379
|
-
import { Layer as
|
|
2379
|
+
import { Layer as Layer14 } from "effect";
|
|
2380
2380
|
|
|
2381
2381
|
// src/layers/git/file-system.ts
|
|
2382
2382
|
init_esm_shims();
|
|
@@ -2770,7 +2770,7 @@ init_esm_shims();
|
|
|
2770
2770
|
// src/domain/schemas/config.ts
|
|
2771
2771
|
init_esm_shims();
|
|
2772
2772
|
import { Schema as S } from "effect";
|
|
2773
|
-
var ProviderNameSchema = S.Literal("claude", "cursor");
|
|
2773
|
+
var ProviderNameSchema = S.Literal("claude", "cursor", "opencode");
|
|
2774
2774
|
var PhasePromptOverridesSchema = S.Struct({
|
|
2775
2775
|
breakdown: S.optional(S.String),
|
|
2776
2776
|
planning: S.optional(S.String),
|
|
@@ -3718,20 +3718,84 @@ var Mock = {
|
|
|
3718
3718
|
|
|
3719
3719
|
// src/layers/llm/providers/index.ts
|
|
3720
3720
|
init_esm_shims();
|
|
3721
|
-
import { Effect as
|
|
3721
|
+
import { Effect as Effect11 } from "effect";
|
|
3722
|
+
|
|
3723
|
+
// src/layers/llm/types.ts
|
|
3724
|
+
init_esm_shims();
|
|
3725
|
+
var PROVIDER_CONFIGS = {
|
|
3726
|
+
claude: {
|
|
3727
|
+
name: "claude",
|
|
3728
|
+
cliCommand: "claude",
|
|
3729
|
+
args: [
|
|
3730
|
+
"--permission-mode",
|
|
3731
|
+
"acceptEdits",
|
|
3732
|
+
"--output-format",
|
|
3733
|
+
"stream-json",
|
|
3734
|
+
"--verbose",
|
|
3735
|
+
"--include-partial-messages"
|
|
3736
|
+
],
|
|
3737
|
+
permissions: "acceptEdits",
|
|
3738
|
+
installUrl: "https://docs.anthropic.com/claude-code"
|
|
3739
|
+
},
|
|
3740
|
+
cursor: {
|
|
3741
|
+
name: "cursor",
|
|
3742
|
+
cliCommand: "cursor-agent",
|
|
3743
|
+
args: ["--print", "--force", "--output-format", "stream-json"],
|
|
3744
|
+
permissions: "acceptEdits",
|
|
3745
|
+
installUrl: "https://cursor.sh/agent"
|
|
3746
|
+
},
|
|
3747
|
+
opencode: {
|
|
3748
|
+
name: "opencode",
|
|
3749
|
+
cliCommand: "opencode",
|
|
3750
|
+
args: ["run", "--format", "json"],
|
|
3751
|
+
installUrl: "https://opencode.ai/docs/"
|
|
3752
|
+
}
|
|
3753
|
+
};
|
|
3722
3754
|
|
|
3723
3755
|
// src/layers/llm/providers/claude.ts
|
|
3724
3756
|
init_esm_shims();
|
|
3757
|
+
|
|
3758
|
+
// src/layers/llm/provider-factory.ts
|
|
3759
|
+
init_esm_shims();
|
|
3725
3760
|
import { spawn } from "child_process";
|
|
3726
|
-
import { Effect as
|
|
3761
|
+
import { Effect as Effect9, Layer as Layer6, Stream as Stream5 } from "effect";
|
|
3762
|
+
function createProvider(name, createStream, checkAvailable) {
|
|
3763
|
+
const config = PROVIDER_CONFIGS[name];
|
|
3764
|
+
return {
|
|
3765
|
+
name,
|
|
3766
|
+
execute: (prompt, options) => {
|
|
3767
|
+
return Stream5.unwrap(
|
|
3768
|
+
checkAvailable(name).pipe(
|
|
3769
|
+
Effect9.map(() => {
|
|
3770
|
+
const args = name === "opencode" ? [...config.args, prompt] : [...config.args, "-p", prompt];
|
|
3771
|
+
const child = spawn(config.cliCommand, args, {
|
|
3772
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
3773
|
+
cwd: options?.cwd,
|
|
3774
|
+
env: {
|
|
3775
|
+
...process.env,
|
|
3776
|
+
FORCE_COLOR: "1",
|
|
3777
|
+
...config.env
|
|
3778
|
+
}
|
|
3779
|
+
});
|
|
3780
|
+
return createStream(child);
|
|
3781
|
+
})
|
|
3782
|
+
)
|
|
3783
|
+
);
|
|
3784
|
+
}
|
|
3785
|
+
};
|
|
3786
|
+
}
|
|
3787
|
+
function createProviderLayer(provider) {
|
|
3788
|
+
return Layer6.succeed(LLM, provider);
|
|
3789
|
+
}
|
|
3727
3790
|
|
|
3728
3791
|
// src/layers/llm/stream.ts
|
|
3729
3792
|
init_esm_shims();
|
|
3730
|
-
import { createInterface } from "readline";
|
|
3731
|
-
import { Effect as Effect9, Stream as Stream5 } from "effect";
|
|
3732
3793
|
|
|
3733
3794
|
// src/layers/llm/providers/parsers/claude.ts
|
|
3734
3795
|
init_esm_shims();
|
|
3796
|
+
|
|
3797
|
+
// src/layers/llm/providers/parsers/stream-json.ts
|
|
3798
|
+
init_esm_shims();
|
|
3735
3799
|
function parseJsonLine(line) {
|
|
3736
3800
|
if (!line.startsWith("{")) {
|
|
3737
3801
|
return null;
|
|
@@ -3809,7 +3873,10 @@ function unwrapStreamEvent(json) {
|
|
|
3809
3873
|
return json;
|
|
3810
3874
|
}
|
|
3811
3875
|
|
|
3812
|
-
// src/layers/llm/stream.ts
|
|
3876
|
+
// src/layers/llm/stream-factory.ts
|
|
3877
|
+
init_esm_shims();
|
|
3878
|
+
import { createInterface } from "readline";
|
|
3879
|
+
import { Effect as Effect10, Stream as Stream6 } from "effect";
|
|
3813
3880
|
function handleToolEvent(toolInfo, toolState, emit) {
|
|
3814
3881
|
if (toolInfo.type === "start") {
|
|
3815
3882
|
toolState.currentTool = toolInfo.name;
|
|
@@ -3832,21 +3899,8 @@ function handleToolEvent(toolInfo, toolState, emit) {
|
|
|
3832
3899
|
toolState.inputChunks.length = 0;
|
|
3833
3900
|
}
|
|
3834
3901
|
}
|
|
3835
|
-
function
|
|
3836
|
-
|
|
3837
|
-
const text = extractText(event);
|
|
3838
|
-
if (text) {
|
|
3839
|
-
outputChunks.push(text);
|
|
3840
|
-
emit.single({ _tag: "Text", text });
|
|
3841
|
-
return;
|
|
3842
|
-
}
|
|
3843
|
-
const toolInfo = extractToolInfo(event);
|
|
3844
|
-
if (toolInfo) {
|
|
3845
|
-
handleToolEvent(toolInfo, toolState, emit);
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
function createEventStream(child) {
|
|
3849
|
-
return Stream5.async((emit) => {
|
|
3902
|
+
function createLLMEventStream(child, parser, providerName) {
|
|
3903
|
+
return Stream6.async((emit) => {
|
|
3850
3904
|
const outputChunks = [];
|
|
3851
3905
|
const toolState = { currentTool: "", inputChunks: [] };
|
|
3852
3906
|
const stdout = child.stdout;
|
|
@@ -3854,16 +3908,16 @@ function createEventStream(child) {
|
|
|
3854
3908
|
emit.fail(
|
|
3855
3909
|
new LLMError({ message: "Failed to get stdout from child process" })
|
|
3856
3910
|
);
|
|
3857
|
-
return
|
|
3911
|
+
return Effect10.void;
|
|
3858
3912
|
}
|
|
3859
3913
|
const rl = createInterface({
|
|
3860
3914
|
input: stdout,
|
|
3861
3915
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
3862
3916
|
});
|
|
3863
3917
|
rl.on("line", (line) => {
|
|
3864
|
-
const json = parseJsonLine(line);
|
|
3918
|
+
const json = parser.parseJsonLine(line);
|
|
3865
3919
|
if (json) {
|
|
3866
|
-
|
|
3920
|
+
parser.processEvent(json, outputChunks, toolState, emit);
|
|
3867
3921
|
}
|
|
3868
3922
|
});
|
|
3869
3923
|
child.stderr?.on("data", (data) => {
|
|
@@ -3876,7 +3930,7 @@ function createEventStream(child) {
|
|
|
3876
3930
|
if (exitCode !== 0) {
|
|
3877
3931
|
emit.fail(
|
|
3878
3932
|
new LLMError({
|
|
3879
|
-
message:
|
|
3933
|
+
message: `${providerName} CLI exited with code ${exitCode}`
|
|
3880
3934
|
})
|
|
3881
3935
|
);
|
|
3882
3936
|
} else {
|
|
@@ -3893,46 +3947,41 @@ function createEventStream(child) {
|
|
|
3893
3947
|
})
|
|
3894
3948
|
);
|
|
3895
3949
|
});
|
|
3896
|
-
return
|
|
3950
|
+
return Effect10.sync(() => {
|
|
3897
3951
|
child.kill("SIGTERM");
|
|
3898
3952
|
});
|
|
3899
3953
|
});
|
|
3900
3954
|
}
|
|
3901
3955
|
|
|
3902
|
-
// src/layers/llm/
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
[
|
|
3911
|
-
"--permission-mode",
|
|
3912
|
-
"acceptEdits",
|
|
3913
|
-
"--output-format",
|
|
3914
|
-
"stream-json",
|
|
3915
|
-
"--verbose",
|
|
3916
|
-
"--include-partial-messages",
|
|
3917
|
-
"-p",
|
|
3918
|
-
prompt
|
|
3919
|
-
],
|
|
3920
|
-
{
|
|
3921
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
3922
|
-
cwd: options?.cwd,
|
|
3923
|
-
env: {
|
|
3924
|
-
...process.env,
|
|
3925
|
-
FORCE_COLOR: "1"
|
|
3926
|
-
}
|
|
3927
|
-
}
|
|
3928
|
-
);
|
|
3929
|
-
return createEventStream(child);
|
|
3930
|
-
})
|
|
3931
|
-
);
|
|
3956
|
+
// src/layers/llm/stream.ts
|
|
3957
|
+
function processJsonLine(json, outputChunks, toolState, emit) {
|
|
3958
|
+
const event = unwrapStreamEvent(json);
|
|
3959
|
+
const text = extractText(event);
|
|
3960
|
+
if (text) {
|
|
3961
|
+
outputChunks.push(text);
|
|
3962
|
+
emit.single({ _tag: "Text", text });
|
|
3963
|
+
return;
|
|
3932
3964
|
}
|
|
3965
|
+
const toolInfo = extractToolInfo(event);
|
|
3966
|
+
if (toolInfo) {
|
|
3967
|
+
handleToolEvent(toolInfo, toolState, emit);
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
var streamJsonParser = {
|
|
3971
|
+
parseJsonLine,
|
|
3972
|
+
processEvent: processJsonLine
|
|
3933
3973
|
};
|
|
3934
|
-
|
|
3935
|
-
|
|
3974
|
+
function createEventStream(child, providerName = "LLM") {
|
|
3975
|
+
return createLLMEventStream(child, streamJsonParser, providerName);
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3978
|
+
// src/layers/llm/providers/claude.ts
|
|
3979
|
+
var ClaudeProvider = createProvider(
|
|
3980
|
+
"claude",
|
|
3981
|
+
(child) => createEventStream(child, "Claude"),
|
|
3982
|
+
checkProviderAvailable
|
|
3983
|
+
);
|
|
3984
|
+
var Live6 = createProviderLayer(ClaudeProvider);
|
|
3936
3985
|
var ClaudeCLI = {
|
|
3937
3986
|
Live: Live6,
|
|
3938
3987
|
Provider: ClaudeProvider
|
|
@@ -3940,51 +3989,161 @@ var ClaudeCLI = {
|
|
|
3940
3989
|
|
|
3941
3990
|
// src/layers/llm/providers/cursor.ts
|
|
3942
3991
|
init_esm_shims();
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
Effect11.sync(() => {
|
|
3950
|
-
const child = spawn2(
|
|
3951
|
-
"cursor-agent",
|
|
3952
|
-
[
|
|
3953
|
-
"--print",
|
|
3954
|
-
"--force",
|
|
3955
|
-
"--output-format",
|
|
3956
|
-
"stream-json",
|
|
3957
|
-
"-p",
|
|
3958
|
-
prompt
|
|
3959
|
-
],
|
|
3960
|
-
{
|
|
3961
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
3962
|
-
cwd: options?.cwd,
|
|
3963
|
-
env: {
|
|
3964
|
-
...process.env,
|
|
3965
|
-
FORCE_COLOR: "1"
|
|
3966
|
-
}
|
|
3967
|
-
}
|
|
3968
|
-
);
|
|
3969
|
-
return createEventStream(child);
|
|
3970
|
-
})
|
|
3971
|
-
);
|
|
3972
|
-
}
|
|
3973
|
-
};
|
|
3974
|
-
var make4 = CursorProvider;
|
|
3975
|
-
var Live7 = Layer7.succeed(LLM, make4);
|
|
3992
|
+
var CursorProvider = createProvider(
|
|
3993
|
+
"cursor",
|
|
3994
|
+
(child) => createEventStream(child, "Cursor"),
|
|
3995
|
+
checkProviderAvailable
|
|
3996
|
+
);
|
|
3997
|
+
var Live7 = createProviderLayer(CursorProvider);
|
|
3976
3998
|
var CursorCLI = {
|
|
3977
3999
|
Live: Live7,
|
|
3978
4000
|
Provider: CursorProvider
|
|
3979
4001
|
};
|
|
3980
4002
|
|
|
4003
|
+
// src/layers/llm/providers/opencode.ts
|
|
4004
|
+
init_esm_shims();
|
|
4005
|
+
|
|
4006
|
+
// src/layers/llm/stream-opencode.ts
|
|
4007
|
+
init_esm_shims();
|
|
4008
|
+
|
|
4009
|
+
// src/layers/llm/providers/parsers/opencode.ts
|
|
4010
|
+
init_esm_shims();
|
|
4011
|
+
function parseJsonLine2(line) {
|
|
4012
|
+
if (!line.startsWith("{")) {
|
|
4013
|
+
return null;
|
|
4014
|
+
}
|
|
4015
|
+
try {
|
|
4016
|
+
return JSON.parse(line);
|
|
4017
|
+
} catch {
|
|
4018
|
+
return null;
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
function isTextContent2(json) {
|
|
4022
|
+
return typeof json === "object" && json !== null && "type" in json && json.type === "text" && "text" in json && typeof json.text === "string";
|
|
4023
|
+
}
|
|
4024
|
+
function isStepFinish(json) {
|
|
4025
|
+
return typeof json === "object" && json !== null && "type" in json && json.type === "step_finish";
|
|
4026
|
+
}
|
|
4027
|
+
function isToolUse2(json) {
|
|
4028
|
+
if (typeof json !== "object" || json === null) {
|
|
4029
|
+
return false;
|
|
4030
|
+
}
|
|
4031
|
+
const obj = json;
|
|
4032
|
+
if (obj.type !== "tool_use") {
|
|
4033
|
+
return false;
|
|
4034
|
+
}
|
|
4035
|
+
if (typeof obj.part !== "object" || obj.part === null) {
|
|
4036
|
+
return false;
|
|
4037
|
+
}
|
|
4038
|
+
const part = obj.part;
|
|
4039
|
+
if (typeof part.tool !== "string") {
|
|
4040
|
+
return false;
|
|
4041
|
+
}
|
|
4042
|
+
if (typeof part.state !== "object" || part.state === null) {
|
|
4043
|
+
return false;
|
|
4044
|
+
}
|
|
4045
|
+
return true;
|
|
4046
|
+
}
|
|
4047
|
+
function extractText2(json) {
|
|
4048
|
+
if (isTextContent2(json)) {
|
|
4049
|
+
return json.text;
|
|
4050
|
+
}
|
|
4051
|
+
return null;
|
|
4052
|
+
}
|
|
4053
|
+
function extractToolInfo2(json) {
|
|
4054
|
+
if (isToolUse2(json)) {
|
|
4055
|
+
return {
|
|
4056
|
+
type: "complete",
|
|
4057
|
+
name: json.part.tool,
|
|
4058
|
+
partialJson: JSON.stringify(json.part.state.input)
|
|
4059
|
+
};
|
|
4060
|
+
}
|
|
4061
|
+
return null;
|
|
4062
|
+
}
|
|
4063
|
+
|
|
4064
|
+
// src/layers/llm/stream-opencode.ts
|
|
4065
|
+
function processOpenCodeEvent(json, outputChunks, _toolState, emit) {
|
|
4066
|
+
const text = extractText2(json);
|
|
4067
|
+
if (text) {
|
|
4068
|
+
outputChunks.push(text);
|
|
4069
|
+
emit.single({ _tag: "Text", text });
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
4072
|
+
const toolInfo = extractToolInfo2(json);
|
|
4073
|
+
if (toolInfo && toolInfo.type === "complete") {
|
|
4074
|
+
emit.single({ _tag: "ToolStart", tool: toolInfo.name });
|
|
4075
|
+
if (toolInfo.partialJson) {
|
|
4076
|
+
const input = safeParseJson(toolInfo.partialJson);
|
|
4077
|
+
if (input !== null) {
|
|
4078
|
+
emit.single({ _tag: "ToolUse", tool: toolInfo.name, input });
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
emit.single({ _tag: "ToolEnd", tool: toolInfo.name });
|
|
4082
|
+
return;
|
|
4083
|
+
}
|
|
4084
|
+
if (isStepFinish(json)) {
|
|
4085
|
+
return;
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
var openCodeParser = {
|
|
4089
|
+
parseJsonLine: parseJsonLine2,
|
|
4090
|
+
processEvent: processOpenCodeEvent
|
|
4091
|
+
};
|
|
4092
|
+
function createOpenCodeEventStream(child, providerName = "OpenCode") {
|
|
4093
|
+
return createLLMEventStream(child, openCodeParser, providerName);
|
|
4094
|
+
}
|
|
4095
|
+
|
|
4096
|
+
// src/layers/llm/providers/opencode.ts
|
|
4097
|
+
var OpenCodeProvider = createProvider(
|
|
4098
|
+
"opencode",
|
|
4099
|
+
(child) => createOpenCodeEventStream(child, "OpenCode"),
|
|
4100
|
+
checkProviderAvailable
|
|
4101
|
+
);
|
|
4102
|
+
var Live8 = createProviderLayer(OpenCodeProvider);
|
|
4103
|
+
var OpenCodeCLI = {
|
|
4104
|
+
Live: Live8,
|
|
4105
|
+
Provider: OpenCodeProvider
|
|
4106
|
+
};
|
|
4107
|
+
|
|
3981
4108
|
// src/layers/llm/providers/index.ts
|
|
3982
|
-
|
|
4109
|
+
var OpenCodeCLI2 = OpenCodeCLI;
|
|
4110
|
+
function isCommandAvailable(command) {
|
|
4111
|
+
return Effect11.tryPromise({
|
|
4112
|
+
try: async () => {
|
|
4113
|
+
const { execSync } = await import("child_process");
|
|
4114
|
+
try {
|
|
4115
|
+
execSync(`which ${command}`, { stdio: "ignore" });
|
|
4116
|
+
return true;
|
|
4117
|
+
} catch {
|
|
4118
|
+
return false;
|
|
4119
|
+
}
|
|
4120
|
+
},
|
|
4121
|
+
catch: () => false
|
|
4122
|
+
}).pipe(Effect11.orElseSucceed(() => false));
|
|
4123
|
+
}
|
|
4124
|
+
function checkProviderAvailable(name) {
|
|
4125
|
+
const config = PROVIDER_CONFIGS[name];
|
|
4126
|
+
return isCommandAvailable(config.cliCommand).pipe(
|
|
4127
|
+
Effect11.flatMap(
|
|
4128
|
+
(available) => available ? Effect11.void : Effect11.fail(
|
|
4129
|
+
new LLMError({
|
|
4130
|
+
message: `Provider "${name}" is not available. The CLI command "${config.cliCommand}" was not found.
|
|
4131
|
+
|
|
4132
|
+
To use this provider, install it first:
|
|
4133
|
+
${config.installUrl}`
|
|
4134
|
+
})
|
|
4135
|
+
)
|
|
4136
|
+
)
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4139
|
+
function createProviderLayer2(name) {
|
|
3983
4140
|
switch (name) {
|
|
3984
4141
|
case "claude":
|
|
3985
4142
|
return ClaudeCLI.Live;
|
|
3986
4143
|
case "cursor":
|
|
3987
4144
|
return CursorCLI.Live;
|
|
4145
|
+
case "opencode":
|
|
4146
|
+
return OpenCodeCLI2.Live;
|
|
3988
4147
|
default: {
|
|
3989
4148
|
const _exhaustive = name;
|
|
3990
4149
|
return _exhaustive;
|
|
@@ -3996,7 +4155,7 @@ function createProviderLayer(name) {
|
|
|
3996
4155
|
init_esm_shims();
|
|
3997
4156
|
import { access as access2, mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3998
4157
|
import { join as join3 } from "path";
|
|
3999
|
-
import { Effect as
|
|
4158
|
+
import { Effect as Effect12, Layer as Layer7 } from "effect";
|
|
4000
4159
|
|
|
4001
4160
|
// src/services/plan-store.ts
|
|
4002
4161
|
init_esm_shims();
|
|
@@ -4007,14 +4166,14 @@ var PlanStore = class extends Context4.Tag("@ferix/PlanStore")() {
|
|
|
4007
4166
|
// src/layers/plan/file-system.ts
|
|
4008
4167
|
var PLANS_DIR2 = ".ferix/plans";
|
|
4009
4168
|
function ensureDir2(dirPath) {
|
|
4010
|
-
return
|
|
4169
|
+
return Effect12.tryPromise({
|
|
4011
4170
|
try: () => mkdir3(dirPath, { recursive: true }),
|
|
4012
4171
|
catch: (error) => new PlanStoreError({
|
|
4013
4172
|
message: `Failed to create directory: ${dirPath}`,
|
|
4014
4173
|
operation: "create",
|
|
4015
4174
|
cause: error
|
|
4016
4175
|
})
|
|
4017
|
-
}).pipe(
|
|
4176
|
+
}).pipe(Effect12.asVoid);
|
|
4018
4177
|
}
|
|
4019
4178
|
function getSessionDir2(sessionId) {
|
|
4020
4179
|
return join3(process.cwd(), PLANS_DIR2, sessionId);
|
|
@@ -4029,8 +4188,8 @@ function serializePlan(plan) {
|
|
|
4029
4188
|
return JSON.stringify(plan, null, 2);
|
|
4030
4189
|
}
|
|
4031
4190
|
function deserializePlan(json, planId) {
|
|
4032
|
-
return
|
|
4033
|
-
const parsed = yield*
|
|
4191
|
+
return Effect12.gen(function* () {
|
|
4192
|
+
const parsed = yield* Effect12.try({
|
|
4034
4193
|
try: () => JSON.parse(json),
|
|
4035
4194
|
catch: (error) => new PlanStoreError({
|
|
4036
4195
|
message: `Invalid JSON in plan file: ${String(error)}`,
|
|
@@ -4039,7 +4198,7 @@ function deserializePlan(json, planId) {
|
|
|
4039
4198
|
})
|
|
4040
4199
|
});
|
|
4041
4200
|
const validated = yield* decodePlanData(parsed).pipe(
|
|
4042
|
-
|
|
4201
|
+
Effect12.mapError(
|
|
4043
4202
|
(error) => new PlanStoreError({
|
|
4044
4203
|
message: `Plan validation failed: ${String(error)}`,
|
|
4045
4204
|
operation: "load",
|
|
@@ -4053,11 +4212,11 @@ function deserializePlan(json, planId) {
|
|
|
4053
4212
|
};
|
|
4054
4213
|
});
|
|
4055
4214
|
}
|
|
4056
|
-
var
|
|
4057
|
-
create: (sessionId, plan) =>
|
|
4215
|
+
var make3 = {
|
|
4216
|
+
create: (sessionId, plan) => Effect12.gen(function* () {
|
|
4058
4217
|
const sessionDir = getSessionDir2(sessionId);
|
|
4059
4218
|
yield* ensureDir2(sessionDir);
|
|
4060
|
-
const existingPlans = yield*
|
|
4219
|
+
const existingPlans = yield* Effect12.tryPromise({
|
|
4061
4220
|
try: async () => {
|
|
4062
4221
|
try {
|
|
4063
4222
|
const files = await readdir(sessionDir);
|
|
@@ -4074,7 +4233,7 @@ var make5 = {
|
|
|
4074
4233
|
const planId = generatePlanId(existingPlans + 1);
|
|
4075
4234
|
const planPath = getPlanPath(sessionId, planId);
|
|
4076
4235
|
const fullPlan = { ...plan, id: planId };
|
|
4077
|
-
yield*
|
|
4236
|
+
yield* Effect12.tryPromise({
|
|
4078
4237
|
try: () => writeFile2(planPath, serializePlan(fullPlan), "utf-8"),
|
|
4079
4238
|
catch: (error) => new PlanStoreError({
|
|
4080
4239
|
message: `Failed to write plan file: ${planPath}`,
|
|
@@ -4084,10 +4243,10 @@ var make5 = {
|
|
|
4084
4243
|
});
|
|
4085
4244
|
return planId;
|
|
4086
4245
|
}),
|
|
4087
|
-
load: (planId, sessionId) =>
|
|
4246
|
+
load: (planId, sessionId) => Effect12.gen(function* () {
|
|
4088
4247
|
if (sessionId) {
|
|
4089
4248
|
const planPath = getPlanPath(sessionId, planId);
|
|
4090
|
-
const content = yield*
|
|
4249
|
+
const content = yield* Effect12.tryPromise({
|
|
4091
4250
|
try: () => readFile2(planPath, "utf-8"),
|
|
4092
4251
|
catch: (error) => new PlanStoreError({
|
|
4093
4252
|
message: `Failed to read plan file: ${planPath}`,
|
|
@@ -4097,7 +4256,7 @@ var make5 = {
|
|
|
4097
4256
|
});
|
|
4098
4257
|
return yield* deserializePlan(content, planId);
|
|
4099
4258
|
}
|
|
4100
|
-
const sessionDirs = yield*
|
|
4259
|
+
const sessionDirs = yield* Effect12.tryPromise({
|
|
4101
4260
|
try: async () => {
|
|
4102
4261
|
const plansDir = join3(process.cwd(), PLANS_DIR2);
|
|
4103
4262
|
const dirs = await readdir(plansDir);
|
|
@@ -4111,7 +4270,7 @@ var make5 = {
|
|
|
4111
4270
|
});
|
|
4112
4271
|
for (const sid of sessionDirs) {
|
|
4113
4272
|
const planPath = getPlanPath(sid, planId);
|
|
4114
|
-
const exists = yield*
|
|
4273
|
+
const exists = yield* Effect12.tryPromise({
|
|
4115
4274
|
try: async () => {
|
|
4116
4275
|
await access2(planPath);
|
|
4117
4276
|
return true;
|
|
@@ -4120,9 +4279,9 @@ var make5 = {
|
|
|
4120
4279
|
message: "File not found",
|
|
4121
4280
|
operation: "load"
|
|
4122
4281
|
})
|
|
4123
|
-
}).pipe(
|
|
4282
|
+
}).pipe(Effect12.orElseSucceed(() => false));
|
|
4124
4283
|
if (exists) {
|
|
4125
|
-
const content = yield*
|
|
4284
|
+
const content = yield* Effect12.tryPromise({
|
|
4126
4285
|
try: () => readFile2(planPath, "utf-8"),
|
|
4127
4286
|
catch: (error) => new PlanStoreError({
|
|
4128
4287
|
message: `Failed to read plan file: ${planPath}`,
|
|
@@ -4133,16 +4292,16 @@ var make5 = {
|
|
|
4133
4292
|
return yield* deserializePlan(content, planId);
|
|
4134
4293
|
}
|
|
4135
4294
|
}
|
|
4136
|
-
return yield*
|
|
4295
|
+
return yield* Effect12.fail(
|
|
4137
4296
|
new PlanStoreError({
|
|
4138
4297
|
message: `Plan not found: ${planId}`,
|
|
4139
4298
|
operation: "load"
|
|
4140
4299
|
})
|
|
4141
4300
|
);
|
|
4142
4301
|
}),
|
|
4143
|
-
update: (planId, plan) =>
|
|
4302
|
+
update: (planId, plan) => Effect12.gen(function* () {
|
|
4144
4303
|
const planPath = getPlanPath(plan.sessionId, planId);
|
|
4145
|
-
yield*
|
|
4304
|
+
yield* Effect12.tryPromise({
|
|
4146
4305
|
try: () => writeFile2(planPath, serializePlan(plan), "utf-8"),
|
|
4147
4306
|
catch: (error) => new PlanStoreError({
|
|
4148
4307
|
message: `Failed to update plan file: ${planPath}`,
|
|
@@ -4151,9 +4310,9 @@ var make5 = {
|
|
|
4151
4310
|
})
|
|
4152
4311
|
});
|
|
4153
4312
|
}),
|
|
4154
|
-
list: (sessionId) =>
|
|
4313
|
+
list: (sessionId) => Effect12.gen(function* () {
|
|
4155
4314
|
const sessionDir = getSessionDir2(sessionId);
|
|
4156
|
-
const files = yield*
|
|
4315
|
+
const files = yield* Effect12.tryPromise({
|
|
4157
4316
|
try: async () => {
|
|
4158
4317
|
try {
|
|
4159
4318
|
return await readdir(sessionDir);
|
|
@@ -4170,17 +4329,17 @@ var make5 = {
|
|
|
4170
4329
|
return files.filter((f) => f.endsWith(".json")).map((f) => PlanId(f.replace(".json", "")));
|
|
4171
4330
|
})
|
|
4172
4331
|
};
|
|
4173
|
-
var
|
|
4332
|
+
var Live9 = Layer7.succeed(PlanStore, make3);
|
|
4174
4333
|
var FileSystemPlan = {
|
|
4175
|
-
Live:
|
|
4334
|
+
Live: Live9
|
|
4176
4335
|
};
|
|
4177
4336
|
|
|
4178
4337
|
// src/layers/plan/memory.ts
|
|
4179
4338
|
init_esm_shims();
|
|
4180
|
-
import { Effect as
|
|
4339
|
+
import { Effect as Effect13, Layer as Layer8, Ref as Ref5 } from "effect";
|
|
4181
4340
|
function createMemoryPlanStore(stateRef) {
|
|
4182
4341
|
return {
|
|
4183
|
-
create: (sessionId, plan) =>
|
|
4342
|
+
create: (sessionId, plan) => Effect13.gen(function* () {
|
|
4184
4343
|
const state = yield* Ref5.get(stateRef);
|
|
4185
4344
|
if (!state.has(sessionId)) {
|
|
4186
4345
|
state.set(sessionId, /* @__PURE__ */ new Map());
|
|
@@ -4195,7 +4354,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4195
4354
|
yield* Ref5.set(stateRef, state);
|
|
4196
4355
|
return planId;
|
|
4197
4356
|
}),
|
|
4198
|
-
load: (planId, sessionId) =>
|
|
4357
|
+
load: (planId, sessionId) => Effect13.gen(function* () {
|
|
4199
4358
|
const state = yield* Ref5.get(stateRef);
|
|
4200
4359
|
if (sessionId) {
|
|
4201
4360
|
const sessionPlans = state.get(sessionId);
|
|
@@ -4203,7 +4362,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4203
4362
|
if (plan) {
|
|
4204
4363
|
return plan;
|
|
4205
4364
|
}
|
|
4206
|
-
return yield*
|
|
4365
|
+
return yield* Effect13.fail(
|
|
4207
4366
|
new PlanStoreError({
|
|
4208
4367
|
message: `Plan not found: ${planId}`,
|
|
4209
4368
|
operation: "load"
|
|
@@ -4216,18 +4375,18 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4216
4375
|
return plan;
|
|
4217
4376
|
}
|
|
4218
4377
|
}
|
|
4219
|
-
return yield*
|
|
4378
|
+
return yield* Effect13.fail(
|
|
4220
4379
|
new PlanStoreError({
|
|
4221
4380
|
message: `Plan not found: ${planId}`,
|
|
4222
4381
|
operation: "load"
|
|
4223
4382
|
})
|
|
4224
4383
|
);
|
|
4225
4384
|
}),
|
|
4226
|
-
update: (planId, plan) =>
|
|
4385
|
+
update: (planId, plan) => Effect13.gen(function* () {
|
|
4227
4386
|
const state = yield* Ref5.get(stateRef);
|
|
4228
4387
|
const sessionPlans = state.get(plan.sessionId);
|
|
4229
4388
|
if (!sessionPlans) {
|
|
4230
|
-
return yield*
|
|
4389
|
+
return yield* Effect13.fail(
|
|
4231
4390
|
new PlanStoreError({
|
|
4232
4391
|
message: `Session not found: ${plan.sessionId}`,
|
|
4233
4392
|
operation: "update"
|
|
@@ -4237,7 +4396,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4237
4396
|
sessionPlans.set(planId, plan);
|
|
4238
4397
|
yield* Ref5.set(stateRef, state);
|
|
4239
4398
|
}),
|
|
4240
|
-
list: (sessionId) =>
|
|
4399
|
+
list: (sessionId) => Effect13.gen(function* () {
|
|
4241
4400
|
const state = yield* Ref5.get(stateRef);
|
|
4242
4401
|
const sessionPlans = state.get(sessionId);
|
|
4243
4402
|
if (!sessionPlans) {
|
|
@@ -4248,17 +4407,17 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4248
4407
|
};
|
|
4249
4408
|
}
|
|
4250
4409
|
function layer4() {
|
|
4251
|
-
return
|
|
4410
|
+
return Layer8.effect(
|
|
4252
4411
|
PlanStore,
|
|
4253
|
-
|
|
4412
|
+
Effect13.gen(function* () {
|
|
4254
4413
|
const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
|
|
4255
4414
|
return createMemoryPlanStore(stateRef);
|
|
4256
4415
|
})
|
|
4257
4416
|
);
|
|
4258
4417
|
}
|
|
4259
|
-
var
|
|
4418
|
+
var Live10 = layer4();
|
|
4260
4419
|
var MemoryPlan = {
|
|
4261
|
-
Live:
|
|
4420
|
+
Live: Live10,
|
|
4262
4421
|
layer: layer4
|
|
4263
4422
|
};
|
|
4264
4423
|
|
|
@@ -4266,7 +4425,7 @@ var MemoryPlan = {
|
|
|
4266
4425
|
init_esm_shims();
|
|
4267
4426
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
4268
4427
|
import { join as join4 } from "path";
|
|
4269
|
-
import { DateTime as DateTime3, Effect as
|
|
4428
|
+
import { DateTime as DateTime3, Effect as Effect14, Layer as Layer9 } from "effect";
|
|
4270
4429
|
|
|
4271
4430
|
// src/services/progress-store.ts
|
|
4272
4431
|
init_esm_shims();
|
|
@@ -4277,14 +4436,14 @@ var ProgressStore = class extends Context5.Tag("@ferix/ProgressStore")() {
|
|
|
4277
4436
|
// src/layers/progress/file-system.ts
|
|
4278
4437
|
var PLANS_DIR3 = ".ferix/plans";
|
|
4279
4438
|
function ensureDir3(dirPath) {
|
|
4280
|
-
return
|
|
4439
|
+
return Effect14.tryPromise({
|
|
4281
4440
|
try: () => mkdir4(dirPath, { recursive: true }),
|
|
4282
4441
|
catch: (error) => new ProgressStoreError({
|
|
4283
4442
|
message: `Failed to create directory: ${dirPath}`,
|
|
4284
4443
|
operation: "append",
|
|
4285
4444
|
cause: error
|
|
4286
4445
|
})
|
|
4287
|
-
}).pipe(
|
|
4446
|
+
}).pipe(Effect14.asVoid);
|
|
4288
4447
|
}
|
|
4289
4448
|
function getSessionDir3(sessionId) {
|
|
4290
4449
|
return join4(process.cwd(), PLANS_DIR3, sessionId);
|
|
@@ -4296,8 +4455,8 @@ function serializeProgress(progress) {
|
|
|
4296
4455
|
return JSON.stringify(progress, null, 2);
|
|
4297
4456
|
}
|
|
4298
4457
|
function deserializeProgress(json) {
|
|
4299
|
-
return
|
|
4300
|
-
const parsed = yield*
|
|
4458
|
+
return Effect14.gen(function* () {
|
|
4459
|
+
const parsed = yield* Effect14.try({
|
|
4301
4460
|
try: () => JSON.parse(json),
|
|
4302
4461
|
catch: (error) => new ProgressStoreError({
|
|
4303
4462
|
message: `Invalid JSON in progress file: ${String(error)}`,
|
|
@@ -4306,7 +4465,7 @@ function deserializeProgress(json) {
|
|
|
4306
4465
|
})
|
|
4307
4466
|
});
|
|
4308
4467
|
const validated = yield* decodeProgressFile(parsed).pipe(
|
|
4309
|
-
|
|
4468
|
+
Effect14.mapError(
|
|
4310
4469
|
(error) => new ProgressStoreError({
|
|
4311
4470
|
message: `Progress validation failed: ${String(error)}`,
|
|
4312
4471
|
operation: "load",
|
|
@@ -4324,12 +4483,12 @@ function createEmptyProgress(sessionId, createdAt) {
|
|
|
4324
4483
|
entries: []
|
|
4325
4484
|
};
|
|
4326
4485
|
}
|
|
4327
|
-
var
|
|
4328
|
-
append: (sessionId, entry) =>
|
|
4486
|
+
var make4 = {
|
|
4487
|
+
append: (sessionId, entry) => Effect14.gen(function* () {
|
|
4329
4488
|
const sessionDir = getSessionDir3(sessionId);
|
|
4330
4489
|
yield* ensureDir3(sessionDir);
|
|
4331
4490
|
const progressPath = getProgressPath(sessionId);
|
|
4332
|
-
const existing = yield*
|
|
4491
|
+
const existing = yield* Effect14.tryPromise({
|
|
4333
4492
|
try: async () => {
|
|
4334
4493
|
try {
|
|
4335
4494
|
const content = await readFile3(progressPath, "utf-8");
|
|
@@ -4347,7 +4506,7 @@ var make6 = {
|
|
|
4347
4506
|
let progress;
|
|
4348
4507
|
if (existing) {
|
|
4349
4508
|
progress = yield* deserializeProgress(existing).pipe(
|
|
4350
|
-
|
|
4509
|
+
Effect14.mapError(
|
|
4351
4510
|
(err) => new ProgressStoreError({
|
|
4352
4511
|
message: err.message,
|
|
4353
4512
|
operation: "append",
|
|
@@ -4363,7 +4522,7 @@ var make6 = {
|
|
|
4363
4522
|
...progress,
|
|
4364
4523
|
entries: [...progress.entries, entry]
|
|
4365
4524
|
};
|
|
4366
|
-
yield*
|
|
4525
|
+
yield* Effect14.tryPromise({
|
|
4367
4526
|
try: () => writeFile3(progressPath, serializeProgress(updatedProgress), "utf-8"),
|
|
4368
4527
|
catch: (error) => new ProgressStoreError({
|
|
4369
4528
|
message: `Failed to write progress file: ${progressPath}`,
|
|
@@ -4372,9 +4531,9 @@ var make6 = {
|
|
|
4372
4531
|
})
|
|
4373
4532
|
});
|
|
4374
4533
|
}),
|
|
4375
|
-
load: (sessionId) =>
|
|
4534
|
+
load: (sessionId) => Effect14.gen(function* () {
|
|
4376
4535
|
const progressPath = getProgressPath(sessionId);
|
|
4377
|
-
const content = yield*
|
|
4536
|
+
const content = yield* Effect14.tryPromise({
|
|
4378
4537
|
try: async () => {
|
|
4379
4538
|
try {
|
|
4380
4539
|
return await readFile3(progressPath, "utf-8");
|
|
@@ -4394,23 +4553,23 @@ var make6 = {
|
|
|
4394
4553
|
}
|
|
4395
4554
|
return yield* deserializeProgress(content);
|
|
4396
4555
|
}),
|
|
4397
|
-
getRecent: (sessionId, count) =>
|
|
4398
|
-
const progress = yield*
|
|
4556
|
+
getRecent: (sessionId, count) => Effect14.gen(function* () {
|
|
4557
|
+
const progress = yield* make4.load(sessionId);
|
|
4399
4558
|
const entries = progress.entries;
|
|
4400
4559
|
return entries.slice(-count);
|
|
4401
4560
|
})
|
|
4402
4561
|
};
|
|
4403
|
-
var
|
|
4562
|
+
var Live11 = Layer9.succeed(ProgressStore, make4);
|
|
4404
4563
|
var FileSystemProgress = {
|
|
4405
|
-
Live:
|
|
4564
|
+
Live: Live11
|
|
4406
4565
|
};
|
|
4407
4566
|
|
|
4408
4567
|
// src/layers/progress/memory.ts
|
|
4409
4568
|
init_esm_shims();
|
|
4410
|
-
import { DateTime as DateTime4, Effect as
|
|
4569
|
+
import { DateTime as DateTime4, Effect as Effect15, Layer as Layer10, Ref as Ref6 } from "effect";
|
|
4411
4570
|
function createMemoryProgressStore(stateRef) {
|
|
4412
4571
|
return {
|
|
4413
|
-
append: (sessionId, entry) =>
|
|
4572
|
+
append: (sessionId, entry) => Effect15.gen(function* () {
|
|
4414
4573
|
const state = yield* Ref6.get(stateRef);
|
|
4415
4574
|
let progress = state.get(sessionId);
|
|
4416
4575
|
if (!progress) {
|
|
@@ -4428,7 +4587,7 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4428
4587
|
state.set(sessionId, updatedProgress);
|
|
4429
4588
|
yield* Ref6.set(stateRef, state);
|
|
4430
4589
|
}),
|
|
4431
|
-
load: (sessionId) =>
|
|
4590
|
+
load: (sessionId) => Effect15.gen(function* () {
|
|
4432
4591
|
const state = yield* Ref6.get(stateRef);
|
|
4433
4592
|
const progress = state.get(sessionId);
|
|
4434
4593
|
if (!progress) {
|
|
@@ -4441,7 +4600,7 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4441
4600
|
}
|
|
4442
4601
|
return progress;
|
|
4443
4602
|
}),
|
|
4444
|
-
getRecent: (sessionId, count) =>
|
|
4603
|
+
getRecent: (sessionId, count) => Effect15.gen(function* () {
|
|
4445
4604
|
const state = yield* Ref6.get(stateRef);
|
|
4446
4605
|
const progress = state.get(sessionId);
|
|
4447
4606
|
if (!progress) {
|
|
@@ -4452,17 +4611,17 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4452
4611
|
};
|
|
4453
4612
|
}
|
|
4454
4613
|
function layer5() {
|
|
4455
|
-
return
|
|
4614
|
+
return Layer10.effect(
|
|
4456
4615
|
ProgressStore,
|
|
4457
|
-
|
|
4616
|
+
Effect15.gen(function* () {
|
|
4458
4617
|
const stateRef = yield* Ref6.make(/* @__PURE__ */ new Map());
|
|
4459
4618
|
return createMemoryProgressStore(stateRef);
|
|
4460
4619
|
})
|
|
4461
4620
|
);
|
|
4462
4621
|
}
|
|
4463
|
-
var
|
|
4622
|
+
var Live12 = layer5();
|
|
4464
4623
|
var MemoryProgress = {
|
|
4465
|
-
Live:
|
|
4624
|
+
Live: Live12,
|
|
4466
4625
|
layer: layer5
|
|
4467
4626
|
};
|
|
4468
4627
|
|
|
@@ -4470,7 +4629,7 @@ var MemoryProgress = {
|
|
|
4470
4629
|
init_esm_shims();
|
|
4471
4630
|
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
4472
4631
|
import { join as join5 } from "path";
|
|
4473
|
-
import { DateTime as DateTime5, Effect as
|
|
4632
|
+
import { DateTime as DateTime5, Effect as Effect16, Layer as Layer11 } from "effect";
|
|
4474
4633
|
import { humanId } from "human-id";
|
|
4475
4634
|
|
|
4476
4635
|
// src/services/session-store.ts
|
|
@@ -4486,14 +4645,14 @@ function generateSessionId(timestampMs) {
|
|
|
4486
4645
|
return `${id}-${timestampMs}`;
|
|
4487
4646
|
}
|
|
4488
4647
|
function ensureDir4(dirPath) {
|
|
4489
|
-
return
|
|
4648
|
+
return Effect16.tryPromise({
|
|
4490
4649
|
try: () => mkdir5(dirPath, { recursive: true }),
|
|
4491
4650
|
catch: (error) => new SessionStoreError({
|
|
4492
4651
|
message: `Failed to create directory: ${dirPath}`,
|
|
4493
4652
|
operation: "create",
|
|
4494
4653
|
cause: error
|
|
4495
4654
|
})
|
|
4496
|
-
}).pipe(
|
|
4655
|
+
}).pipe(Effect16.asVoid);
|
|
4497
4656
|
}
|
|
4498
4657
|
function getSessionPath(sessionId) {
|
|
4499
4658
|
return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
|
|
@@ -4502,8 +4661,8 @@ function serializeSession(session) {
|
|
|
4502
4661
|
return JSON.stringify(session, null, 2);
|
|
4503
4662
|
}
|
|
4504
4663
|
function deserializeSession(json) {
|
|
4505
|
-
return
|
|
4506
|
-
const parsed = yield*
|
|
4664
|
+
return Effect16.gen(function* () {
|
|
4665
|
+
const parsed = yield* Effect16.try({
|
|
4507
4666
|
try: () => JSON.parse(json),
|
|
4508
4667
|
catch: (error) => new SessionStoreError({
|
|
4509
4668
|
message: `Invalid JSON in session file: ${String(error)}`,
|
|
@@ -4512,7 +4671,7 @@ function deserializeSession(json) {
|
|
|
4512
4671
|
})
|
|
4513
4672
|
});
|
|
4514
4673
|
const validated = yield* decodeSession(parsed).pipe(
|
|
4515
|
-
|
|
4674
|
+
Effect16.mapError(
|
|
4516
4675
|
(error) => new SessionStoreError({
|
|
4517
4676
|
message: `Session validation failed: ${String(error)}`,
|
|
4518
4677
|
operation: "get",
|
|
@@ -4523,8 +4682,8 @@ function deserializeSession(json) {
|
|
|
4523
4682
|
return validated;
|
|
4524
4683
|
});
|
|
4525
4684
|
}
|
|
4526
|
-
var
|
|
4527
|
-
create: (originalTask) =>
|
|
4685
|
+
var make5 = {
|
|
4686
|
+
create: (originalTask) => Effect16.gen(function* () {
|
|
4528
4687
|
const sessionsDir = join5(process.cwd(), SESSIONS_DIR);
|
|
4529
4688
|
yield* ensureDir4(sessionsDir);
|
|
4530
4689
|
const now = yield* DateTime5.now;
|
|
@@ -4538,7 +4697,7 @@ var make7 = {
|
|
|
4538
4697
|
completedTasks: []
|
|
4539
4698
|
};
|
|
4540
4699
|
const sessionPath = getSessionPath(sessionId);
|
|
4541
|
-
yield*
|
|
4700
|
+
yield* Effect16.tryPromise({
|
|
4542
4701
|
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
4543
4702
|
catch: (error) => new SessionStoreError({
|
|
4544
4703
|
message: `Failed to write session file: ${sessionPath}`,
|
|
@@ -4548,9 +4707,9 @@ var make7 = {
|
|
|
4548
4707
|
});
|
|
4549
4708
|
return session;
|
|
4550
4709
|
}),
|
|
4551
|
-
get: (sessionId) =>
|
|
4710
|
+
get: (sessionId) => Effect16.gen(function* () {
|
|
4552
4711
|
const sessionPath = getSessionPath(sessionId);
|
|
4553
|
-
const content = yield*
|
|
4712
|
+
const content = yield* Effect16.tryPromise({
|
|
4554
4713
|
try: () => readFile4(sessionPath, "utf-8"),
|
|
4555
4714
|
catch: (error) => new SessionStoreError({
|
|
4556
4715
|
message: `Failed to read session file: ${sessionPath}`,
|
|
@@ -4560,9 +4719,9 @@ var make7 = {
|
|
|
4560
4719
|
});
|
|
4561
4720
|
return yield* deserializeSession(content);
|
|
4562
4721
|
}),
|
|
4563
|
-
update: (sessionId, session) =>
|
|
4722
|
+
update: (sessionId, session) => Effect16.gen(function* () {
|
|
4564
4723
|
const sessionPath = getSessionPath(sessionId);
|
|
4565
|
-
yield*
|
|
4724
|
+
yield* Effect16.tryPromise({
|
|
4566
4725
|
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
4567
4726
|
catch: (error) => new SessionStoreError({
|
|
4568
4727
|
message: `Failed to update session file: ${sessionPath}`,
|
|
@@ -4572,17 +4731,17 @@ var make7 = {
|
|
|
4572
4731
|
});
|
|
4573
4732
|
})
|
|
4574
4733
|
};
|
|
4575
|
-
var
|
|
4734
|
+
var Live13 = Layer11.succeed(SessionStore, make5);
|
|
4576
4735
|
var FileSystemSession = {
|
|
4577
|
-
Live:
|
|
4736
|
+
Live: Live13
|
|
4578
4737
|
};
|
|
4579
4738
|
|
|
4580
4739
|
// src/layers/session/memory.ts
|
|
4581
4740
|
init_esm_shims();
|
|
4582
|
-
import { DateTime as DateTime6, Effect as
|
|
4741
|
+
import { DateTime as DateTime6, Effect as Effect17, Layer as Layer12, Ref as Ref7 } from "effect";
|
|
4583
4742
|
function createMemorySessionStore(stateRef, counterRef) {
|
|
4584
4743
|
return {
|
|
4585
|
-
create: (originalTask) =>
|
|
4744
|
+
create: (originalTask) => Effect17.gen(function* () {
|
|
4586
4745
|
const state = yield* Ref7.get(stateRef);
|
|
4587
4746
|
const counter = yield* Ref7.updateAndGet(counterRef, (n) => n + 1);
|
|
4588
4747
|
const sessionId = `test-session-${counter}`;
|
|
@@ -4598,11 +4757,11 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4598
4757
|
yield* Ref7.set(stateRef, state);
|
|
4599
4758
|
return session;
|
|
4600
4759
|
}),
|
|
4601
|
-
get: (sessionId) =>
|
|
4760
|
+
get: (sessionId) => Effect17.gen(function* () {
|
|
4602
4761
|
const state = yield* Ref7.get(stateRef);
|
|
4603
4762
|
const session = state.get(sessionId);
|
|
4604
4763
|
if (!session) {
|
|
4605
|
-
return yield*
|
|
4764
|
+
return yield* Effect17.fail(
|
|
4606
4765
|
new SessionStoreError({
|
|
4607
4766
|
message: `Session not found: ${sessionId}`,
|
|
4608
4767
|
operation: "get"
|
|
@@ -4611,10 +4770,10 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4611
4770
|
}
|
|
4612
4771
|
return session;
|
|
4613
4772
|
}),
|
|
4614
|
-
update: (sessionId, session) =>
|
|
4773
|
+
update: (sessionId, session) => Effect17.gen(function* () {
|
|
4615
4774
|
const state = yield* Ref7.get(stateRef);
|
|
4616
4775
|
if (!state.has(sessionId)) {
|
|
4617
|
-
return yield*
|
|
4776
|
+
return yield* Effect17.fail(
|
|
4618
4777
|
new SessionStoreError({
|
|
4619
4778
|
message: `Session not found: ${sessionId}`,
|
|
4620
4779
|
operation: "update"
|
|
@@ -4627,24 +4786,24 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4627
4786
|
};
|
|
4628
4787
|
}
|
|
4629
4788
|
function layer6() {
|
|
4630
|
-
return
|
|
4789
|
+
return Layer12.effect(
|
|
4631
4790
|
SessionStore,
|
|
4632
|
-
|
|
4791
|
+
Effect17.gen(function* () {
|
|
4633
4792
|
const stateRef = yield* Ref7.make(/* @__PURE__ */ new Map());
|
|
4634
4793
|
const counterRef = yield* Ref7.make(0);
|
|
4635
4794
|
return createMemorySessionStore(stateRef, counterRef);
|
|
4636
4795
|
})
|
|
4637
4796
|
);
|
|
4638
4797
|
}
|
|
4639
|
-
var
|
|
4798
|
+
var Live14 = layer6();
|
|
4640
4799
|
var MemorySession = {
|
|
4641
|
-
Live:
|
|
4800
|
+
Live: Live14,
|
|
4642
4801
|
layer: layer6
|
|
4643
4802
|
};
|
|
4644
4803
|
|
|
4645
4804
|
// src/layers/signal/ferix-parser.ts
|
|
4646
4805
|
init_esm_shims();
|
|
4647
|
-
import { Effect as
|
|
4806
|
+
import { Effect as Effect18, Layer as Layer13, Ref as Ref8 } from "effect";
|
|
4648
4807
|
|
|
4649
4808
|
// src/services/signal-parser.ts
|
|
4650
4809
|
init_esm_shims();
|
|
@@ -5154,10 +5313,10 @@ signalSpecRegistry.register(tasksDefinedSpec);
|
|
|
5154
5313
|
// src/layers/signal/ferix-parser.ts
|
|
5155
5314
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
5156
5315
|
function createAccumulatorImpl() {
|
|
5157
|
-
return
|
|
5316
|
+
return Effect18.gen(function* () {
|
|
5158
5317
|
const chunksRef = yield* Ref8.make([]);
|
|
5159
5318
|
const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
|
|
5160
|
-
const feed = (text) =>
|
|
5319
|
+
const feed = (text) => Effect18.gen(function* () {
|
|
5161
5320
|
const chunks = yield* Ref8.get(chunksRef);
|
|
5162
5321
|
chunks.push(text);
|
|
5163
5322
|
const buffer = chunks.join("");
|
|
@@ -5185,7 +5344,7 @@ function createAccumulatorImpl() {
|
|
|
5185
5344
|
yield* Ref8.set(emittedRef, emitted);
|
|
5186
5345
|
return newSignals;
|
|
5187
5346
|
});
|
|
5188
|
-
const flush = () =>
|
|
5347
|
+
const flush = () => Effect18.gen(function* () {
|
|
5189
5348
|
const chunks = yield* Ref8.get(chunksRef);
|
|
5190
5349
|
const buffer = chunks.join("");
|
|
5191
5350
|
yield* Ref8.set(chunksRef, []);
|
|
@@ -5200,41 +5359,17 @@ function createAccumulatorImpl() {
|
|
|
5200
5359
|
return { feed, flush };
|
|
5201
5360
|
});
|
|
5202
5361
|
}
|
|
5203
|
-
var
|
|
5204
|
-
parse: (text) =>
|
|
5362
|
+
var make6 = {
|
|
5363
|
+
parse: (text) => Effect18.succeed(signalSpecRegistry.parseAll(text)),
|
|
5205
5364
|
createAccumulator: createAccumulatorImpl
|
|
5206
5365
|
};
|
|
5207
|
-
var
|
|
5366
|
+
var Live15 = Layer13.succeed(SignalParser, make6);
|
|
5208
5367
|
var FerixParser = {
|
|
5209
|
-
Live:
|
|
5210
|
-
};
|
|
5211
|
-
|
|
5212
|
-
// src/layers/llm/types.ts
|
|
5213
|
-
init_esm_shims();
|
|
5214
|
-
var PROVIDER_CONFIGS = {
|
|
5215
|
-
claude: {
|
|
5216
|
-
name: "claude",
|
|
5217
|
-
cliCommand: "claude",
|
|
5218
|
-
args: [
|
|
5219
|
-
"--permission-mode",
|
|
5220
|
-
"acceptEdits",
|
|
5221
|
-
"--output-format",
|
|
5222
|
-
"stream-json",
|
|
5223
|
-
"--verbose",
|
|
5224
|
-
"--include-partial-messages"
|
|
5225
|
-
],
|
|
5226
|
-
permissions: "acceptEdits"
|
|
5227
|
-
},
|
|
5228
|
-
cursor: {
|
|
5229
|
-
name: "cursor",
|
|
5230
|
-
cliCommand: "cursor-agent",
|
|
5231
|
-
args: ["--print", "--force", "--output-format", "stream-json"],
|
|
5232
|
-
permissions: "acceptEdits"
|
|
5233
|
-
}
|
|
5368
|
+
Live: Live15
|
|
5234
5369
|
};
|
|
5235
5370
|
|
|
5236
5371
|
// src/layers/index.ts
|
|
5237
|
-
var ProductionLayers =
|
|
5372
|
+
var ProductionLayers = Layer14.mergeAll(
|
|
5238
5373
|
ClaudeCLI.Live,
|
|
5239
5374
|
FerixParser.Live,
|
|
5240
5375
|
FileSystemPlan.Live,
|
|
@@ -5244,8 +5379,8 @@ var ProductionLayers = Layer15.mergeAll(
|
|
|
5244
5379
|
FileSystemGit.Live
|
|
5245
5380
|
);
|
|
5246
5381
|
function createProductionLayers(provider = "claude") {
|
|
5247
|
-
const llmLayer =
|
|
5248
|
-
return
|
|
5382
|
+
const llmLayer = createProviderLayer2(provider);
|
|
5383
|
+
return Layer14.mergeAll(
|
|
5249
5384
|
llmLayer,
|
|
5250
5385
|
FerixParser.Live,
|
|
5251
5386
|
FileSystemPlan.Live,
|
|
@@ -5255,7 +5390,7 @@ function createProductionLayers(provider = "claude") {
|
|
|
5255
5390
|
FileSystemGit.Live
|
|
5256
5391
|
);
|
|
5257
5392
|
}
|
|
5258
|
-
var TestLayers =
|
|
5393
|
+
var TestLayers = Layer14.mergeAll(
|
|
5259
5394
|
Mock.Live,
|
|
5260
5395
|
FerixParser.Live,
|
|
5261
5396
|
MemoryPlan.Live,
|
|
@@ -5265,7 +5400,7 @@ var TestLayers = Layer15.mergeAll(
|
|
|
5265
5400
|
MemoryGit.Live
|
|
5266
5401
|
);
|
|
5267
5402
|
function createTestLayers(events) {
|
|
5268
|
-
return
|
|
5403
|
+
return Layer14.mergeAll(
|
|
5269
5404
|
Mock.layer({ events }),
|
|
5270
5405
|
FerixParser.Live,
|
|
5271
5406
|
MemoryPlan.layer(),
|
|
@@ -5281,27 +5416,27 @@ init_esm_shims();
|
|
|
5281
5416
|
|
|
5282
5417
|
// src/orchestrator/loop.ts
|
|
5283
5418
|
init_esm_shims();
|
|
5284
|
-
import { DateTime as DateTime10, Effect as
|
|
5419
|
+
import { DateTime as DateTime10, Effect as Effect23, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
|
|
5285
5420
|
|
|
5286
5421
|
// src/orchestrator/discovery.ts
|
|
5287
5422
|
init_esm_shims();
|
|
5288
|
-
import { DateTime as DateTime8, Effect as
|
|
5423
|
+
import { DateTime as DateTime8, Effect as Effect21, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
|
|
5289
5424
|
|
|
5290
5425
|
// src/layers/plan/task-generation.ts
|
|
5291
5426
|
init_esm_shims();
|
|
5292
5427
|
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
5293
5428
|
import { join as join6 } from "path";
|
|
5294
|
-
import { Effect as
|
|
5429
|
+
import { Effect as Effect19 } from "effect";
|
|
5295
5430
|
var PLANS_DIR4 = ".ferix/plans";
|
|
5296
5431
|
function ensureDir5(dirPath) {
|
|
5297
|
-
return
|
|
5432
|
+
return Effect19.tryPromise({
|
|
5298
5433
|
try: () => mkdir6(dirPath, { recursive: true }),
|
|
5299
5434
|
catch: (error) => new PlanStoreError({
|
|
5300
5435
|
message: `Failed to create directory: ${dirPath}`,
|
|
5301
5436
|
operation: "create",
|
|
5302
5437
|
cause: error
|
|
5303
5438
|
})
|
|
5304
|
-
}).pipe(
|
|
5439
|
+
}).pipe(Effect19.asVoid);
|
|
5305
5440
|
}
|
|
5306
5441
|
function getSessionDir4(sessionId) {
|
|
5307
5442
|
return join6(process.cwd(), PLANS_DIR4, sessionId);
|
|
@@ -5310,12 +5445,12 @@ function getTasksMdPath(sessionId) {
|
|
|
5310
5445
|
return join6(getSessionDir4(sessionId), "tasks.md");
|
|
5311
5446
|
}
|
|
5312
5447
|
function writeTasksMd(sessionId, tasks) {
|
|
5313
|
-
return
|
|
5448
|
+
return Effect19.gen(function* () {
|
|
5314
5449
|
const sessionDir = getSessionDir4(sessionId);
|
|
5315
5450
|
yield* ensureDir5(sessionDir);
|
|
5316
5451
|
const tasksMdPath = getTasksMdPath(sessionId);
|
|
5317
5452
|
const content = formatTasksMd(tasks);
|
|
5318
|
-
yield*
|
|
5453
|
+
yield* Effect19.tryPromise({
|
|
5319
5454
|
try: () => writeFile5(tasksMdPath, content, "utf-8"),
|
|
5320
5455
|
catch: (error) => new PlanStoreError({
|
|
5321
5456
|
message: `Failed to write tasks.md: ${tasksMdPath}`,
|
|
@@ -5497,7 +5632,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
5497
5632
|
|
|
5498
5633
|
// src/orchestrator/plan-updates.ts
|
|
5499
5634
|
init_esm_shims();
|
|
5500
|
-
import { DateTime as DateTime7, Effect as
|
|
5635
|
+
import { DateTime as DateTime7, Effect as Effect20, Ref as Ref9 } from "effect";
|
|
5501
5636
|
|
|
5502
5637
|
// src/orchestrator/plan-updates/index.ts
|
|
5503
5638
|
init_esm_shims();
|
|
@@ -5802,9 +5937,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
5802
5937
|
tasks: plan.tasks
|
|
5803
5938
|
}) : planStore.update(plan.id, plan);
|
|
5804
5939
|
return storeOp.pipe(
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
(error) =>
|
|
5940
|
+
Effect20.map(() => null),
|
|
5941
|
+
Effect20.catchAll(
|
|
5942
|
+
(error) => Effect20.succeed({
|
|
5808
5943
|
_tag: "PlanUpdateFailed",
|
|
5809
5944
|
operation,
|
|
5810
5945
|
error: error.message,
|
|
@@ -5814,7 +5949,7 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
5814
5949
|
);
|
|
5815
5950
|
}
|
|
5816
5951
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
5817
|
-
return
|
|
5952
|
+
return Effect20.gen(function* () {
|
|
5818
5953
|
const currentPlan = yield* Ref9.get(currentPlanRef);
|
|
5819
5954
|
const now = yield* DateTime7.now;
|
|
5820
5955
|
const timestamp = DateTime7.formatIso(now);
|
|
@@ -5836,7 +5971,7 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
5836
5971
|
});
|
|
5837
5972
|
}
|
|
5838
5973
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
5839
|
-
return
|
|
5974
|
+
return Effect20.gen(function* () {
|
|
5840
5975
|
const state = yield* Ref9.get(persistenceStateRef);
|
|
5841
5976
|
if (!(state.dirty && state.pendingOperation)) {
|
|
5842
5977
|
return [];
|
|
@@ -6110,12 +6245,12 @@ Begin.`);
|
|
|
6110
6245
|
|
|
6111
6246
|
// src/orchestrator/discovery.ts
|
|
6112
6247
|
function processTextSignals(signalParser, text, context) {
|
|
6113
|
-
return
|
|
6248
|
+
return Effect21.gen(function* () {
|
|
6114
6249
|
const events = [];
|
|
6115
6250
|
const parsedSignals = [];
|
|
6116
6251
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6117
|
-
|
|
6118
|
-
(error) =>
|
|
6252
|
+
Effect21.tapError(
|
|
6253
|
+
(error) => Effect21.logDebug(
|
|
6119
6254
|
"Signal parsing failed, continuing with empty signals",
|
|
6120
6255
|
{
|
|
6121
6256
|
error: String(error),
|
|
@@ -6123,7 +6258,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6123
6258
|
}
|
|
6124
6259
|
)
|
|
6125
6260
|
),
|
|
6126
|
-
|
|
6261
|
+
Effect21.orElseSucceed(() => [])
|
|
6127
6262
|
);
|
|
6128
6263
|
for (const signal of signals) {
|
|
6129
6264
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6133,7 +6268,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6133
6268
|
});
|
|
6134
6269
|
}
|
|
6135
6270
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
6136
|
-
return
|
|
6271
|
+
return Effect21.gen(function* () {
|
|
6137
6272
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6138
6273
|
const events = [domainEvent];
|
|
6139
6274
|
const allSignals = [];
|
|
@@ -6177,8 +6312,8 @@ function planTasksToGeneratedTasks(plan) {
|
|
|
6177
6312
|
}));
|
|
6178
6313
|
}
|
|
6179
6314
|
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath) {
|
|
6180
|
-
return
|
|
6181
|
-
|
|
6315
|
+
return Stream7.unwrap(
|
|
6316
|
+
Effect21.gen(function* () {
|
|
6182
6317
|
const startTimeUtc = yield* DateTime8.now;
|
|
6183
6318
|
const startTime = DateTime8.toEpochMillis(startTimeUtc);
|
|
6184
6319
|
const persistenceStateRef = yield* Ref10.make({
|
|
@@ -6196,16 +6331,16 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6196
6331
|
};
|
|
6197
6332
|
const prompt = buildDiscoveryPrompt(config);
|
|
6198
6333
|
const llmStream = llm.execute(prompt, worktreePath ? { cwd: worktreePath } : void 0).pipe(
|
|
6199
|
-
|
|
6334
|
+
Stream7.mapError(
|
|
6200
6335
|
(e) => new OrchestratorError({
|
|
6201
6336
|
message: `LLM execution failed during discovery: ${String(e)}`,
|
|
6202
6337
|
phase: "discovery",
|
|
6203
6338
|
cause: e
|
|
6204
6339
|
})
|
|
6205
6340
|
),
|
|
6206
|
-
|
|
6207
|
-
(llmEvent) =>
|
|
6208
|
-
|
|
6341
|
+
Stream7.flatMap(
|
|
6342
|
+
(llmEvent) => Stream7.unwrap(
|
|
6343
|
+
Effect21.gen(function* () {
|
|
6209
6344
|
const now = yield* DateTime8.now;
|
|
6210
6345
|
const context = {
|
|
6211
6346
|
timestamp: DateTime8.toEpochMillis(now)
|
|
@@ -6226,13 +6361,13 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6226
6361
|
);
|
|
6227
6362
|
events.push(...planEvents);
|
|
6228
6363
|
}
|
|
6229
|
-
return
|
|
6364
|
+
return Stream7.fromIterable(events);
|
|
6230
6365
|
})
|
|
6231
6366
|
)
|
|
6232
6367
|
),
|
|
6233
6368
|
// Convert LLM errors to LoopFailed events
|
|
6234
|
-
|
|
6235
|
-
(error) =>
|
|
6369
|
+
Stream7.catchAll(
|
|
6370
|
+
(error) => Stream7.succeed({
|
|
6236
6371
|
_tag: "LoopFailed",
|
|
6237
6372
|
error: {
|
|
6238
6373
|
message: error.message,
|
|
@@ -6241,8 +6376,8 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6241
6376
|
})
|
|
6242
6377
|
)
|
|
6243
6378
|
);
|
|
6244
|
-
const completionStream =
|
|
6245
|
-
|
|
6379
|
+
const completionStream = Stream7.fromEffect(
|
|
6380
|
+
Effect21.gen(function* () {
|
|
6246
6381
|
const persistEvents = yield* flushPlanPersistence(
|
|
6247
6382
|
planStore,
|
|
6248
6383
|
currentPlanRef,
|
|
@@ -6253,12 +6388,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6253
6388
|
if (plan && plan.tasks.length > 0) {
|
|
6254
6389
|
const generatedTasks = planTasksToGeneratedTasks(plan);
|
|
6255
6390
|
yield* writeTasksMd(sessionId, generatedTasks).pipe(
|
|
6256
|
-
|
|
6257
|
-
(error) =>
|
|
6391
|
+
Effect21.tapError(
|
|
6392
|
+
(error) => Effect21.logDebug("Failed to write tasks.md, continuing", {
|
|
6258
6393
|
error: String(error)
|
|
6259
6394
|
})
|
|
6260
6395
|
),
|
|
6261
|
-
|
|
6396
|
+
Effect21.orElseSucceed(() => void 0)
|
|
6262
6397
|
);
|
|
6263
6398
|
}
|
|
6264
6399
|
const endTimeUtc = yield* DateTime8.now;
|
|
@@ -6270,12 +6405,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6270
6405
|
};
|
|
6271
6406
|
return [...persistEvents, discoveryCompleted];
|
|
6272
6407
|
})
|
|
6273
|
-
).pipe(
|
|
6408
|
+
).pipe(Stream7.flatMap((events) => Stream7.fromIterable(events)));
|
|
6274
6409
|
return pipe(
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6410
|
+
Stream7.succeed(discoveryStarted),
|
|
6411
|
+
Stream7.concat(Stream7.succeed(analysingToolUse)),
|
|
6412
|
+
Stream7.concat(llmStream),
|
|
6413
|
+
Stream7.concat(completionStream)
|
|
6279
6414
|
);
|
|
6280
6415
|
})
|
|
6281
6416
|
);
|
|
@@ -6283,15 +6418,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6283
6418
|
|
|
6284
6419
|
// src/orchestrator/iteration.ts
|
|
6285
6420
|
init_esm_shims();
|
|
6286
|
-
import { DateTime as DateTime9, Effect as
|
|
6421
|
+
import { DateTime as DateTime9, Effect as Effect22, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
|
|
6287
6422
|
function processTextSignals2(signalParser, text, context) {
|
|
6288
|
-
return
|
|
6423
|
+
return Effect22.gen(function* () {
|
|
6289
6424
|
const events = [];
|
|
6290
6425
|
let completed = false;
|
|
6291
6426
|
const parsedSignals = [];
|
|
6292
6427
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6293
|
-
|
|
6294
|
-
(error) =>
|
|
6428
|
+
Effect22.tapError(
|
|
6429
|
+
(error) => Effect22.logDebug(
|
|
6295
6430
|
"Signal parsing failed, continuing with empty signals",
|
|
6296
6431
|
{
|
|
6297
6432
|
error: String(error),
|
|
@@ -6299,7 +6434,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6299
6434
|
}
|
|
6300
6435
|
)
|
|
6301
6436
|
),
|
|
6302
|
-
|
|
6437
|
+
Effect22.orElseSucceed(() => [])
|
|
6303
6438
|
);
|
|
6304
6439
|
for (const signal of signals) {
|
|
6305
6440
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6312,7 +6447,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6312
6447
|
});
|
|
6313
6448
|
}
|
|
6314
6449
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
6315
|
-
return
|
|
6450
|
+
return Effect22.gen(function* () {
|
|
6316
6451
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6317
6452
|
const events = [domainEvent];
|
|
6318
6453
|
let completed = false;
|
|
@@ -6344,8 +6479,8 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
6344
6479
|
});
|
|
6345
6480
|
}
|
|
6346
6481
|
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
6347
|
-
return
|
|
6348
|
-
|
|
6482
|
+
return Stream8.unwrap(
|
|
6483
|
+
Effect22.gen(function* () {
|
|
6349
6484
|
const currentPlan = yield* Ref11.get(currentPlanRef);
|
|
6350
6485
|
const persistenceStateRef = yield* Ref11.make({
|
|
6351
6486
|
dirty: false,
|
|
@@ -6359,16 +6494,16 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6359
6494
|
buildPrompt(config, iteration, currentPlan),
|
|
6360
6495
|
worktreePath ? { cwd: worktreePath } : void 0
|
|
6361
6496
|
).pipe(
|
|
6362
|
-
|
|
6497
|
+
Stream8.mapError(
|
|
6363
6498
|
(e) => new OrchestratorError({
|
|
6364
6499
|
message: `LLM execution failed: ${String(e)}`,
|
|
6365
6500
|
phase: "iteration",
|
|
6366
6501
|
cause: e
|
|
6367
6502
|
})
|
|
6368
6503
|
),
|
|
6369
|
-
|
|
6370
|
-
(llmEvent) =>
|
|
6371
|
-
|
|
6504
|
+
Stream8.flatMap(
|
|
6505
|
+
(llmEvent) => Stream8.unwrap(
|
|
6506
|
+
Effect22.gen(function* () {
|
|
6372
6507
|
const now = yield* DateTime9.now;
|
|
6373
6508
|
const context = {
|
|
6374
6509
|
timestamp: DateTime9.toEpochMillis(now)
|
|
@@ -6392,13 +6527,13 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6392
6527
|
if (result.completed) {
|
|
6393
6528
|
yield* Ref11.set(loopCompletedRef, true);
|
|
6394
6529
|
}
|
|
6395
|
-
return
|
|
6530
|
+
return Stream8.fromIterable(events);
|
|
6396
6531
|
})
|
|
6397
6532
|
)
|
|
6398
6533
|
),
|
|
6399
6534
|
// Convert LLM errors to LoopFailed events with iteration context
|
|
6400
|
-
|
|
6401
|
-
(error) =>
|
|
6535
|
+
Stream8.catchAll(
|
|
6536
|
+
(error) => Stream8.succeed({
|
|
6402
6537
|
_tag: "LoopFailed",
|
|
6403
6538
|
error: {
|
|
6404
6539
|
message: error.message,
|
|
@@ -6408,8 +6543,8 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6408
6543
|
})
|
|
6409
6544
|
)
|
|
6410
6545
|
);
|
|
6411
|
-
const completionStream =
|
|
6412
|
-
|
|
6546
|
+
const completionStream = Stream8.fromEffect(
|
|
6547
|
+
Effect22.gen(function* () {
|
|
6413
6548
|
const persistEvents = yield* flushPlanPersistence(
|
|
6414
6549
|
planStore,
|
|
6415
6550
|
currentPlanRef,
|
|
@@ -6421,11 +6556,11 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6421
6556
|
};
|
|
6422
6557
|
return [...persistEvents, iterCompleted];
|
|
6423
6558
|
})
|
|
6424
|
-
).pipe(
|
|
6559
|
+
).pipe(Stream8.flatMap((events) => Stream8.fromIterable(events)));
|
|
6425
6560
|
return pipe2(
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6561
|
+
Stream8.succeed(iterStarted),
|
|
6562
|
+
Stream8.concat(llmStream),
|
|
6563
|
+
Stream8.concat(completionStream)
|
|
6429
6564
|
);
|
|
6430
6565
|
})
|
|
6431
6566
|
);
|
|
@@ -6433,15 +6568,15 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6433
6568
|
|
|
6434
6569
|
// src/orchestrator/loop.ts
|
|
6435
6570
|
function runLoop(config) {
|
|
6436
|
-
return
|
|
6437
|
-
|
|
6571
|
+
return Stream9.unwrap(
|
|
6572
|
+
Effect23.gen(function* () {
|
|
6438
6573
|
const llm = yield* LLM;
|
|
6439
6574
|
const signalParser = yield* SignalParser;
|
|
6440
6575
|
const sessionStore = yield* SessionStore;
|
|
6441
6576
|
const planStore = yield* PlanStore;
|
|
6442
6577
|
const git = yield* Git;
|
|
6443
6578
|
const session = yield* sessionStore.create(config.task).pipe(
|
|
6444
|
-
|
|
6579
|
+
Effect23.mapError(
|
|
6445
6580
|
(e) => new OrchestratorError({
|
|
6446
6581
|
message: `Failed to create session: ${e.message}`,
|
|
6447
6582
|
phase: "setup",
|
|
@@ -6450,7 +6585,7 @@ function runLoop(config) {
|
|
|
6450
6585
|
)
|
|
6451
6586
|
);
|
|
6452
6587
|
const worktreePath = yield* git.createWorktree(session.id).pipe(
|
|
6453
|
-
|
|
6588
|
+
Effect23.mapError(
|
|
6454
6589
|
(e) => new OrchestratorError({
|
|
6455
6590
|
message: `Failed to create worktree: ${e.message}`,
|
|
6456
6591
|
phase: "setup",
|
|
@@ -6464,12 +6599,12 @@ function runLoop(config) {
|
|
|
6464
6599
|
worktreePath,
|
|
6465
6600
|
branchName
|
|
6466
6601
|
}).pipe(
|
|
6467
|
-
|
|
6468
|
-
(error) =>
|
|
6602
|
+
Effect23.tapError(
|
|
6603
|
+
(error) => Effect23.logDebug("Failed to update session with worktree info", {
|
|
6469
6604
|
error: String(error)
|
|
6470
6605
|
})
|
|
6471
6606
|
),
|
|
6472
|
-
|
|
6607
|
+
Effect23.orElseSucceed(() => void 0)
|
|
6473
6608
|
);
|
|
6474
6609
|
const startTimeUtc = yield* DateTime10.now;
|
|
6475
6610
|
const startTime = DateTime10.toEpochMillis(startTimeUtc);
|
|
@@ -6497,9 +6632,9 @@ function runLoop(config) {
|
|
|
6497
6632
|
session.id,
|
|
6498
6633
|
worktreePath
|
|
6499
6634
|
);
|
|
6500
|
-
const iterationsStream =
|
|
6635
|
+
const iterationsStream = Stream9.unfoldEffect(
|
|
6501
6636
|
1,
|
|
6502
|
-
(iteration) =>
|
|
6637
|
+
(iteration) => Effect23.gen(function* () {
|
|
6503
6638
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
6504
6639
|
if (completed || iteration > maxIterations) {
|
|
6505
6640
|
return Option.none();
|
|
@@ -6507,7 +6642,7 @@ function runLoop(config) {
|
|
|
6507
6642
|
return Option.some([iteration, iteration + 1]);
|
|
6508
6643
|
})
|
|
6509
6644
|
).pipe(
|
|
6510
|
-
|
|
6645
|
+
Stream9.flatMap(
|
|
6511
6646
|
(iteration) => createIterationStream(
|
|
6512
6647
|
llm,
|
|
6513
6648
|
signalParser,
|
|
@@ -6531,17 +6666,17 @@ function runLoop(config) {
|
|
|
6531
6666
|
worktreePath
|
|
6532
6667
|
);
|
|
6533
6668
|
return pipe3(
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6669
|
+
Stream9.succeed(loopStarted),
|
|
6670
|
+
Stream9.concat(Stream9.succeed(worktreeCreated)),
|
|
6671
|
+
Stream9.concat(discoveryStream),
|
|
6672
|
+
Stream9.concat(iterationsStream),
|
|
6673
|
+
Stream9.concat(completionStream)
|
|
6539
6674
|
);
|
|
6540
6675
|
}).pipe(
|
|
6541
6676
|
// Also catch setup errors (e.g., session creation failure)
|
|
6542
|
-
|
|
6543
|
-
(error) =>
|
|
6544
|
-
|
|
6677
|
+
Effect23.catchAll(
|
|
6678
|
+
(error) => Effect23.succeed(
|
|
6679
|
+
Stream9.succeed({
|
|
6545
6680
|
_tag: "LoopFailed",
|
|
6546
6681
|
error: {
|
|
6547
6682
|
message: error.message,
|
|
@@ -6554,19 +6689,19 @@ function runLoop(config) {
|
|
|
6554
6689
|
);
|
|
6555
6690
|
}
|
|
6556
6691
|
function createCompletionStream(sessionStore, git, session, config, startTime, loopCompletedRef, _worktreePath) {
|
|
6557
|
-
return
|
|
6558
|
-
|
|
6692
|
+
return Stream9.unwrap(
|
|
6693
|
+
Effect23.gen(function* () {
|
|
6559
6694
|
const endTimeUtc = yield* DateTime10.now;
|
|
6560
6695
|
const durationMs = DateTime10.toEpochMillis(endTimeUtc) - startTime;
|
|
6561
6696
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
6562
6697
|
yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
|
|
6563
|
-
|
|
6564
|
-
(error) =>
|
|
6698
|
+
Effect23.tapError(
|
|
6699
|
+
(error) => Effect23.logDebug("Final commit failed, continuing", {
|
|
6565
6700
|
sessionId: session.id,
|
|
6566
6701
|
error: String(error)
|
|
6567
6702
|
})
|
|
6568
6703
|
),
|
|
6569
|
-
|
|
6704
|
+
Effect23.orElseSucceed(() => void 0)
|
|
6570
6705
|
);
|
|
6571
6706
|
const summary = {
|
|
6572
6707
|
iterations: config.maxIterations,
|
|
@@ -6579,16 +6714,16 @@ function createCompletionStream(sessionStore, git, session, config, startTime, l
|
|
|
6579
6714
|
...session,
|
|
6580
6715
|
status: completed ? "completed" : "paused"
|
|
6581
6716
|
}).pipe(
|
|
6582
|
-
|
|
6583
|
-
(error) =>
|
|
6717
|
+
Effect23.tapError(
|
|
6718
|
+
(error) => Effect23.logDebug("Session update failed, continuing", {
|
|
6584
6719
|
sessionId: session.id,
|
|
6585
6720
|
error: String(error)
|
|
6586
6721
|
})
|
|
6587
6722
|
),
|
|
6588
|
-
|
|
6723
|
+
Effect23.orElseSucceed(() => void 0)
|
|
6589
6724
|
);
|
|
6590
6725
|
const loopCompleted = { _tag: "LoopCompleted", summary };
|
|
6591
|
-
return
|
|
6726
|
+
return Stream9.succeed(loopCompleted);
|
|
6592
6727
|
})
|
|
6593
6728
|
);
|
|
6594
6729
|
}
|
|
@@ -6597,11 +6732,11 @@ function createCompletionStream(sessionStore, git, session, config, startTime, l
|
|
|
6597
6732
|
function run(options) {
|
|
6598
6733
|
const { config, consumer: consumerType = "headless", onEvent } = options;
|
|
6599
6734
|
const events = runLoop(config);
|
|
6600
|
-
const eventsWithCallback = onEvent ? events.pipe(
|
|
6735
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect24.sync(() => onEvent(event)))) : events;
|
|
6601
6736
|
const layers = config.provider ? createProductionLayers(config.provider) : ProductionLayers;
|
|
6602
|
-
const eventsWithLayers = eventsWithCallback.pipe(
|
|
6737
|
+
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
6603
6738
|
if (consumerType === "none") {
|
|
6604
|
-
return eventsWithLayers.pipe(
|
|
6739
|
+
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
6605
6740
|
}
|
|
6606
6741
|
const consumer = consumerType === "tui" ? createTUIConsumer() : createHeadlessConsumer();
|
|
6607
6742
|
return consumer.consume(eventsWithLayers);
|
|
@@ -6609,19 +6744,19 @@ function run(options) {
|
|
|
6609
6744
|
function runTest(options, mockEvents) {
|
|
6610
6745
|
const { config, onEvent } = options;
|
|
6611
6746
|
const events = runLoop(config);
|
|
6612
|
-
const eventsWithCallback = onEvent ? events.pipe(
|
|
6747
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect24.sync(() => onEvent(event)))) : events;
|
|
6613
6748
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
6614
|
-
const eventsWithLayers = eventsWithCallback.pipe(
|
|
6615
|
-
return eventsWithLayers.pipe(
|
|
6749
|
+
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
6750
|
+
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
6616
6751
|
}
|
|
6617
6752
|
function collectEvents(config, mockEvents) {
|
|
6618
6753
|
const events = runLoop(config);
|
|
6619
6754
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
6620
|
-
return events.pipe(
|
|
6755
|
+
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect24.map((chunk) => Array.from(chunk)));
|
|
6621
6756
|
}
|
|
6622
6757
|
function main(config) {
|
|
6623
6758
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
6624
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
6759
|
+
return run({ config, consumer: consumerType }).pipe(Effect24.runPromise);
|
|
6625
6760
|
}
|
|
6626
6761
|
|
|
6627
6762
|
// src/services/index.ts
|
|
@@ -6796,7 +6931,7 @@ export {
|
|
|
6796
6931
|
collectEvents,
|
|
6797
6932
|
createHeadlessConsumer,
|
|
6798
6933
|
createProductionLayers,
|
|
6799
|
-
createProviderLayer,
|
|
6934
|
+
createProviderLayer2 as createProviderLayer,
|
|
6800
6935
|
createTUIConsumer,
|
|
6801
6936
|
createTestLayers,
|
|
6802
6937
|
decodeGuardrail,
|