ferix-code 0.0.2-beta.13 → 0.0.2-beta.14
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 +404 -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.14",
|
|
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,119 @@ 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 extractText2(json) {
|
|
4028
|
+
if (isTextContent2(json)) {
|
|
4029
|
+
return json.text;
|
|
4030
|
+
}
|
|
4031
|
+
return null;
|
|
4032
|
+
}
|
|
4033
|
+
|
|
4034
|
+
// src/layers/llm/stream-opencode.ts
|
|
4035
|
+
function processOpenCodeEvent(json, outputChunks, _toolState, emit) {
|
|
4036
|
+
const text = extractText2(json);
|
|
4037
|
+
if (text) {
|
|
4038
|
+
outputChunks.push(text);
|
|
4039
|
+
emit.single({ _tag: "Text", text });
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
if (isStepFinish(json)) {
|
|
4043
|
+
return;
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
var openCodeParser = {
|
|
4047
|
+
parseJsonLine: parseJsonLine2,
|
|
4048
|
+
processEvent: processOpenCodeEvent
|
|
4049
|
+
};
|
|
4050
|
+
function createOpenCodeEventStream(child, providerName = "OpenCode") {
|
|
4051
|
+
return createLLMEventStream(child, openCodeParser, providerName);
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
// src/layers/llm/providers/opencode.ts
|
|
4055
|
+
var OpenCodeProvider = createProvider(
|
|
4056
|
+
"opencode",
|
|
4057
|
+
(child) => createOpenCodeEventStream(child, "OpenCode"),
|
|
4058
|
+
checkProviderAvailable
|
|
4059
|
+
);
|
|
4060
|
+
var Live8 = createProviderLayer(OpenCodeProvider);
|
|
4061
|
+
var OpenCodeCLI = {
|
|
4062
|
+
Live: Live8,
|
|
4063
|
+
Provider: OpenCodeProvider
|
|
4064
|
+
};
|
|
4065
|
+
|
|
3981
4066
|
// src/layers/llm/providers/index.ts
|
|
3982
|
-
|
|
4067
|
+
var OpenCodeCLI2 = OpenCodeCLI;
|
|
4068
|
+
function isCommandAvailable(command) {
|
|
4069
|
+
return Effect11.tryPromise({
|
|
4070
|
+
try: async () => {
|
|
4071
|
+
const { execSync } = await import("child_process");
|
|
4072
|
+
try {
|
|
4073
|
+
execSync(`which ${command}`, { stdio: "ignore" });
|
|
4074
|
+
return true;
|
|
4075
|
+
} catch {
|
|
4076
|
+
return false;
|
|
4077
|
+
}
|
|
4078
|
+
},
|
|
4079
|
+
catch: () => false
|
|
4080
|
+
}).pipe(Effect11.orElseSucceed(() => false));
|
|
4081
|
+
}
|
|
4082
|
+
function checkProviderAvailable(name) {
|
|
4083
|
+
const config = PROVIDER_CONFIGS[name];
|
|
4084
|
+
return isCommandAvailable(config.cliCommand).pipe(
|
|
4085
|
+
Effect11.flatMap(
|
|
4086
|
+
(available) => available ? Effect11.void : Effect11.fail(
|
|
4087
|
+
new LLMError({
|
|
4088
|
+
message: `Provider "${name}" is not available. The CLI command "${config.cliCommand}" was not found.
|
|
4089
|
+
|
|
4090
|
+
To use this provider, install it first:
|
|
4091
|
+
${config.installUrl}`
|
|
4092
|
+
})
|
|
4093
|
+
)
|
|
4094
|
+
)
|
|
4095
|
+
);
|
|
4096
|
+
}
|
|
4097
|
+
function createProviderLayer2(name) {
|
|
3983
4098
|
switch (name) {
|
|
3984
4099
|
case "claude":
|
|
3985
4100
|
return ClaudeCLI.Live;
|
|
3986
4101
|
case "cursor":
|
|
3987
4102
|
return CursorCLI.Live;
|
|
4103
|
+
case "opencode":
|
|
4104
|
+
return OpenCodeCLI2.Live;
|
|
3988
4105
|
default: {
|
|
3989
4106
|
const _exhaustive = name;
|
|
3990
4107
|
return _exhaustive;
|
|
@@ -3996,7 +4113,7 @@ function createProviderLayer(name) {
|
|
|
3996
4113
|
init_esm_shims();
|
|
3997
4114
|
import { access as access2, mkdir as mkdir3, readdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3998
4115
|
import { join as join3 } from "path";
|
|
3999
|
-
import { Effect as
|
|
4116
|
+
import { Effect as Effect12, Layer as Layer7 } from "effect";
|
|
4000
4117
|
|
|
4001
4118
|
// src/services/plan-store.ts
|
|
4002
4119
|
init_esm_shims();
|
|
@@ -4007,14 +4124,14 @@ var PlanStore = class extends Context4.Tag("@ferix/PlanStore")() {
|
|
|
4007
4124
|
// src/layers/plan/file-system.ts
|
|
4008
4125
|
var PLANS_DIR2 = ".ferix/plans";
|
|
4009
4126
|
function ensureDir2(dirPath) {
|
|
4010
|
-
return
|
|
4127
|
+
return Effect12.tryPromise({
|
|
4011
4128
|
try: () => mkdir3(dirPath, { recursive: true }),
|
|
4012
4129
|
catch: (error) => new PlanStoreError({
|
|
4013
4130
|
message: `Failed to create directory: ${dirPath}`,
|
|
4014
4131
|
operation: "create",
|
|
4015
4132
|
cause: error
|
|
4016
4133
|
})
|
|
4017
|
-
}).pipe(
|
|
4134
|
+
}).pipe(Effect12.asVoid);
|
|
4018
4135
|
}
|
|
4019
4136
|
function getSessionDir2(sessionId) {
|
|
4020
4137
|
return join3(process.cwd(), PLANS_DIR2, sessionId);
|
|
@@ -4029,8 +4146,8 @@ function serializePlan(plan) {
|
|
|
4029
4146
|
return JSON.stringify(plan, null, 2);
|
|
4030
4147
|
}
|
|
4031
4148
|
function deserializePlan(json, planId) {
|
|
4032
|
-
return
|
|
4033
|
-
const parsed = yield*
|
|
4149
|
+
return Effect12.gen(function* () {
|
|
4150
|
+
const parsed = yield* Effect12.try({
|
|
4034
4151
|
try: () => JSON.parse(json),
|
|
4035
4152
|
catch: (error) => new PlanStoreError({
|
|
4036
4153
|
message: `Invalid JSON in plan file: ${String(error)}`,
|
|
@@ -4039,7 +4156,7 @@ function deserializePlan(json, planId) {
|
|
|
4039
4156
|
})
|
|
4040
4157
|
});
|
|
4041
4158
|
const validated = yield* decodePlanData(parsed).pipe(
|
|
4042
|
-
|
|
4159
|
+
Effect12.mapError(
|
|
4043
4160
|
(error) => new PlanStoreError({
|
|
4044
4161
|
message: `Plan validation failed: ${String(error)}`,
|
|
4045
4162
|
operation: "load",
|
|
@@ -4053,11 +4170,11 @@ function deserializePlan(json, planId) {
|
|
|
4053
4170
|
};
|
|
4054
4171
|
});
|
|
4055
4172
|
}
|
|
4056
|
-
var
|
|
4057
|
-
create: (sessionId, plan) =>
|
|
4173
|
+
var make3 = {
|
|
4174
|
+
create: (sessionId, plan) => Effect12.gen(function* () {
|
|
4058
4175
|
const sessionDir = getSessionDir2(sessionId);
|
|
4059
4176
|
yield* ensureDir2(sessionDir);
|
|
4060
|
-
const existingPlans = yield*
|
|
4177
|
+
const existingPlans = yield* Effect12.tryPromise({
|
|
4061
4178
|
try: async () => {
|
|
4062
4179
|
try {
|
|
4063
4180
|
const files = await readdir(sessionDir);
|
|
@@ -4074,7 +4191,7 @@ var make5 = {
|
|
|
4074
4191
|
const planId = generatePlanId(existingPlans + 1);
|
|
4075
4192
|
const planPath = getPlanPath(sessionId, planId);
|
|
4076
4193
|
const fullPlan = { ...plan, id: planId };
|
|
4077
|
-
yield*
|
|
4194
|
+
yield* Effect12.tryPromise({
|
|
4078
4195
|
try: () => writeFile2(planPath, serializePlan(fullPlan), "utf-8"),
|
|
4079
4196
|
catch: (error) => new PlanStoreError({
|
|
4080
4197
|
message: `Failed to write plan file: ${planPath}`,
|
|
@@ -4084,10 +4201,10 @@ var make5 = {
|
|
|
4084
4201
|
});
|
|
4085
4202
|
return planId;
|
|
4086
4203
|
}),
|
|
4087
|
-
load: (planId, sessionId) =>
|
|
4204
|
+
load: (planId, sessionId) => Effect12.gen(function* () {
|
|
4088
4205
|
if (sessionId) {
|
|
4089
4206
|
const planPath = getPlanPath(sessionId, planId);
|
|
4090
|
-
const content = yield*
|
|
4207
|
+
const content = yield* Effect12.tryPromise({
|
|
4091
4208
|
try: () => readFile2(planPath, "utf-8"),
|
|
4092
4209
|
catch: (error) => new PlanStoreError({
|
|
4093
4210
|
message: `Failed to read plan file: ${planPath}`,
|
|
@@ -4097,7 +4214,7 @@ var make5 = {
|
|
|
4097
4214
|
});
|
|
4098
4215
|
return yield* deserializePlan(content, planId);
|
|
4099
4216
|
}
|
|
4100
|
-
const sessionDirs = yield*
|
|
4217
|
+
const sessionDirs = yield* Effect12.tryPromise({
|
|
4101
4218
|
try: async () => {
|
|
4102
4219
|
const plansDir = join3(process.cwd(), PLANS_DIR2);
|
|
4103
4220
|
const dirs = await readdir(plansDir);
|
|
@@ -4111,7 +4228,7 @@ var make5 = {
|
|
|
4111
4228
|
});
|
|
4112
4229
|
for (const sid of sessionDirs) {
|
|
4113
4230
|
const planPath = getPlanPath(sid, planId);
|
|
4114
|
-
const exists = yield*
|
|
4231
|
+
const exists = yield* Effect12.tryPromise({
|
|
4115
4232
|
try: async () => {
|
|
4116
4233
|
await access2(planPath);
|
|
4117
4234
|
return true;
|
|
@@ -4120,9 +4237,9 @@ var make5 = {
|
|
|
4120
4237
|
message: "File not found",
|
|
4121
4238
|
operation: "load"
|
|
4122
4239
|
})
|
|
4123
|
-
}).pipe(
|
|
4240
|
+
}).pipe(Effect12.orElseSucceed(() => false));
|
|
4124
4241
|
if (exists) {
|
|
4125
|
-
const content = yield*
|
|
4242
|
+
const content = yield* Effect12.tryPromise({
|
|
4126
4243
|
try: () => readFile2(planPath, "utf-8"),
|
|
4127
4244
|
catch: (error) => new PlanStoreError({
|
|
4128
4245
|
message: `Failed to read plan file: ${planPath}`,
|
|
@@ -4133,16 +4250,16 @@ var make5 = {
|
|
|
4133
4250
|
return yield* deserializePlan(content, planId);
|
|
4134
4251
|
}
|
|
4135
4252
|
}
|
|
4136
|
-
return yield*
|
|
4253
|
+
return yield* Effect12.fail(
|
|
4137
4254
|
new PlanStoreError({
|
|
4138
4255
|
message: `Plan not found: ${planId}`,
|
|
4139
4256
|
operation: "load"
|
|
4140
4257
|
})
|
|
4141
4258
|
);
|
|
4142
4259
|
}),
|
|
4143
|
-
update: (planId, plan) =>
|
|
4260
|
+
update: (planId, plan) => Effect12.gen(function* () {
|
|
4144
4261
|
const planPath = getPlanPath(plan.sessionId, planId);
|
|
4145
|
-
yield*
|
|
4262
|
+
yield* Effect12.tryPromise({
|
|
4146
4263
|
try: () => writeFile2(planPath, serializePlan(plan), "utf-8"),
|
|
4147
4264
|
catch: (error) => new PlanStoreError({
|
|
4148
4265
|
message: `Failed to update plan file: ${planPath}`,
|
|
@@ -4151,9 +4268,9 @@ var make5 = {
|
|
|
4151
4268
|
})
|
|
4152
4269
|
});
|
|
4153
4270
|
}),
|
|
4154
|
-
list: (sessionId) =>
|
|
4271
|
+
list: (sessionId) => Effect12.gen(function* () {
|
|
4155
4272
|
const sessionDir = getSessionDir2(sessionId);
|
|
4156
|
-
const files = yield*
|
|
4273
|
+
const files = yield* Effect12.tryPromise({
|
|
4157
4274
|
try: async () => {
|
|
4158
4275
|
try {
|
|
4159
4276
|
return await readdir(sessionDir);
|
|
@@ -4170,17 +4287,17 @@ var make5 = {
|
|
|
4170
4287
|
return files.filter((f) => f.endsWith(".json")).map((f) => PlanId(f.replace(".json", "")));
|
|
4171
4288
|
})
|
|
4172
4289
|
};
|
|
4173
|
-
var
|
|
4290
|
+
var Live9 = Layer7.succeed(PlanStore, make3);
|
|
4174
4291
|
var FileSystemPlan = {
|
|
4175
|
-
Live:
|
|
4292
|
+
Live: Live9
|
|
4176
4293
|
};
|
|
4177
4294
|
|
|
4178
4295
|
// src/layers/plan/memory.ts
|
|
4179
4296
|
init_esm_shims();
|
|
4180
|
-
import { Effect as
|
|
4297
|
+
import { Effect as Effect13, Layer as Layer8, Ref as Ref5 } from "effect";
|
|
4181
4298
|
function createMemoryPlanStore(stateRef) {
|
|
4182
4299
|
return {
|
|
4183
|
-
create: (sessionId, plan) =>
|
|
4300
|
+
create: (sessionId, plan) => Effect13.gen(function* () {
|
|
4184
4301
|
const state = yield* Ref5.get(stateRef);
|
|
4185
4302
|
if (!state.has(sessionId)) {
|
|
4186
4303
|
state.set(sessionId, /* @__PURE__ */ new Map());
|
|
@@ -4195,7 +4312,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4195
4312
|
yield* Ref5.set(stateRef, state);
|
|
4196
4313
|
return planId;
|
|
4197
4314
|
}),
|
|
4198
|
-
load: (planId, sessionId) =>
|
|
4315
|
+
load: (planId, sessionId) => Effect13.gen(function* () {
|
|
4199
4316
|
const state = yield* Ref5.get(stateRef);
|
|
4200
4317
|
if (sessionId) {
|
|
4201
4318
|
const sessionPlans = state.get(sessionId);
|
|
@@ -4203,7 +4320,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4203
4320
|
if (plan) {
|
|
4204
4321
|
return plan;
|
|
4205
4322
|
}
|
|
4206
|
-
return yield*
|
|
4323
|
+
return yield* Effect13.fail(
|
|
4207
4324
|
new PlanStoreError({
|
|
4208
4325
|
message: `Plan not found: ${planId}`,
|
|
4209
4326
|
operation: "load"
|
|
@@ -4216,18 +4333,18 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4216
4333
|
return plan;
|
|
4217
4334
|
}
|
|
4218
4335
|
}
|
|
4219
|
-
return yield*
|
|
4336
|
+
return yield* Effect13.fail(
|
|
4220
4337
|
new PlanStoreError({
|
|
4221
4338
|
message: `Plan not found: ${planId}`,
|
|
4222
4339
|
operation: "load"
|
|
4223
4340
|
})
|
|
4224
4341
|
);
|
|
4225
4342
|
}),
|
|
4226
|
-
update: (planId, plan) =>
|
|
4343
|
+
update: (planId, plan) => Effect13.gen(function* () {
|
|
4227
4344
|
const state = yield* Ref5.get(stateRef);
|
|
4228
4345
|
const sessionPlans = state.get(plan.sessionId);
|
|
4229
4346
|
if (!sessionPlans) {
|
|
4230
|
-
return yield*
|
|
4347
|
+
return yield* Effect13.fail(
|
|
4231
4348
|
new PlanStoreError({
|
|
4232
4349
|
message: `Session not found: ${plan.sessionId}`,
|
|
4233
4350
|
operation: "update"
|
|
@@ -4237,7 +4354,7 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4237
4354
|
sessionPlans.set(planId, plan);
|
|
4238
4355
|
yield* Ref5.set(stateRef, state);
|
|
4239
4356
|
}),
|
|
4240
|
-
list: (sessionId) =>
|
|
4357
|
+
list: (sessionId) => Effect13.gen(function* () {
|
|
4241
4358
|
const state = yield* Ref5.get(stateRef);
|
|
4242
4359
|
const sessionPlans = state.get(sessionId);
|
|
4243
4360
|
if (!sessionPlans) {
|
|
@@ -4248,17 +4365,17 @@ function createMemoryPlanStore(stateRef) {
|
|
|
4248
4365
|
};
|
|
4249
4366
|
}
|
|
4250
4367
|
function layer4() {
|
|
4251
|
-
return
|
|
4368
|
+
return Layer8.effect(
|
|
4252
4369
|
PlanStore,
|
|
4253
|
-
|
|
4370
|
+
Effect13.gen(function* () {
|
|
4254
4371
|
const stateRef = yield* Ref5.make(/* @__PURE__ */ new Map());
|
|
4255
4372
|
return createMemoryPlanStore(stateRef);
|
|
4256
4373
|
})
|
|
4257
4374
|
);
|
|
4258
4375
|
}
|
|
4259
|
-
var
|
|
4376
|
+
var Live10 = layer4();
|
|
4260
4377
|
var MemoryPlan = {
|
|
4261
|
-
Live:
|
|
4378
|
+
Live: Live10,
|
|
4262
4379
|
layer: layer4
|
|
4263
4380
|
};
|
|
4264
4381
|
|
|
@@ -4266,7 +4383,7 @@ var MemoryPlan = {
|
|
|
4266
4383
|
init_esm_shims();
|
|
4267
4384
|
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
4268
4385
|
import { join as join4 } from "path";
|
|
4269
|
-
import { DateTime as DateTime3, Effect as
|
|
4386
|
+
import { DateTime as DateTime3, Effect as Effect14, Layer as Layer9 } from "effect";
|
|
4270
4387
|
|
|
4271
4388
|
// src/services/progress-store.ts
|
|
4272
4389
|
init_esm_shims();
|
|
@@ -4277,14 +4394,14 @@ var ProgressStore = class extends Context5.Tag("@ferix/ProgressStore")() {
|
|
|
4277
4394
|
// src/layers/progress/file-system.ts
|
|
4278
4395
|
var PLANS_DIR3 = ".ferix/plans";
|
|
4279
4396
|
function ensureDir3(dirPath) {
|
|
4280
|
-
return
|
|
4397
|
+
return Effect14.tryPromise({
|
|
4281
4398
|
try: () => mkdir4(dirPath, { recursive: true }),
|
|
4282
4399
|
catch: (error) => new ProgressStoreError({
|
|
4283
4400
|
message: `Failed to create directory: ${dirPath}`,
|
|
4284
4401
|
operation: "append",
|
|
4285
4402
|
cause: error
|
|
4286
4403
|
})
|
|
4287
|
-
}).pipe(
|
|
4404
|
+
}).pipe(Effect14.asVoid);
|
|
4288
4405
|
}
|
|
4289
4406
|
function getSessionDir3(sessionId) {
|
|
4290
4407
|
return join4(process.cwd(), PLANS_DIR3, sessionId);
|
|
@@ -4296,8 +4413,8 @@ function serializeProgress(progress) {
|
|
|
4296
4413
|
return JSON.stringify(progress, null, 2);
|
|
4297
4414
|
}
|
|
4298
4415
|
function deserializeProgress(json) {
|
|
4299
|
-
return
|
|
4300
|
-
const parsed = yield*
|
|
4416
|
+
return Effect14.gen(function* () {
|
|
4417
|
+
const parsed = yield* Effect14.try({
|
|
4301
4418
|
try: () => JSON.parse(json),
|
|
4302
4419
|
catch: (error) => new ProgressStoreError({
|
|
4303
4420
|
message: `Invalid JSON in progress file: ${String(error)}`,
|
|
@@ -4306,7 +4423,7 @@ function deserializeProgress(json) {
|
|
|
4306
4423
|
})
|
|
4307
4424
|
});
|
|
4308
4425
|
const validated = yield* decodeProgressFile(parsed).pipe(
|
|
4309
|
-
|
|
4426
|
+
Effect14.mapError(
|
|
4310
4427
|
(error) => new ProgressStoreError({
|
|
4311
4428
|
message: `Progress validation failed: ${String(error)}`,
|
|
4312
4429
|
operation: "load",
|
|
@@ -4324,12 +4441,12 @@ function createEmptyProgress(sessionId, createdAt) {
|
|
|
4324
4441
|
entries: []
|
|
4325
4442
|
};
|
|
4326
4443
|
}
|
|
4327
|
-
var
|
|
4328
|
-
append: (sessionId, entry) =>
|
|
4444
|
+
var make4 = {
|
|
4445
|
+
append: (sessionId, entry) => Effect14.gen(function* () {
|
|
4329
4446
|
const sessionDir = getSessionDir3(sessionId);
|
|
4330
4447
|
yield* ensureDir3(sessionDir);
|
|
4331
4448
|
const progressPath = getProgressPath(sessionId);
|
|
4332
|
-
const existing = yield*
|
|
4449
|
+
const existing = yield* Effect14.tryPromise({
|
|
4333
4450
|
try: async () => {
|
|
4334
4451
|
try {
|
|
4335
4452
|
const content = await readFile3(progressPath, "utf-8");
|
|
@@ -4347,7 +4464,7 @@ var make6 = {
|
|
|
4347
4464
|
let progress;
|
|
4348
4465
|
if (existing) {
|
|
4349
4466
|
progress = yield* deserializeProgress(existing).pipe(
|
|
4350
|
-
|
|
4467
|
+
Effect14.mapError(
|
|
4351
4468
|
(err) => new ProgressStoreError({
|
|
4352
4469
|
message: err.message,
|
|
4353
4470
|
operation: "append",
|
|
@@ -4363,7 +4480,7 @@ var make6 = {
|
|
|
4363
4480
|
...progress,
|
|
4364
4481
|
entries: [...progress.entries, entry]
|
|
4365
4482
|
};
|
|
4366
|
-
yield*
|
|
4483
|
+
yield* Effect14.tryPromise({
|
|
4367
4484
|
try: () => writeFile3(progressPath, serializeProgress(updatedProgress), "utf-8"),
|
|
4368
4485
|
catch: (error) => new ProgressStoreError({
|
|
4369
4486
|
message: `Failed to write progress file: ${progressPath}`,
|
|
@@ -4372,9 +4489,9 @@ var make6 = {
|
|
|
4372
4489
|
})
|
|
4373
4490
|
});
|
|
4374
4491
|
}),
|
|
4375
|
-
load: (sessionId) =>
|
|
4492
|
+
load: (sessionId) => Effect14.gen(function* () {
|
|
4376
4493
|
const progressPath = getProgressPath(sessionId);
|
|
4377
|
-
const content = yield*
|
|
4494
|
+
const content = yield* Effect14.tryPromise({
|
|
4378
4495
|
try: async () => {
|
|
4379
4496
|
try {
|
|
4380
4497
|
return await readFile3(progressPath, "utf-8");
|
|
@@ -4394,23 +4511,23 @@ var make6 = {
|
|
|
4394
4511
|
}
|
|
4395
4512
|
return yield* deserializeProgress(content);
|
|
4396
4513
|
}),
|
|
4397
|
-
getRecent: (sessionId, count) =>
|
|
4398
|
-
const progress = yield*
|
|
4514
|
+
getRecent: (sessionId, count) => Effect14.gen(function* () {
|
|
4515
|
+
const progress = yield* make4.load(sessionId);
|
|
4399
4516
|
const entries = progress.entries;
|
|
4400
4517
|
return entries.slice(-count);
|
|
4401
4518
|
})
|
|
4402
4519
|
};
|
|
4403
|
-
var
|
|
4520
|
+
var Live11 = Layer9.succeed(ProgressStore, make4);
|
|
4404
4521
|
var FileSystemProgress = {
|
|
4405
|
-
Live:
|
|
4522
|
+
Live: Live11
|
|
4406
4523
|
};
|
|
4407
4524
|
|
|
4408
4525
|
// src/layers/progress/memory.ts
|
|
4409
4526
|
init_esm_shims();
|
|
4410
|
-
import { DateTime as DateTime4, Effect as
|
|
4527
|
+
import { DateTime as DateTime4, Effect as Effect15, Layer as Layer10, Ref as Ref6 } from "effect";
|
|
4411
4528
|
function createMemoryProgressStore(stateRef) {
|
|
4412
4529
|
return {
|
|
4413
|
-
append: (sessionId, entry) =>
|
|
4530
|
+
append: (sessionId, entry) => Effect15.gen(function* () {
|
|
4414
4531
|
const state = yield* Ref6.get(stateRef);
|
|
4415
4532
|
let progress = state.get(sessionId);
|
|
4416
4533
|
if (!progress) {
|
|
@@ -4428,7 +4545,7 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4428
4545
|
state.set(sessionId, updatedProgress);
|
|
4429
4546
|
yield* Ref6.set(stateRef, state);
|
|
4430
4547
|
}),
|
|
4431
|
-
load: (sessionId) =>
|
|
4548
|
+
load: (sessionId) => Effect15.gen(function* () {
|
|
4432
4549
|
const state = yield* Ref6.get(stateRef);
|
|
4433
4550
|
const progress = state.get(sessionId);
|
|
4434
4551
|
if (!progress) {
|
|
@@ -4441,7 +4558,7 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4441
4558
|
}
|
|
4442
4559
|
return progress;
|
|
4443
4560
|
}),
|
|
4444
|
-
getRecent: (sessionId, count) =>
|
|
4561
|
+
getRecent: (sessionId, count) => Effect15.gen(function* () {
|
|
4445
4562
|
const state = yield* Ref6.get(stateRef);
|
|
4446
4563
|
const progress = state.get(sessionId);
|
|
4447
4564
|
if (!progress) {
|
|
@@ -4452,17 +4569,17 @@ function createMemoryProgressStore(stateRef) {
|
|
|
4452
4569
|
};
|
|
4453
4570
|
}
|
|
4454
4571
|
function layer5() {
|
|
4455
|
-
return
|
|
4572
|
+
return Layer10.effect(
|
|
4456
4573
|
ProgressStore,
|
|
4457
|
-
|
|
4574
|
+
Effect15.gen(function* () {
|
|
4458
4575
|
const stateRef = yield* Ref6.make(/* @__PURE__ */ new Map());
|
|
4459
4576
|
return createMemoryProgressStore(stateRef);
|
|
4460
4577
|
})
|
|
4461
4578
|
);
|
|
4462
4579
|
}
|
|
4463
|
-
var
|
|
4580
|
+
var Live12 = layer5();
|
|
4464
4581
|
var MemoryProgress = {
|
|
4465
|
-
Live:
|
|
4582
|
+
Live: Live12,
|
|
4466
4583
|
layer: layer5
|
|
4467
4584
|
};
|
|
4468
4585
|
|
|
@@ -4470,7 +4587,7 @@ var MemoryProgress = {
|
|
|
4470
4587
|
init_esm_shims();
|
|
4471
4588
|
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
4472
4589
|
import { join as join5 } from "path";
|
|
4473
|
-
import { DateTime as DateTime5, Effect as
|
|
4590
|
+
import { DateTime as DateTime5, Effect as Effect16, Layer as Layer11 } from "effect";
|
|
4474
4591
|
import { humanId } from "human-id";
|
|
4475
4592
|
|
|
4476
4593
|
// src/services/session-store.ts
|
|
@@ -4486,14 +4603,14 @@ function generateSessionId(timestampMs) {
|
|
|
4486
4603
|
return `${id}-${timestampMs}`;
|
|
4487
4604
|
}
|
|
4488
4605
|
function ensureDir4(dirPath) {
|
|
4489
|
-
return
|
|
4606
|
+
return Effect16.tryPromise({
|
|
4490
4607
|
try: () => mkdir5(dirPath, { recursive: true }),
|
|
4491
4608
|
catch: (error) => new SessionStoreError({
|
|
4492
4609
|
message: `Failed to create directory: ${dirPath}`,
|
|
4493
4610
|
operation: "create",
|
|
4494
4611
|
cause: error
|
|
4495
4612
|
})
|
|
4496
|
-
}).pipe(
|
|
4613
|
+
}).pipe(Effect16.asVoid);
|
|
4497
4614
|
}
|
|
4498
4615
|
function getSessionPath(sessionId) {
|
|
4499
4616
|
return join5(process.cwd(), SESSIONS_DIR, `${sessionId}.json`);
|
|
@@ -4502,8 +4619,8 @@ function serializeSession(session) {
|
|
|
4502
4619
|
return JSON.stringify(session, null, 2);
|
|
4503
4620
|
}
|
|
4504
4621
|
function deserializeSession(json) {
|
|
4505
|
-
return
|
|
4506
|
-
const parsed = yield*
|
|
4622
|
+
return Effect16.gen(function* () {
|
|
4623
|
+
const parsed = yield* Effect16.try({
|
|
4507
4624
|
try: () => JSON.parse(json),
|
|
4508
4625
|
catch: (error) => new SessionStoreError({
|
|
4509
4626
|
message: `Invalid JSON in session file: ${String(error)}`,
|
|
@@ -4512,7 +4629,7 @@ function deserializeSession(json) {
|
|
|
4512
4629
|
})
|
|
4513
4630
|
});
|
|
4514
4631
|
const validated = yield* decodeSession(parsed).pipe(
|
|
4515
|
-
|
|
4632
|
+
Effect16.mapError(
|
|
4516
4633
|
(error) => new SessionStoreError({
|
|
4517
4634
|
message: `Session validation failed: ${String(error)}`,
|
|
4518
4635
|
operation: "get",
|
|
@@ -4523,8 +4640,8 @@ function deserializeSession(json) {
|
|
|
4523
4640
|
return validated;
|
|
4524
4641
|
});
|
|
4525
4642
|
}
|
|
4526
|
-
var
|
|
4527
|
-
create: (originalTask) =>
|
|
4643
|
+
var make5 = {
|
|
4644
|
+
create: (originalTask) => Effect16.gen(function* () {
|
|
4528
4645
|
const sessionsDir = join5(process.cwd(), SESSIONS_DIR);
|
|
4529
4646
|
yield* ensureDir4(sessionsDir);
|
|
4530
4647
|
const now = yield* DateTime5.now;
|
|
@@ -4538,7 +4655,7 @@ var make7 = {
|
|
|
4538
4655
|
completedTasks: []
|
|
4539
4656
|
};
|
|
4540
4657
|
const sessionPath = getSessionPath(sessionId);
|
|
4541
|
-
yield*
|
|
4658
|
+
yield* Effect16.tryPromise({
|
|
4542
4659
|
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
4543
4660
|
catch: (error) => new SessionStoreError({
|
|
4544
4661
|
message: `Failed to write session file: ${sessionPath}`,
|
|
@@ -4548,9 +4665,9 @@ var make7 = {
|
|
|
4548
4665
|
});
|
|
4549
4666
|
return session;
|
|
4550
4667
|
}),
|
|
4551
|
-
get: (sessionId) =>
|
|
4668
|
+
get: (sessionId) => Effect16.gen(function* () {
|
|
4552
4669
|
const sessionPath = getSessionPath(sessionId);
|
|
4553
|
-
const content = yield*
|
|
4670
|
+
const content = yield* Effect16.tryPromise({
|
|
4554
4671
|
try: () => readFile4(sessionPath, "utf-8"),
|
|
4555
4672
|
catch: (error) => new SessionStoreError({
|
|
4556
4673
|
message: `Failed to read session file: ${sessionPath}`,
|
|
@@ -4560,9 +4677,9 @@ var make7 = {
|
|
|
4560
4677
|
});
|
|
4561
4678
|
return yield* deserializeSession(content);
|
|
4562
4679
|
}),
|
|
4563
|
-
update: (sessionId, session) =>
|
|
4680
|
+
update: (sessionId, session) => Effect16.gen(function* () {
|
|
4564
4681
|
const sessionPath = getSessionPath(sessionId);
|
|
4565
|
-
yield*
|
|
4682
|
+
yield* Effect16.tryPromise({
|
|
4566
4683
|
try: () => writeFile4(sessionPath, serializeSession(session), "utf-8"),
|
|
4567
4684
|
catch: (error) => new SessionStoreError({
|
|
4568
4685
|
message: `Failed to update session file: ${sessionPath}`,
|
|
@@ -4572,17 +4689,17 @@ var make7 = {
|
|
|
4572
4689
|
});
|
|
4573
4690
|
})
|
|
4574
4691
|
};
|
|
4575
|
-
var
|
|
4692
|
+
var Live13 = Layer11.succeed(SessionStore, make5);
|
|
4576
4693
|
var FileSystemSession = {
|
|
4577
|
-
Live:
|
|
4694
|
+
Live: Live13
|
|
4578
4695
|
};
|
|
4579
4696
|
|
|
4580
4697
|
// src/layers/session/memory.ts
|
|
4581
4698
|
init_esm_shims();
|
|
4582
|
-
import { DateTime as DateTime6, Effect as
|
|
4699
|
+
import { DateTime as DateTime6, Effect as Effect17, Layer as Layer12, Ref as Ref7 } from "effect";
|
|
4583
4700
|
function createMemorySessionStore(stateRef, counterRef) {
|
|
4584
4701
|
return {
|
|
4585
|
-
create: (originalTask) =>
|
|
4702
|
+
create: (originalTask) => Effect17.gen(function* () {
|
|
4586
4703
|
const state = yield* Ref7.get(stateRef);
|
|
4587
4704
|
const counter = yield* Ref7.updateAndGet(counterRef, (n) => n + 1);
|
|
4588
4705
|
const sessionId = `test-session-${counter}`;
|
|
@@ -4598,11 +4715,11 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4598
4715
|
yield* Ref7.set(stateRef, state);
|
|
4599
4716
|
return session;
|
|
4600
4717
|
}),
|
|
4601
|
-
get: (sessionId) =>
|
|
4718
|
+
get: (sessionId) => Effect17.gen(function* () {
|
|
4602
4719
|
const state = yield* Ref7.get(stateRef);
|
|
4603
4720
|
const session = state.get(sessionId);
|
|
4604
4721
|
if (!session) {
|
|
4605
|
-
return yield*
|
|
4722
|
+
return yield* Effect17.fail(
|
|
4606
4723
|
new SessionStoreError({
|
|
4607
4724
|
message: `Session not found: ${sessionId}`,
|
|
4608
4725
|
operation: "get"
|
|
@@ -4611,10 +4728,10 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4611
4728
|
}
|
|
4612
4729
|
return session;
|
|
4613
4730
|
}),
|
|
4614
|
-
update: (sessionId, session) =>
|
|
4731
|
+
update: (sessionId, session) => Effect17.gen(function* () {
|
|
4615
4732
|
const state = yield* Ref7.get(stateRef);
|
|
4616
4733
|
if (!state.has(sessionId)) {
|
|
4617
|
-
return yield*
|
|
4734
|
+
return yield* Effect17.fail(
|
|
4618
4735
|
new SessionStoreError({
|
|
4619
4736
|
message: `Session not found: ${sessionId}`,
|
|
4620
4737
|
operation: "update"
|
|
@@ -4627,24 +4744,24 @@ function createMemorySessionStore(stateRef, counterRef) {
|
|
|
4627
4744
|
};
|
|
4628
4745
|
}
|
|
4629
4746
|
function layer6() {
|
|
4630
|
-
return
|
|
4747
|
+
return Layer12.effect(
|
|
4631
4748
|
SessionStore,
|
|
4632
|
-
|
|
4749
|
+
Effect17.gen(function* () {
|
|
4633
4750
|
const stateRef = yield* Ref7.make(/* @__PURE__ */ new Map());
|
|
4634
4751
|
const counterRef = yield* Ref7.make(0);
|
|
4635
4752
|
return createMemorySessionStore(stateRef, counterRef);
|
|
4636
4753
|
})
|
|
4637
4754
|
);
|
|
4638
4755
|
}
|
|
4639
|
-
var
|
|
4756
|
+
var Live14 = layer6();
|
|
4640
4757
|
var MemorySession = {
|
|
4641
|
-
Live:
|
|
4758
|
+
Live: Live14,
|
|
4642
4759
|
layer: layer6
|
|
4643
4760
|
};
|
|
4644
4761
|
|
|
4645
4762
|
// src/layers/signal/ferix-parser.ts
|
|
4646
4763
|
init_esm_shims();
|
|
4647
|
-
import { Effect as
|
|
4764
|
+
import { Effect as Effect18, Layer as Layer13, Ref as Ref8 } from "effect";
|
|
4648
4765
|
|
|
4649
4766
|
// src/services/signal-parser.ts
|
|
4650
4767
|
init_esm_shims();
|
|
@@ -5154,10 +5271,10 @@ signalSpecRegistry.register(tasksDefinedSpec);
|
|
|
5154
5271
|
// src/layers/signal/ferix-parser.ts
|
|
5155
5272
|
var MAX_BUFFER_SIZE = 1024 * 1024;
|
|
5156
5273
|
function createAccumulatorImpl() {
|
|
5157
|
-
return
|
|
5274
|
+
return Effect18.gen(function* () {
|
|
5158
5275
|
const chunksRef = yield* Ref8.make([]);
|
|
5159
5276
|
const emittedRef = yield* Ref8.make(/* @__PURE__ */ new Set());
|
|
5160
|
-
const feed = (text) =>
|
|
5277
|
+
const feed = (text) => Effect18.gen(function* () {
|
|
5161
5278
|
const chunks = yield* Ref8.get(chunksRef);
|
|
5162
5279
|
chunks.push(text);
|
|
5163
5280
|
const buffer = chunks.join("");
|
|
@@ -5185,7 +5302,7 @@ function createAccumulatorImpl() {
|
|
|
5185
5302
|
yield* Ref8.set(emittedRef, emitted);
|
|
5186
5303
|
return newSignals;
|
|
5187
5304
|
});
|
|
5188
|
-
const flush = () =>
|
|
5305
|
+
const flush = () => Effect18.gen(function* () {
|
|
5189
5306
|
const chunks = yield* Ref8.get(chunksRef);
|
|
5190
5307
|
const buffer = chunks.join("");
|
|
5191
5308
|
yield* Ref8.set(chunksRef, []);
|
|
@@ -5200,41 +5317,17 @@ function createAccumulatorImpl() {
|
|
|
5200
5317
|
return { feed, flush };
|
|
5201
5318
|
});
|
|
5202
5319
|
}
|
|
5203
|
-
var
|
|
5204
|
-
parse: (text) =>
|
|
5320
|
+
var make6 = {
|
|
5321
|
+
parse: (text) => Effect18.succeed(signalSpecRegistry.parseAll(text)),
|
|
5205
5322
|
createAccumulator: createAccumulatorImpl
|
|
5206
5323
|
};
|
|
5207
|
-
var
|
|
5324
|
+
var Live15 = Layer13.succeed(SignalParser, make6);
|
|
5208
5325
|
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
|
-
}
|
|
5326
|
+
Live: Live15
|
|
5234
5327
|
};
|
|
5235
5328
|
|
|
5236
5329
|
// src/layers/index.ts
|
|
5237
|
-
var ProductionLayers =
|
|
5330
|
+
var ProductionLayers = Layer14.mergeAll(
|
|
5238
5331
|
ClaudeCLI.Live,
|
|
5239
5332
|
FerixParser.Live,
|
|
5240
5333
|
FileSystemPlan.Live,
|
|
@@ -5244,8 +5337,8 @@ var ProductionLayers = Layer15.mergeAll(
|
|
|
5244
5337
|
FileSystemGit.Live
|
|
5245
5338
|
);
|
|
5246
5339
|
function createProductionLayers(provider = "claude") {
|
|
5247
|
-
const llmLayer =
|
|
5248
|
-
return
|
|
5340
|
+
const llmLayer = createProviderLayer2(provider);
|
|
5341
|
+
return Layer14.mergeAll(
|
|
5249
5342
|
llmLayer,
|
|
5250
5343
|
FerixParser.Live,
|
|
5251
5344
|
FileSystemPlan.Live,
|
|
@@ -5255,7 +5348,7 @@ function createProductionLayers(provider = "claude") {
|
|
|
5255
5348
|
FileSystemGit.Live
|
|
5256
5349
|
);
|
|
5257
5350
|
}
|
|
5258
|
-
var TestLayers =
|
|
5351
|
+
var TestLayers = Layer14.mergeAll(
|
|
5259
5352
|
Mock.Live,
|
|
5260
5353
|
FerixParser.Live,
|
|
5261
5354
|
MemoryPlan.Live,
|
|
@@ -5265,7 +5358,7 @@ var TestLayers = Layer15.mergeAll(
|
|
|
5265
5358
|
MemoryGit.Live
|
|
5266
5359
|
);
|
|
5267
5360
|
function createTestLayers(events) {
|
|
5268
|
-
return
|
|
5361
|
+
return Layer14.mergeAll(
|
|
5269
5362
|
Mock.layer({ events }),
|
|
5270
5363
|
FerixParser.Live,
|
|
5271
5364
|
MemoryPlan.layer(),
|
|
@@ -5281,27 +5374,27 @@ init_esm_shims();
|
|
|
5281
5374
|
|
|
5282
5375
|
// src/orchestrator/loop.ts
|
|
5283
5376
|
init_esm_shims();
|
|
5284
|
-
import { DateTime as DateTime10, Effect as
|
|
5377
|
+
import { DateTime as DateTime10, Effect as Effect23, Option, pipe as pipe3, Ref as Ref12, Stream as Stream9 } from "effect";
|
|
5285
5378
|
|
|
5286
5379
|
// src/orchestrator/discovery.ts
|
|
5287
5380
|
init_esm_shims();
|
|
5288
|
-
import { DateTime as DateTime8, Effect as
|
|
5381
|
+
import { DateTime as DateTime8, Effect as Effect21, pipe, Ref as Ref10, Stream as Stream7 } from "effect";
|
|
5289
5382
|
|
|
5290
5383
|
// src/layers/plan/task-generation.ts
|
|
5291
5384
|
init_esm_shims();
|
|
5292
5385
|
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
5293
5386
|
import { join as join6 } from "path";
|
|
5294
|
-
import { Effect as
|
|
5387
|
+
import { Effect as Effect19 } from "effect";
|
|
5295
5388
|
var PLANS_DIR4 = ".ferix/plans";
|
|
5296
5389
|
function ensureDir5(dirPath) {
|
|
5297
|
-
return
|
|
5390
|
+
return Effect19.tryPromise({
|
|
5298
5391
|
try: () => mkdir6(dirPath, { recursive: true }),
|
|
5299
5392
|
catch: (error) => new PlanStoreError({
|
|
5300
5393
|
message: `Failed to create directory: ${dirPath}`,
|
|
5301
5394
|
operation: "create",
|
|
5302
5395
|
cause: error
|
|
5303
5396
|
})
|
|
5304
|
-
}).pipe(
|
|
5397
|
+
}).pipe(Effect19.asVoid);
|
|
5305
5398
|
}
|
|
5306
5399
|
function getSessionDir4(sessionId) {
|
|
5307
5400
|
return join6(process.cwd(), PLANS_DIR4, sessionId);
|
|
@@ -5310,12 +5403,12 @@ function getTasksMdPath(sessionId) {
|
|
|
5310
5403
|
return join6(getSessionDir4(sessionId), "tasks.md");
|
|
5311
5404
|
}
|
|
5312
5405
|
function writeTasksMd(sessionId, tasks) {
|
|
5313
|
-
return
|
|
5406
|
+
return Effect19.gen(function* () {
|
|
5314
5407
|
const sessionDir = getSessionDir4(sessionId);
|
|
5315
5408
|
yield* ensureDir5(sessionDir);
|
|
5316
5409
|
const tasksMdPath = getTasksMdPath(sessionId);
|
|
5317
5410
|
const content = formatTasksMd(tasks);
|
|
5318
|
-
yield*
|
|
5411
|
+
yield* Effect19.tryPromise({
|
|
5319
5412
|
try: () => writeFile5(tasksMdPath, content, "utf-8"),
|
|
5320
5413
|
catch: (error) => new PlanStoreError({
|
|
5321
5414
|
message: `Failed to write tasks.md: ${tasksMdPath}`,
|
|
@@ -5497,7 +5590,7 @@ function mapSignalToDomain(signal, context) {
|
|
|
5497
5590
|
|
|
5498
5591
|
// src/orchestrator/plan-updates.ts
|
|
5499
5592
|
init_esm_shims();
|
|
5500
|
-
import { DateTime as DateTime7, Effect as
|
|
5593
|
+
import { DateTime as DateTime7, Effect as Effect20, Ref as Ref9 } from "effect";
|
|
5501
5594
|
|
|
5502
5595
|
// src/orchestrator/plan-updates/index.ts
|
|
5503
5596
|
init_esm_shims();
|
|
@@ -5802,9 +5895,9 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
5802
5895
|
tasks: plan.tasks
|
|
5803
5896
|
}) : planStore.update(plan.id, plan);
|
|
5804
5897
|
return storeOp.pipe(
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
(error) =>
|
|
5898
|
+
Effect20.map(() => null),
|
|
5899
|
+
Effect20.catchAll(
|
|
5900
|
+
(error) => Effect20.succeed({
|
|
5808
5901
|
_tag: "PlanUpdateFailed",
|
|
5809
5902
|
operation,
|
|
5810
5903
|
error: error.message,
|
|
@@ -5814,7 +5907,7 @@ function persistPlanUpdate(planStore, plan, operation) {
|
|
|
5814
5907
|
);
|
|
5815
5908
|
}
|
|
5816
5909
|
function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessionId, originalTask) {
|
|
5817
|
-
return
|
|
5910
|
+
return Effect20.gen(function* () {
|
|
5818
5911
|
const currentPlan = yield* Ref9.get(currentPlanRef);
|
|
5819
5912
|
const now = yield* DateTime7.now;
|
|
5820
5913
|
const timestamp = DateTime7.formatIso(now);
|
|
@@ -5836,7 +5929,7 @@ function updatePlanFromSignal(currentPlanRef, persistenceStateRef, signal, sessi
|
|
|
5836
5929
|
});
|
|
5837
5930
|
}
|
|
5838
5931
|
function flushPlanPersistence(planStore, currentPlanRef, persistenceStateRef) {
|
|
5839
|
-
return
|
|
5932
|
+
return Effect20.gen(function* () {
|
|
5840
5933
|
const state = yield* Ref9.get(persistenceStateRef);
|
|
5841
5934
|
if (!(state.dirty && state.pendingOperation)) {
|
|
5842
5935
|
return [];
|
|
@@ -6110,12 +6203,12 @@ Begin.`);
|
|
|
6110
6203
|
|
|
6111
6204
|
// src/orchestrator/discovery.ts
|
|
6112
6205
|
function processTextSignals(signalParser, text, context) {
|
|
6113
|
-
return
|
|
6206
|
+
return Effect21.gen(function* () {
|
|
6114
6207
|
const events = [];
|
|
6115
6208
|
const parsedSignals = [];
|
|
6116
6209
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6117
|
-
|
|
6118
|
-
(error) =>
|
|
6210
|
+
Effect21.tapError(
|
|
6211
|
+
(error) => Effect21.logDebug(
|
|
6119
6212
|
"Signal parsing failed, continuing with empty signals",
|
|
6120
6213
|
{
|
|
6121
6214
|
error: String(error),
|
|
@@ -6123,7 +6216,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6123
6216
|
}
|
|
6124
6217
|
)
|
|
6125
6218
|
),
|
|
6126
|
-
|
|
6219
|
+
Effect21.orElseSucceed(() => [])
|
|
6127
6220
|
);
|
|
6128
6221
|
for (const signal of signals) {
|
|
6129
6222
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6133,7 +6226,7 @@ function processTextSignals(signalParser, text, context) {
|
|
|
6133
6226
|
});
|
|
6134
6227
|
}
|
|
6135
6228
|
function processLLMEvent(signalParser, llmEvent, context) {
|
|
6136
|
-
return
|
|
6229
|
+
return Effect21.gen(function* () {
|
|
6137
6230
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6138
6231
|
const events = [domainEvent];
|
|
6139
6232
|
const allSignals = [];
|
|
@@ -6177,8 +6270,8 @@ function planTasksToGeneratedTasks(plan) {
|
|
|
6177
6270
|
}));
|
|
6178
6271
|
}
|
|
6179
6272
|
function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, config, sessionId, worktreePath) {
|
|
6180
|
-
return
|
|
6181
|
-
|
|
6273
|
+
return Stream7.unwrap(
|
|
6274
|
+
Effect21.gen(function* () {
|
|
6182
6275
|
const startTimeUtc = yield* DateTime8.now;
|
|
6183
6276
|
const startTime = DateTime8.toEpochMillis(startTimeUtc);
|
|
6184
6277
|
const persistenceStateRef = yield* Ref10.make({
|
|
@@ -6196,16 +6289,16 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6196
6289
|
};
|
|
6197
6290
|
const prompt = buildDiscoveryPrompt(config);
|
|
6198
6291
|
const llmStream = llm.execute(prompt, worktreePath ? { cwd: worktreePath } : void 0).pipe(
|
|
6199
|
-
|
|
6292
|
+
Stream7.mapError(
|
|
6200
6293
|
(e) => new OrchestratorError({
|
|
6201
6294
|
message: `LLM execution failed during discovery: ${String(e)}`,
|
|
6202
6295
|
phase: "discovery",
|
|
6203
6296
|
cause: e
|
|
6204
6297
|
})
|
|
6205
6298
|
),
|
|
6206
|
-
|
|
6207
|
-
(llmEvent) =>
|
|
6208
|
-
|
|
6299
|
+
Stream7.flatMap(
|
|
6300
|
+
(llmEvent) => Stream7.unwrap(
|
|
6301
|
+
Effect21.gen(function* () {
|
|
6209
6302
|
const now = yield* DateTime8.now;
|
|
6210
6303
|
const context = {
|
|
6211
6304
|
timestamp: DateTime8.toEpochMillis(now)
|
|
@@ -6226,13 +6319,13 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6226
6319
|
);
|
|
6227
6320
|
events.push(...planEvents);
|
|
6228
6321
|
}
|
|
6229
|
-
return
|
|
6322
|
+
return Stream7.fromIterable(events);
|
|
6230
6323
|
})
|
|
6231
6324
|
)
|
|
6232
6325
|
),
|
|
6233
6326
|
// Convert LLM errors to LoopFailed events
|
|
6234
|
-
|
|
6235
|
-
(error) =>
|
|
6327
|
+
Stream7.catchAll(
|
|
6328
|
+
(error) => Stream7.succeed({
|
|
6236
6329
|
_tag: "LoopFailed",
|
|
6237
6330
|
error: {
|
|
6238
6331
|
message: error.message,
|
|
@@ -6241,8 +6334,8 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6241
6334
|
})
|
|
6242
6335
|
)
|
|
6243
6336
|
);
|
|
6244
|
-
const completionStream =
|
|
6245
|
-
|
|
6337
|
+
const completionStream = Stream7.fromEffect(
|
|
6338
|
+
Effect21.gen(function* () {
|
|
6246
6339
|
const persistEvents = yield* flushPlanPersistence(
|
|
6247
6340
|
planStore,
|
|
6248
6341
|
currentPlanRef,
|
|
@@ -6253,12 +6346,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6253
6346
|
if (plan && plan.tasks.length > 0) {
|
|
6254
6347
|
const generatedTasks = planTasksToGeneratedTasks(plan);
|
|
6255
6348
|
yield* writeTasksMd(sessionId, generatedTasks).pipe(
|
|
6256
|
-
|
|
6257
|
-
(error) =>
|
|
6349
|
+
Effect21.tapError(
|
|
6350
|
+
(error) => Effect21.logDebug("Failed to write tasks.md, continuing", {
|
|
6258
6351
|
error: String(error)
|
|
6259
6352
|
})
|
|
6260
6353
|
),
|
|
6261
|
-
|
|
6354
|
+
Effect21.orElseSucceed(() => void 0)
|
|
6262
6355
|
);
|
|
6263
6356
|
}
|
|
6264
6357
|
const endTimeUtc = yield* DateTime8.now;
|
|
@@ -6270,12 +6363,12 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6270
6363
|
};
|
|
6271
6364
|
return [...persistEvents, discoveryCompleted];
|
|
6272
6365
|
})
|
|
6273
|
-
).pipe(
|
|
6366
|
+
).pipe(Stream7.flatMap((events) => Stream7.fromIterable(events)));
|
|
6274
6367
|
return pipe(
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6368
|
+
Stream7.succeed(discoveryStarted),
|
|
6369
|
+
Stream7.concat(Stream7.succeed(analysingToolUse)),
|
|
6370
|
+
Stream7.concat(llmStream),
|
|
6371
|
+
Stream7.concat(completionStream)
|
|
6279
6372
|
);
|
|
6280
6373
|
})
|
|
6281
6374
|
);
|
|
@@ -6283,15 +6376,15 @@ function createDiscoveryStream(llm, signalParser, planStore, currentPlanRef, con
|
|
|
6283
6376
|
|
|
6284
6377
|
// src/orchestrator/iteration.ts
|
|
6285
6378
|
init_esm_shims();
|
|
6286
|
-
import { DateTime as DateTime9, Effect as
|
|
6379
|
+
import { DateTime as DateTime9, Effect as Effect22, pipe as pipe2, Ref as Ref11, Stream as Stream8 } from "effect";
|
|
6287
6380
|
function processTextSignals2(signalParser, text, context) {
|
|
6288
|
-
return
|
|
6381
|
+
return Effect22.gen(function* () {
|
|
6289
6382
|
const events = [];
|
|
6290
6383
|
let completed = false;
|
|
6291
6384
|
const parsedSignals = [];
|
|
6292
6385
|
const signals = yield* signalParser.parse(text).pipe(
|
|
6293
|
-
|
|
6294
|
-
(error) =>
|
|
6386
|
+
Effect22.tapError(
|
|
6387
|
+
(error) => Effect22.logDebug(
|
|
6295
6388
|
"Signal parsing failed, continuing with empty signals",
|
|
6296
6389
|
{
|
|
6297
6390
|
error: String(error),
|
|
@@ -6299,7 +6392,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6299
6392
|
}
|
|
6300
6393
|
)
|
|
6301
6394
|
),
|
|
6302
|
-
|
|
6395
|
+
Effect22.orElseSucceed(() => [])
|
|
6303
6396
|
);
|
|
6304
6397
|
for (const signal of signals) {
|
|
6305
6398
|
events.push(mapSignalToDomain(signal, context));
|
|
@@ -6312,7 +6405,7 @@ function processTextSignals2(signalParser, text, context) {
|
|
|
6312
6405
|
});
|
|
6313
6406
|
}
|
|
6314
6407
|
function processLLMEvent2(signalParser, llmEvent, context) {
|
|
6315
|
-
return
|
|
6408
|
+
return Effect22.gen(function* () {
|
|
6316
6409
|
const domainEvent = mapLLMEventToDomain(llmEvent, context);
|
|
6317
6410
|
const events = [domainEvent];
|
|
6318
6411
|
let completed = false;
|
|
@@ -6344,8 +6437,8 @@ function processLLMEvent2(signalParser, llmEvent, context) {
|
|
|
6344
6437
|
});
|
|
6345
6438
|
}
|
|
6346
6439
|
function createIterationStream(llm, signalParser, planStore, currentPlanRef, loopCompletedRef, config, iteration, sessionId, worktreePath) {
|
|
6347
|
-
return
|
|
6348
|
-
|
|
6440
|
+
return Stream8.unwrap(
|
|
6441
|
+
Effect22.gen(function* () {
|
|
6349
6442
|
const currentPlan = yield* Ref11.get(currentPlanRef);
|
|
6350
6443
|
const persistenceStateRef = yield* Ref11.make({
|
|
6351
6444
|
dirty: false,
|
|
@@ -6359,16 +6452,16 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6359
6452
|
buildPrompt(config, iteration, currentPlan),
|
|
6360
6453
|
worktreePath ? { cwd: worktreePath } : void 0
|
|
6361
6454
|
).pipe(
|
|
6362
|
-
|
|
6455
|
+
Stream8.mapError(
|
|
6363
6456
|
(e) => new OrchestratorError({
|
|
6364
6457
|
message: `LLM execution failed: ${String(e)}`,
|
|
6365
6458
|
phase: "iteration",
|
|
6366
6459
|
cause: e
|
|
6367
6460
|
})
|
|
6368
6461
|
),
|
|
6369
|
-
|
|
6370
|
-
(llmEvent) =>
|
|
6371
|
-
|
|
6462
|
+
Stream8.flatMap(
|
|
6463
|
+
(llmEvent) => Stream8.unwrap(
|
|
6464
|
+
Effect22.gen(function* () {
|
|
6372
6465
|
const now = yield* DateTime9.now;
|
|
6373
6466
|
const context = {
|
|
6374
6467
|
timestamp: DateTime9.toEpochMillis(now)
|
|
@@ -6392,13 +6485,13 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6392
6485
|
if (result.completed) {
|
|
6393
6486
|
yield* Ref11.set(loopCompletedRef, true);
|
|
6394
6487
|
}
|
|
6395
|
-
return
|
|
6488
|
+
return Stream8.fromIterable(events);
|
|
6396
6489
|
})
|
|
6397
6490
|
)
|
|
6398
6491
|
),
|
|
6399
6492
|
// Convert LLM errors to LoopFailed events with iteration context
|
|
6400
|
-
|
|
6401
|
-
(error) =>
|
|
6493
|
+
Stream8.catchAll(
|
|
6494
|
+
(error) => Stream8.succeed({
|
|
6402
6495
|
_tag: "LoopFailed",
|
|
6403
6496
|
error: {
|
|
6404
6497
|
message: error.message,
|
|
@@ -6408,8 +6501,8 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6408
6501
|
})
|
|
6409
6502
|
)
|
|
6410
6503
|
);
|
|
6411
|
-
const completionStream =
|
|
6412
|
-
|
|
6504
|
+
const completionStream = Stream8.fromEffect(
|
|
6505
|
+
Effect22.gen(function* () {
|
|
6413
6506
|
const persistEvents = yield* flushPlanPersistence(
|
|
6414
6507
|
planStore,
|
|
6415
6508
|
currentPlanRef,
|
|
@@ -6421,11 +6514,11 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6421
6514
|
};
|
|
6422
6515
|
return [...persistEvents, iterCompleted];
|
|
6423
6516
|
})
|
|
6424
|
-
).pipe(
|
|
6517
|
+
).pipe(Stream8.flatMap((events) => Stream8.fromIterable(events)));
|
|
6425
6518
|
return pipe2(
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6519
|
+
Stream8.succeed(iterStarted),
|
|
6520
|
+
Stream8.concat(llmStream),
|
|
6521
|
+
Stream8.concat(completionStream)
|
|
6429
6522
|
);
|
|
6430
6523
|
})
|
|
6431
6524
|
);
|
|
@@ -6433,15 +6526,15 @@ function createIterationStream(llm, signalParser, planStore, currentPlanRef, loo
|
|
|
6433
6526
|
|
|
6434
6527
|
// src/orchestrator/loop.ts
|
|
6435
6528
|
function runLoop(config) {
|
|
6436
|
-
return
|
|
6437
|
-
|
|
6529
|
+
return Stream9.unwrap(
|
|
6530
|
+
Effect23.gen(function* () {
|
|
6438
6531
|
const llm = yield* LLM;
|
|
6439
6532
|
const signalParser = yield* SignalParser;
|
|
6440
6533
|
const sessionStore = yield* SessionStore;
|
|
6441
6534
|
const planStore = yield* PlanStore;
|
|
6442
6535
|
const git = yield* Git;
|
|
6443
6536
|
const session = yield* sessionStore.create(config.task).pipe(
|
|
6444
|
-
|
|
6537
|
+
Effect23.mapError(
|
|
6445
6538
|
(e) => new OrchestratorError({
|
|
6446
6539
|
message: `Failed to create session: ${e.message}`,
|
|
6447
6540
|
phase: "setup",
|
|
@@ -6450,7 +6543,7 @@ function runLoop(config) {
|
|
|
6450
6543
|
)
|
|
6451
6544
|
);
|
|
6452
6545
|
const worktreePath = yield* git.createWorktree(session.id).pipe(
|
|
6453
|
-
|
|
6546
|
+
Effect23.mapError(
|
|
6454
6547
|
(e) => new OrchestratorError({
|
|
6455
6548
|
message: `Failed to create worktree: ${e.message}`,
|
|
6456
6549
|
phase: "setup",
|
|
@@ -6464,12 +6557,12 @@ function runLoop(config) {
|
|
|
6464
6557
|
worktreePath,
|
|
6465
6558
|
branchName
|
|
6466
6559
|
}).pipe(
|
|
6467
|
-
|
|
6468
|
-
(error) =>
|
|
6560
|
+
Effect23.tapError(
|
|
6561
|
+
(error) => Effect23.logDebug("Failed to update session with worktree info", {
|
|
6469
6562
|
error: String(error)
|
|
6470
6563
|
})
|
|
6471
6564
|
),
|
|
6472
|
-
|
|
6565
|
+
Effect23.orElseSucceed(() => void 0)
|
|
6473
6566
|
);
|
|
6474
6567
|
const startTimeUtc = yield* DateTime10.now;
|
|
6475
6568
|
const startTime = DateTime10.toEpochMillis(startTimeUtc);
|
|
@@ -6497,9 +6590,9 @@ function runLoop(config) {
|
|
|
6497
6590
|
session.id,
|
|
6498
6591
|
worktreePath
|
|
6499
6592
|
);
|
|
6500
|
-
const iterationsStream =
|
|
6593
|
+
const iterationsStream = Stream9.unfoldEffect(
|
|
6501
6594
|
1,
|
|
6502
|
-
(iteration) =>
|
|
6595
|
+
(iteration) => Effect23.gen(function* () {
|
|
6503
6596
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
6504
6597
|
if (completed || iteration > maxIterations) {
|
|
6505
6598
|
return Option.none();
|
|
@@ -6507,7 +6600,7 @@ function runLoop(config) {
|
|
|
6507
6600
|
return Option.some([iteration, iteration + 1]);
|
|
6508
6601
|
})
|
|
6509
6602
|
).pipe(
|
|
6510
|
-
|
|
6603
|
+
Stream9.flatMap(
|
|
6511
6604
|
(iteration) => createIterationStream(
|
|
6512
6605
|
llm,
|
|
6513
6606
|
signalParser,
|
|
@@ -6531,17 +6624,17 @@ function runLoop(config) {
|
|
|
6531
6624
|
worktreePath
|
|
6532
6625
|
);
|
|
6533
6626
|
return pipe3(
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6627
|
+
Stream9.succeed(loopStarted),
|
|
6628
|
+
Stream9.concat(Stream9.succeed(worktreeCreated)),
|
|
6629
|
+
Stream9.concat(discoveryStream),
|
|
6630
|
+
Stream9.concat(iterationsStream),
|
|
6631
|
+
Stream9.concat(completionStream)
|
|
6539
6632
|
);
|
|
6540
6633
|
}).pipe(
|
|
6541
6634
|
// Also catch setup errors (e.g., session creation failure)
|
|
6542
|
-
|
|
6543
|
-
(error) =>
|
|
6544
|
-
|
|
6635
|
+
Effect23.catchAll(
|
|
6636
|
+
(error) => Effect23.succeed(
|
|
6637
|
+
Stream9.succeed({
|
|
6545
6638
|
_tag: "LoopFailed",
|
|
6546
6639
|
error: {
|
|
6547
6640
|
message: error.message,
|
|
@@ -6554,19 +6647,19 @@ function runLoop(config) {
|
|
|
6554
6647
|
);
|
|
6555
6648
|
}
|
|
6556
6649
|
function createCompletionStream(sessionStore, git, session, config, startTime, loopCompletedRef, _worktreePath) {
|
|
6557
|
-
return
|
|
6558
|
-
|
|
6650
|
+
return Stream9.unwrap(
|
|
6651
|
+
Effect23.gen(function* () {
|
|
6559
6652
|
const endTimeUtc = yield* DateTime10.now;
|
|
6560
6653
|
const durationMs = DateTime10.toEpochMillis(endTimeUtc) - startTime;
|
|
6561
6654
|
const completed = yield* Ref12.get(loopCompletedRef);
|
|
6562
6655
|
yield* git.commitChanges(session.id, `feat: complete session ${session.id}`).pipe(
|
|
6563
|
-
|
|
6564
|
-
(error) =>
|
|
6656
|
+
Effect23.tapError(
|
|
6657
|
+
(error) => Effect23.logDebug("Final commit failed, continuing", {
|
|
6565
6658
|
sessionId: session.id,
|
|
6566
6659
|
error: String(error)
|
|
6567
6660
|
})
|
|
6568
6661
|
),
|
|
6569
|
-
|
|
6662
|
+
Effect23.orElseSucceed(() => void 0)
|
|
6570
6663
|
);
|
|
6571
6664
|
const summary = {
|
|
6572
6665
|
iterations: config.maxIterations,
|
|
@@ -6579,16 +6672,16 @@ function createCompletionStream(sessionStore, git, session, config, startTime, l
|
|
|
6579
6672
|
...session,
|
|
6580
6673
|
status: completed ? "completed" : "paused"
|
|
6581
6674
|
}).pipe(
|
|
6582
|
-
|
|
6583
|
-
(error) =>
|
|
6675
|
+
Effect23.tapError(
|
|
6676
|
+
(error) => Effect23.logDebug("Session update failed, continuing", {
|
|
6584
6677
|
sessionId: session.id,
|
|
6585
6678
|
error: String(error)
|
|
6586
6679
|
})
|
|
6587
6680
|
),
|
|
6588
|
-
|
|
6681
|
+
Effect23.orElseSucceed(() => void 0)
|
|
6589
6682
|
);
|
|
6590
6683
|
const loopCompleted = { _tag: "LoopCompleted", summary };
|
|
6591
|
-
return
|
|
6684
|
+
return Stream9.succeed(loopCompleted);
|
|
6592
6685
|
})
|
|
6593
6686
|
);
|
|
6594
6687
|
}
|
|
@@ -6597,11 +6690,11 @@ function createCompletionStream(sessionStore, git, session, config, startTime, l
|
|
|
6597
6690
|
function run(options) {
|
|
6598
6691
|
const { config, consumer: consumerType = "headless", onEvent } = options;
|
|
6599
6692
|
const events = runLoop(config);
|
|
6600
|
-
const eventsWithCallback = onEvent ? events.pipe(
|
|
6693
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect24.sync(() => onEvent(event)))) : events;
|
|
6601
6694
|
const layers = config.provider ? createProductionLayers(config.provider) : ProductionLayers;
|
|
6602
|
-
const eventsWithLayers = eventsWithCallback.pipe(
|
|
6695
|
+
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
6603
6696
|
if (consumerType === "none") {
|
|
6604
|
-
return eventsWithLayers.pipe(
|
|
6697
|
+
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
6605
6698
|
}
|
|
6606
6699
|
const consumer = consumerType === "tui" ? createTUIConsumer() : createHeadlessConsumer();
|
|
6607
6700
|
return consumer.consume(eventsWithLayers);
|
|
@@ -6609,19 +6702,19 @@ function run(options) {
|
|
|
6609
6702
|
function runTest(options, mockEvents) {
|
|
6610
6703
|
const { config, onEvent } = options;
|
|
6611
6704
|
const events = runLoop(config);
|
|
6612
|
-
const eventsWithCallback = onEvent ? events.pipe(
|
|
6705
|
+
const eventsWithCallback = onEvent ? events.pipe(Stream10.tap((event) => Effect24.sync(() => onEvent(event)))) : events;
|
|
6613
6706
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
6614
|
-
const eventsWithLayers = eventsWithCallback.pipe(
|
|
6615
|
-
return eventsWithLayers.pipe(
|
|
6707
|
+
const eventsWithLayers = eventsWithCallback.pipe(Stream10.provideLayer(layers));
|
|
6708
|
+
return eventsWithLayers.pipe(Stream10.runDrain);
|
|
6616
6709
|
}
|
|
6617
6710
|
function collectEvents(config, mockEvents) {
|
|
6618
6711
|
const events = runLoop(config);
|
|
6619
6712
|
const layers = mockEvents ? createTestLayers(mockEvents) : TestLayers;
|
|
6620
|
-
return events.pipe(
|
|
6713
|
+
return events.pipe(Stream10.provideLayer(layers), Stream10.runCollect).pipe(Effect24.map((chunk) => Array.from(chunk)));
|
|
6621
6714
|
}
|
|
6622
6715
|
function main(config) {
|
|
6623
6716
|
const consumerType = process.stdout.isTTY ? "tui" : "headless";
|
|
6624
|
-
return run({ config, consumer: consumerType }).pipe(
|
|
6717
|
+
return run({ config, consumer: consumerType }).pipe(Effect24.runPromise);
|
|
6625
6718
|
}
|
|
6626
6719
|
|
|
6627
6720
|
// src/services/index.ts
|
|
@@ -6796,7 +6889,7 @@ export {
|
|
|
6796
6889
|
collectEvents,
|
|
6797
6890
|
createHeadlessConsumer,
|
|
6798
6891
|
createProductionLayers,
|
|
6799
|
-
createProviderLayer,
|
|
6892
|
+
createProviderLayer2 as createProviderLayer,
|
|
6800
6893
|
createTUIConsumer,
|
|
6801
6894
|
createTestLayers,
|
|
6802
6895
|
decodeGuardrail,
|