@llm4ts/flow 0.1.3 → 0.2.0
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/Chat.d.ts +10 -0
- package/dist/Chat.d.ts.map +1 -1
- package/dist/Chat.js +9 -0
- package/dist/Chat.js.map +1 -1
- package/dist/Flow.d.ts +7 -0
- package/dist/Flow.d.ts.map +1 -1
- package/dist/Flow.js +62 -10
- package/dist/Flow.js.map +1 -1
- package/dist/PlanExecution.d.ts +1 -1
- package/dist/PlanExecution.d.ts.map +1 -1
- package/dist/PlanExecution.js +1 -1
- package/dist/PlanExecution.js.map +1 -1
- package/package.json +2 -2
- package/src/Chat.ts +23 -1
- package/src/Flow.ts +109 -38
- package/src/PlanExecution.ts +2 -2
package/dist/Chat.d.ts
CHANGED
|
@@ -2,10 +2,20 @@ import * as Effect from "effect/Effect";
|
|
|
2
2
|
import type { LlmServiceShape } from "@llm4ts/core/LlmService";
|
|
3
3
|
import { Message } from "@llm4ts/core/Models";
|
|
4
4
|
import { FlowLlmError } from "./FlowError.ts";
|
|
5
|
+
import { type FlowEventsShape } from "./FlowEvents.ts";
|
|
5
6
|
export declare const gitOwnershipInstruction = "The flow runtime owns git operations. Do not create or switch branches, commit, push, or open pull requests.";
|
|
6
7
|
export interface ChatOptions {
|
|
7
8
|
readonly system?: string;
|
|
8
9
|
readonly manageGit?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* When set, every reply whose collected response carries token usage is
|
|
12
|
+
* published as a `TokensUsed` event, which is what `CostTracker` (and
|
|
13
|
+
* therefore cost summaries and budgets) consume. Without it, usage
|
|
14
|
+
* reported by the backend is silently discarded.
|
|
15
|
+
*/
|
|
16
|
+
readonly events?: FlowEventsShape;
|
|
17
|
+
/** Request label for published `TokensUsed` events. Default: "chat". */
|
|
18
|
+
readonly agent?: string;
|
|
9
19
|
}
|
|
10
20
|
export interface Chat {
|
|
11
21
|
readonly ask: (prompt: string) => Effect.Effect<string, FlowLlmError>;
|
package/dist/Chat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../src/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../src/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAoB,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAElE,eAAO,MAAM,uBAAuB,iHAC4E,CAAA;AAEhH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAA;IACjC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAaD,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;CACzD;AAcD,eAAO,MAAM,QAAQ,oGA2BnB,CAAA"}
|
package/dist/Chat.js
CHANGED
|
@@ -4,7 +4,15 @@ import * as Semaphore from "effect/Semaphore";
|
|
|
4
4
|
import { Message } from "@llm4ts/core/Models";
|
|
5
5
|
import { collect } from "@llm4ts/core/Streaming";
|
|
6
6
|
import { FlowLlmError } from "./FlowError.js";
|
|
7
|
+
import { TokensUsed } from "./FlowEvents.js";
|
|
7
8
|
export const gitOwnershipInstruction = "The flow runtime owns git operations. Do not create or switch branches, commit, push, or open pull requests.";
|
|
9
|
+
const publishUsage = (options, response) => options.events === undefined || response.usage === undefined
|
|
10
|
+
? Effect.void
|
|
11
|
+
: options.events.publish(TokensUsed.make({
|
|
12
|
+
agent: options.agent ?? "chat",
|
|
13
|
+
usage: response.usage,
|
|
14
|
+
...(response.metadata.model === undefined ? {} : { model: response.metadata.model })
|
|
15
|
+
}));
|
|
8
16
|
const initialHistory = (options) => {
|
|
9
17
|
const system = options.manageGit === true
|
|
10
18
|
? options.system
|
|
@@ -22,6 +30,7 @@ export const makeChat = Effect.fn("@llm4ts/flow/Chat.make")(function* (service,
|
|
|
22
30
|
const userTurn = Message.make({ role: "User", content: prompt });
|
|
23
31
|
const messages = [...(yield* Ref.get(history)), userTurn];
|
|
24
32
|
const response = yield* collect(service.executeStreamWithHistory(messages)).pipe(Effect.mapError(FlowLlmError.from));
|
|
33
|
+
yield* publishUsage(options, response);
|
|
25
34
|
yield* Ref.set(history, [
|
|
26
35
|
...messages,
|
|
27
36
|
Message.make({ role: "Assistant", content: response.content })
|
package/dist/Chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.js","sourceRoot":"","sources":["../src/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAA;AACjC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"Chat.js","sourceRoot":"","sources":["../src/Chat.ts"],"names":[],"mappings":"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"}
|
package/dist/Flow.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ export interface ImplementPlanOptions {
|
|
|
14
14
|
readonly planPath: string;
|
|
15
15
|
readonly plan: Effect.Effect<Plan, FlowError>;
|
|
16
16
|
readonly system?: string;
|
|
17
|
+
/**
|
|
18
|
+
* When true, each task gets a fresh Chat (system prompt plus the plan's
|
|
19
|
+
* current render, showing prior tasks' completion status); that task's
|
|
20
|
+
* review-fix rounds share the same Chat. When false or omitted (default),
|
|
21
|
+
* one Chat is shared across every task in the plan.
|
|
22
|
+
*/
|
|
23
|
+
readonly chatPerTask?: boolean;
|
|
17
24
|
readonly reviewers?: ReadonlyArray<Reviewer>;
|
|
18
25
|
readonly commitMessage?: (plan: Plan, task: Task) => string;
|
|
19
26
|
readonly checkoutBranch?: boolean;
|
package/dist/Flow.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Flow.d.ts","sourceRoot":"","sources":["../src/Flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAG9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1E,OAAO,
|
|
1
|
+
{"version":3,"file":"Flow.d.ts","sourceRoot":"","sources":["../src/Flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAG9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1E,OAAO,EAAsC,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC1F,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEtD,OAAO,EAAsC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,eAAO,MAAM,YAAY,GAAI,SAAS,gBAAgB,KAAG,eACd,CAAA;AAE3C,eAAO,MAAM,kBAAkB,mHAmB7B,CAAA;AAEF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC5C,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IAC3D,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;CACjD;AAOD,eAAO,MAAM,iBAAiB,2gBAoG5B,CAAA"}
|
package/dist/Flow.js
CHANGED
|
@@ -2,32 +2,81 @@ import * as Effect from "effect/Effect";
|
|
|
2
2
|
import { collect } from "@llm4ts/core/Streaming";
|
|
3
3
|
import { makeChat } from "./Chat.js";
|
|
4
4
|
import { FlowAborted, FlowLlmError } from "./FlowError.js";
|
|
5
|
-
import { AssistantMessage, Info } from "./FlowEvents.js";
|
|
5
|
+
import { AssistantMessage, Info, TokensUsed } from "./FlowEvents.js";
|
|
6
6
|
import { implementTaskLoop, stage } from "./PlanExecution.js";
|
|
7
7
|
import { minimalReviewers, reviewAndFixLoop } from "./Review.js";
|
|
8
8
|
export const flowReviewer = (context) => context.reviewers[0] ?? context.reasoning;
|
|
9
9
|
export const completeAndPublish = Effect.fn("@llm4ts/flow/Flow.completeAndPublish")(function* (service, events, prompt) {
|
|
10
10
|
const response = yield* collect(service.executeStream(prompt)).pipe(Effect.mapError(FlowLlmError.from));
|
|
11
|
+
if (response.usage !== undefined) {
|
|
12
|
+
yield* events.publish(TokensUsed.make({
|
|
13
|
+
agent: "assistant",
|
|
14
|
+
usage: response.usage,
|
|
15
|
+
...(response.metadata.model === undefined ? {} : { model: response.metadata.model })
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
11
18
|
yield* events.publish(AssistantMessage.make({ text: response.content }));
|
|
12
19
|
return response.content;
|
|
13
20
|
});
|
|
14
21
|
const defaultCommitMessage = (plan, task) => `${plan.epicId}: ${task.title}`;
|
|
22
|
+
const composeSystem = (base, note) => [base, note].filter((part) => part !== undefined && part.length > 0).join("\n\n");
|
|
15
23
|
export const implementPlanFlow = Effect.fn("@llm4ts/flow/Flow.implementPlan")(function* (context, options) {
|
|
16
24
|
const plan = yield* options.store.recoverOrCreate(options.planPath, options.plan);
|
|
17
25
|
if (options.checkoutBranch !== false) {
|
|
18
26
|
yield* stage(context.events, "branch", context.git.checkoutOrCreate(plan.epicId));
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
let sharedCoder;
|
|
29
|
+
if (options.chatPerTask !== true) {
|
|
30
|
+
sharedCoder = yield* makeChat(context.coder, {
|
|
31
|
+
events: context.events,
|
|
32
|
+
agent: "coder",
|
|
33
|
+
...(options.system === undefined ? {} : { system: options.system })
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return yield* implementTaskLoop(options.store, context.events, options.planPath, plan, (task, planSoFar) => Effect.gen(function* () {
|
|
37
|
+
// `sharedCoder`'s definedness mirrors `options.chatPerTask !== true`
|
|
38
|
+
// above: when it's set, every task reuses it; when it's undefined,
|
|
39
|
+
// chatPerTask is active and each task builds its own fresh Chat.
|
|
40
|
+
let coder;
|
|
41
|
+
if (sharedCoder !== undefined) {
|
|
42
|
+
coder = sharedCoder;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
coder = yield* makeChat(context.coder, {
|
|
46
|
+
events: context.events,
|
|
47
|
+
agent: "coder",
|
|
48
|
+
system: composeSystem(options.system, planSoFar.render)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// `plan.taskPrompt` deliberately reads the frozen `plan` captured at
|
|
52
|
+
// the top of this function: a task prompt only needs that task's own
|
|
53
|
+
// details. `planSoFar`, threaded through by implementTaskLoop, is the
|
|
54
|
+
// single source of truth for completion progress instead.
|
|
24
55
|
yield* coder.ask(plan.taskPrompt(task));
|
|
25
56
|
const produced = yield* context.git.diffAll;
|
|
26
57
|
if (produced.trim().length === 0) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
58
|
+
// An empty diff is ambiguous: the task may be genuinely satisfied
|
|
59
|
+
// already, or the coder may simply have produced nothing. Ask
|
|
60
|
+
// explicitly instead of inferring from absence; a task that
|
|
61
|
+
// produces no changes and no confirmation fails rather than being
|
|
62
|
+
// silently marked complete.
|
|
63
|
+
const confirmation = yield* coder.ask([
|
|
64
|
+
"Your previous turn produced no file changes.",
|
|
65
|
+
`If the task "${task.title}" is already fully satisfied by the current state of the repository, reply with exactly TASK_ALREADY_SATISFIED and nothing else.`,
|
|
66
|
+
"Otherwise, implement the task now."
|
|
67
|
+
].join("\n"));
|
|
68
|
+
const afterConfirmation = yield* context.git.diffAll;
|
|
69
|
+
if (afterConfirmation.trim().length === 0) {
|
|
70
|
+
if (confirmation.includes("TASK_ALREADY_SATISFIED")) {
|
|
71
|
+
yield* context.events.publish(Info.make({
|
|
72
|
+
message: `task "${task.title}" confirmed already satisfied; skipping review and commit`
|
|
73
|
+
}));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
return yield* FlowAborted.make({
|
|
77
|
+
message: `task "${task.title}" produced no changes and did not confirm TASK_ALREADY_SATISFIED; failing instead of marking it complete`
|
|
78
|
+
});
|
|
79
|
+
}
|
|
31
80
|
}
|
|
32
81
|
yield* reviewAndFixLoop({
|
|
33
82
|
reviewers: options.reviewers ?? minimalReviewers,
|
|
@@ -46,7 +95,10 @@ export const implementPlanFlow = Effect.fn("@llm4ts/flow/Flow.implementPlan")(fu
|
|
|
46
95
|
return yield* FlowAborted.make({
|
|
47
96
|
message: [
|
|
48
97
|
`task "${task.title}": the lint gate is still failing after review settled; refusing to commit`,
|
|
49
|
-
...gate.issues.map((issue) =>
|
|
98
|
+
...gate.issues.map((issue) => {
|
|
99
|
+
const detail = issue.description.length === 0 ? "" : `\n${issue.description.slice(-2000)}`;
|
|
100
|
+
return `- [${issue.severity}] ${issue.title}${detail}`;
|
|
101
|
+
})
|
|
50
102
|
].join("\n")
|
|
51
103
|
});
|
|
52
104
|
}
|
package/dist/Flow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Flow.js","sourceRoot":"","sources":["../src/Flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"Flow.js","sourceRoot":"","sources":["../src/Flow.ts"],"names":[],"mappings":"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"}
|
package/dist/PlanExecution.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ import { type FlowEventsShape } from "./FlowEvents.ts";
|
|
|
3
3
|
import type { Plan, Task } from "./Plan.ts";
|
|
4
4
|
import type { PlanStoreShape } from "./Persistence.ts";
|
|
5
5
|
export declare const stage: <A, E, R>(events: FlowEventsShape, name: string, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
6
|
-
export declare const implementTaskLoop: <E, R>(store: PlanStoreShape, events: FlowEventsShape, planPath: string, plan: Plan, perTask: (task: Task) => Effect.Effect<void, E, R>) => Effect.Effect<Plan, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").BudgetExceeded | E, R>;
|
|
6
|
+
export declare const implementTaskLoop: <E, R>(store: PlanStoreShape, events: FlowEventsShape, planPath: string, plan: Plan, perTask: (task: Task, planSoFar: Plan) => Effect.Effect<void, E, R>) => Effect.Effect<Plan, import("./FlowError.ts").PersistenceError | import("./FlowError.ts").PlanParseError | import("./FlowError.ts").UnsupportedSchemaVersion | import("./FlowError.ts").WorkspacePathError | import("./FlowError.ts").WorkspaceLimitError | import("./FlowError.ts").WorkspaceIoError | import("./FlowError.ts").FlowAborted | import("./FlowError.ts").ProcessError | import("./FlowError.ts").FlowLlmError | import("./FlowError.ts").FlowCapabilityDenied | import("./FlowError.ts").BudgetExceeded | E, R>;
|
|
7
7
|
//# sourceMappingURL=PlanExecution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlanExecution.d.ts","sourceRoot":"","sources":["../src/PlanExecution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAA6C,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjG,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAKtD,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC3B,QAAQ,eAAe,EACvB,MAAM,MAAM,EACZ,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAYrB,CAAA;AAEH,eAAO,MAAM,iBAAiB,GACjB,CAAC,EAAE,CAAC,gGAKG,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"PlanExecution.d.ts","sourceRoot":"","sources":["../src/PlanExecution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAA6C,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjG,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAKtD,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC3B,QAAQ,eAAe,EACvB,MAAM,MAAM,EACZ,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAYrB,CAAA;AAEH,eAAO,MAAM,iBAAiB,GACjB,CAAC,EAAE,CAAC,gGAKG,IAAI,aAAa,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,kgBAYtE,CAAA"}
|
package/dist/PlanExecution.js
CHANGED
|
@@ -9,7 +9,7 @@ export const implementTaskLoop = Effect.fn("@llm4ts/flow/PlanExecution.implement
|
|
|
9
9
|
let current = plan;
|
|
10
10
|
for (const task of plan.tasks) {
|
|
11
11
|
if (!task.completed) {
|
|
12
|
-
yield* stage(events, task.title, perTask(task));
|
|
12
|
+
yield* stage(events, task.title, perTask(task, current));
|
|
13
13
|
current = current.complete(task.title);
|
|
14
14
|
yield* store.save(planPath, current);
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlanExecution.js","sourceRoot":"","sources":["../src/PlanExecution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAwB,MAAM,iBAAiB,CAAA;AAKjG,MAAM,YAAY,GAAG,CAAC,KAAc,EAAU,EAAE,CAC9C,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAEpF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,MAAuB,EACvB,IAAY,EACZ,MAA8B,EACN,EAAE,CAC1B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CACrD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EACtB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EACtE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,WAAW,CAAC,IAAI,CAAC;IACf,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;CAC7B,CAAC,CACH,CACF,CACF,CAAA;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,8CAA8C,CAAC,CACxF,QAAQ,CAAC,EACP,KAAqB,EACrB,MAAuB,EACvB,QAAgB,EAChB,IAAU,EACV,
|
|
1
|
+
{"version":3,"file":"PlanExecution.js","sourceRoot":"","sources":["../src/PlanExecution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAwB,MAAM,iBAAiB,CAAA;AAKjG,MAAM,YAAY,GAAG,CAAC,KAAc,EAAU,EAAE,CAC9C,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAEpF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,MAAuB,EACvB,IAAY,EACZ,MAA8B,EACN,EAAE,CAC1B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CACrD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EACtB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EACtE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,MAAM,CAAC,OAAO,CACZ,WAAW,CAAC,IAAI,CAAC;IACf,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;CAC7B,CAAC,CACH,CACF,CACF,CAAA;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,8CAA8C,CAAC,CACxF,QAAQ,CAAC,EACP,KAAqB,EACrB,MAAuB,EACvB,QAAgB,EAChB,IAAU,EACV,OAAmE;IAEnE,IAAI,OAAO,GAAG,IAAI,CAAA;IAClB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;YACxD,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACtC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC,CACF,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llm4ts/flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Effect-native LLM workflow, persistence, review, and repository automation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"typescript"
|
|
77
77
|
],
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@llm4ts/core": "0.
|
|
79
|
+
"@llm4ts/core": "0.2.0"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"effect": "^4.0.0-beta.102"
|
package/src/Chat.ts
CHANGED
|
@@ -2,9 +2,10 @@ import * as Effect from "effect/Effect"
|
|
|
2
2
|
import * as Ref from "effect/Ref"
|
|
3
3
|
import * as Semaphore from "effect/Semaphore"
|
|
4
4
|
import type { LlmServiceShape } from "@llm4ts/core/LlmService"
|
|
5
|
-
import { Message } from "@llm4ts/core/Models"
|
|
5
|
+
import { Message, type LlmResponse } from "@llm4ts/core/Models"
|
|
6
6
|
import { collect } from "@llm4ts/core/Streaming"
|
|
7
7
|
import { FlowLlmError } from "./FlowError.ts"
|
|
8
|
+
import { TokensUsed, type FlowEventsShape } from "./FlowEvents.ts"
|
|
8
9
|
|
|
9
10
|
export const gitOwnershipInstruction =
|
|
10
11
|
"The flow runtime owns git operations. Do not create or switch branches, commit, push, or open pull requests."
|
|
@@ -12,8 +13,28 @@ export const gitOwnershipInstruction =
|
|
|
12
13
|
export interface ChatOptions {
|
|
13
14
|
readonly system?: string
|
|
14
15
|
readonly manageGit?: boolean
|
|
16
|
+
/**
|
|
17
|
+
* When set, every reply whose collected response carries token usage is
|
|
18
|
+
* published as a `TokensUsed` event, which is what `CostTracker` (and
|
|
19
|
+
* therefore cost summaries and budgets) consume. Without it, usage
|
|
20
|
+
* reported by the backend is silently discarded.
|
|
21
|
+
*/
|
|
22
|
+
readonly events?: FlowEventsShape
|
|
23
|
+
/** Request label for published `TokensUsed` events. Default: "chat". */
|
|
24
|
+
readonly agent?: string
|
|
15
25
|
}
|
|
16
26
|
|
|
27
|
+
const publishUsage = (options: ChatOptions, response: LlmResponse): Effect.Effect<void> =>
|
|
28
|
+
options.events === undefined || response.usage === undefined
|
|
29
|
+
? Effect.void
|
|
30
|
+
: options.events.publish(
|
|
31
|
+
TokensUsed.make({
|
|
32
|
+
agent: options.agent ?? "chat",
|
|
33
|
+
usage: response.usage,
|
|
34
|
+
...(response.metadata.model === undefined ? {} : { model: response.metadata.model })
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
|
|
17
38
|
export interface Chat {
|
|
18
39
|
readonly ask: (prompt: string) => Effect.Effect<string, FlowLlmError>
|
|
19
40
|
readonly messages: Effect.Effect<ReadonlyArray<Message>>
|
|
@@ -46,6 +67,7 @@ export const makeChat = Effect.fn("@llm4ts/flow/Chat.make")(function* (
|
|
|
46
67
|
const response = yield* collect(service.executeStreamWithHistory(messages)).pipe(
|
|
47
68
|
Effect.mapError(FlowLlmError.from)
|
|
48
69
|
)
|
|
70
|
+
yield* publishUsage(options, response)
|
|
49
71
|
yield* Ref.set(history, [
|
|
50
72
|
...messages,
|
|
51
73
|
Message.make({ role: "Assistant", content: response.content })
|
package/src/Flow.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect"
|
|
2
2
|
import type { LlmServiceShape } from "@llm4ts/core/LlmService"
|
|
3
3
|
import { collect } from "@llm4ts/core/Streaming"
|
|
4
|
-
import { makeChat } from "./Chat.ts"
|
|
4
|
+
import { makeChat, type Chat } from "./Chat.ts"
|
|
5
5
|
import type { FlowContextShape } from "./FlowContext.ts"
|
|
6
6
|
import { FlowAborted, FlowLlmError, type FlowError } from "./FlowError.ts"
|
|
7
|
-
import { AssistantMessage, Info, type FlowEventsShape } from "./FlowEvents.ts"
|
|
7
|
+
import { AssistantMessage, Info, TokensUsed, type FlowEventsShape } from "./FlowEvents.ts"
|
|
8
8
|
import type { Plan, Task } from "./Plan.ts"
|
|
9
9
|
import type { PlanStoreShape } from "./Persistence.ts"
|
|
10
10
|
import { implementTaskLoop, stage } from "./PlanExecution.ts"
|
|
@@ -22,6 +22,15 @@ export const completeAndPublish = Effect.fn("@llm4ts/flow/Flow.completeAndPublis
|
|
|
22
22
|
const response = yield* collect(service.executeStream(prompt)).pipe(
|
|
23
23
|
Effect.mapError(FlowLlmError.from)
|
|
24
24
|
)
|
|
25
|
+
if (response.usage !== undefined) {
|
|
26
|
+
yield* events.publish(
|
|
27
|
+
TokensUsed.make({
|
|
28
|
+
agent: "assistant",
|
|
29
|
+
usage: response.usage,
|
|
30
|
+
...(response.metadata.model === undefined ? {} : { model: response.metadata.model })
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
}
|
|
25
34
|
yield* events.publish(AssistantMessage.make({ text: response.content }))
|
|
26
35
|
return response.content
|
|
27
36
|
})
|
|
@@ -31,6 +40,13 @@ export interface ImplementPlanOptions {
|
|
|
31
40
|
readonly planPath: string
|
|
32
41
|
readonly plan: Effect.Effect<Plan, FlowError>
|
|
33
42
|
readonly system?: string
|
|
43
|
+
/**
|
|
44
|
+
* When true, each task gets a fresh Chat (system prompt plus the plan's
|
|
45
|
+
* current render, showing prior tasks' completion status); that task's
|
|
46
|
+
* review-fix rounds share the same Chat. When false or omitted (default),
|
|
47
|
+
* one Chat is shared across every task in the plan.
|
|
48
|
+
*/
|
|
49
|
+
readonly chatPerTask?: boolean
|
|
34
50
|
readonly reviewers?: ReadonlyArray<Reviewer>
|
|
35
51
|
readonly commitMessage?: (plan: Plan, task: Task) => string
|
|
36
52
|
readonly checkoutBranch?: boolean
|
|
@@ -41,6 +57,9 @@ export interface ImplementPlanOptions {
|
|
|
41
57
|
|
|
42
58
|
const defaultCommitMessage = (plan: Plan, task: Task): string => `${plan.epicId}: ${task.title}`
|
|
43
59
|
|
|
60
|
+
const composeSystem = (base: string | undefined, note: string): string =>
|
|
61
|
+
[base, note].filter((part): part is string => part !== undefined && part.length > 0).join("\n\n")
|
|
62
|
+
|
|
44
63
|
export const implementPlanFlow = Effect.fn("@llm4ts/flow/Flow.implementPlan")(function* (
|
|
45
64
|
context: FlowContextShape,
|
|
46
65
|
options: ImplementPlanOptions
|
|
@@ -49,44 +68,96 @@ export const implementPlanFlow = Effect.fn("@llm4ts/flow/Flow.implementPlan")(fu
|
|
|
49
68
|
if (options.checkoutBranch !== false) {
|
|
50
69
|
yield* stage(context.events, "branch", context.git.checkoutOrCreate(plan.epicId))
|
|
51
70
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
let sharedCoder: Chat | undefined
|
|
72
|
+
if (options.chatPerTask !== true) {
|
|
73
|
+
sharedCoder = yield* makeChat(context.coder, {
|
|
74
|
+
events: context.events,
|
|
75
|
+
agent: "coder",
|
|
76
|
+
...(options.system === undefined ? {} : { system: options.system })
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return yield* implementTaskLoop(
|
|
81
|
+
options.store,
|
|
82
|
+
context.events,
|
|
83
|
+
options.planPath,
|
|
84
|
+
plan,
|
|
85
|
+
(task, planSoFar) =>
|
|
86
|
+
Effect.gen(function* () {
|
|
87
|
+
// `sharedCoder`'s definedness mirrors `options.chatPerTask !== true`
|
|
88
|
+
// above: when it's set, every task reuses it; when it's undefined,
|
|
89
|
+
// chatPerTask is active and each task builds its own fresh Chat.
|
|
90
|
+
let coder: Chat
|
|
91
|
+
if (sharedCoder !== undefined) {
|
|
92
|
+
coder = sharedCoder
|
|
93
|
+
} else {
|
|
94
|
+
coder = yield* makeChat(context.coder, {
|
|
95
|
+
events: context.events,
|
|
96
|
+
agent: "coder",
|
|
97
|
+
system: composeSystem(options.system, planSoFar.render)
|
|
63
98
|
})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
message: [
|
|
83
|
-
`task "${task.title}": the lint gate is still failing after review settled; refusing to commit`,
|
|
84
|
-
...gate.issues.map((issue) => `- [${issue.severity}] ${issue.title}`)
|
|
99
|
+
}
|
|
100
|
+
// `plan.taskPrompt` deliberately reads the frozen `plan` captured at
|
|
101
|
+
// the top of this function: a task prompt only needs that task's own
|
|
102
|
+
// details. `planSoFar`, threaded through by implementTaskLoop, is the
|
|
103
|
+
// single source of truth for completion progress instead.
|
|
104
|
+
yield* coder.ask(plan.taskPrompt(task))
|
|
105
|
+
const produced = yield* context.git.diffAll
|
|
106
|
+
if (produced.trim().length === 0) {
|
|
107
|
+
// An empty diff is ambiguous: the task may be genuinely satisfied
|
|
108
|
+
// already, or the coder may simply have produced nothing. Ask
|
|
109
|
+
// explicitly instead of inferring from absence; a task that
|
|
110
|
+
// produces no changes and no confirmation fails rather than being
|
|
111
|
+
// silently marked complete.
|
|
112
|
+
const confirmation = yield* coder.ask(
|
|
113
|
+
[
|
|
114
|
+
"Your previous turn produced no file changes.",
|
|
115
|
+
`If the task "${task.title}" is already fully satisfied by the current state of the repository, reply with exactly TASK_ALREADY_SATISFIED and nothing else.`,
|
|
116
|
+
"Otherwise, implement the task now."
|
|
85
117
|
].join("\n")
|
|
86
|
-
|
|
118
|
+
)
|
|
119
|
+
const afterConfirmation = yield* context.git.diffAll
|
|
120
|
+
if (afterConfirmation.trim().length === 0) {
|
|
121
|
+
if (confirmation.includes("TASK_ALREADY_SATISFIED")) {
|
|
122
|
+
yield* context.events.publish(
|
|
123
|
+
Info.make({
|
|
124
|
+
message: `task "${task.title}" confirmed already satisfied; skipping review and commit`
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
return yield* FlowAborted.make({
|
|
130
|
+
message: `task "${task.title}" produced no changes and did not confirm TASK_ALREADY_SATISFIED; failing instead of marking it complete`
|
|
131
|
+
})
|
|
132
|
+
}
|
|
87
133
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
134
|
+
yield* reviewAndFixLoop({
|
|
135
|
+
reviewers: options.reviewers ?? minimalReviewers,
|
|
136
|
+
reviewerService: flowReviewer(context),
|
|
137
|
+
coder,
|
|
138
|
+
taskTitle: task.title,
|
|
139
|
+
currentDiff: context.git.diffAll,
|
|
140
|
+
events: context.events,
|
|
141
|
+
...(options.maxRounds === undefined ? {} : { maxRounds: options.maxRounds }),
|
|
142
|
+
...(options.lint === undefined ? {} : { lint: options.lint }),
|
|
143
|
+
...(options.format === undefined ? {} : { format: options.format })
|
|
144
|
+
})
|
|
145
|
+
if (options.lint !== undefined) {
|
|
146
|
+
const gate = yield* options.lint
|
|
147
|
+
if (!gate.isClean) {
|
|
148
|
+
return yield* FlowAborted.make({
|
|
149
|
+
message: [
|
|
150
|
+
`task "${task.title}": the lint gate is still failing after review settled; refusing to commit`,
|
|
151
|
+
...gate.issues.map((issue) => {
|
|
152
|
+
const detail =
|
|
153
|
+
issue.description.length === 0 ? "" : `\n${issue.description.slice(-2000)}`
|
|
154
|
+
return `- [${issue.severity}] ${issue.title}${detail}`
|
|
155
|
+
})
|
|
156
|
+
].join("\n")
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
yield* context.git.commitAll((options.commitMessage ?? defaultCommitMessage)(plan, task))
|
|
161
|
+
})
|
|
91
162
|
)
|
|
92
163
|
})
|
package/src/PlanExecution.ts
CHANGED
|
@@ -31,12 +31,12 @@ export const implementTaskLoop = Effect.fn("@llm4ts/flow/PlanExecution.implement
|
|
|
31
31
|
events: FlowEventsShape,
|
|
32
32
|
planPath: string,
|
|
33
33
|
plan: Plan,
|
|
34
|
-
perTask: (task: Task) => Effect.Effect<void, E, R>
|
|
34
|
+
perTask: (task: Task, planSoFar: Plan) => Effect.Effect<void, E, R>
|
|
35
35
|
): Effect.fn.Return<Plan, E | FlowError, R> {
|
|
36
36
|
let current = plan
|
|
37
37
|
for (const task of plan.tasks) {
|
|
38
38
|
if (!task.completed) {
|
|
39
|
-
yield* stage(events, task.title, perTask(task))
|
|
39
|
+
yield* stage(events, task.title, perTask(task, current))
|
|
40
40
|
current = current.complete(task.title)
|
|
41
41
|
yield* store.save(planPath, current)
|
|
42
42
|
}
|