oh-my-opencode 5.0.0-beta.56 → 5.0.0-beta.60
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/cli/index.js +18 -22
- package/dist/cli-node/index.js +18 -22
- package/dist/index.js +16 -16
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
- package/package.json +16 -16
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.d.ts +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +4 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.d.ts +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +38 -39
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +163 -36
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.d.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +125 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +46 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +28 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +127 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk.js +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +8 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +6 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +50 -48
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +159 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +42 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +140 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-contract.test.ts +137 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-session-context.test.ts +148 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +4 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { checkpointUlwLoop } from "../checkpoint.js";
|
|
2
|
+
import { checkpointTemplate } from "../checkpoint-template.js";
|
|
3
|
+
import { recordEvidence } from "../evidence.js";
|
|
4
|
+
import { ulwLoopAttemptEvidenceDir } from "../paths.js";
|
|
5
|
+
import { addUlwLoopGoal, createUlwLoopPlan, startNextUlwLoop, summarizeUlwLoopPlan } from "../plan-crud.js";
|
|
6
|
+
import { readUlwLoopPlan } from "../plan-io.js";
|
|
7
|
+
import { recordFinalReviewBlockers } from "../review-blockers.js";
|
|
8
|
+
import { UlwLoopError } from "../runtime.js";
|
|
9
|
+
import { statusNextActions } from "../status-next-actions.js";
|
|
10
|
+
import { steerUlwLoop } from "../steering.js";
|
|
11
|
+
import { ULW_LOOP_MANIFEST } from "./manifest.js";
|
|
12
|
+
function validateContext(context) {
|
|
13
|
+
if (!context.cwd.trim())
|
|
14
|
+
throw new UlwLoopError("cwd is required.", "ULW_LOOP_CWD_REQUIRED");
|
|
15
|
+
if (!context.sessionId.trim())
|
|
16
|
+
throw new UlwLoopError("ULW_LOOP_SESSION_ID_REQUIRED: sessionId is required.", "ULW_LOOP_SESSION_ID_REQUIRED");
|
|
17
|
+
if (context.surface !== "omo-senpi" && context.surface !== "lazycodex")
|
|
18
|
+
throw new UlwLoopError("surface must be omo-senpi or lazycodex.", "ULW_LOOP_SURFACE_INVALID");
|
|
19
|
+
}
|
|
20
|
+
function errorDetails(error) {
|
|
21
|
+
const details = error.details === undefined
|
|
22
|
+
? undefined
|
|
23
|
+
: Object.fromEntries(Object.entries(error.details).map(([key, value]) => [key, String(value)]));
|
|
24
|
+
return details === undefined
|
|
25
|
+
? { code: error.code, message: error.message }
|
|
26
|
+
: { code: error.code, message: error.message, details };
|
|
27
|
+
}
|
|
28
|
+
function failure(operation, error) {
|
|
29
|
+
return { ok: false, operation, error: errorDetails(error) };
|
|
30
|
+
}
|
|
31
|
+
function caught(operation, error) {
|
|
32
|
+
return failure(operation, error instanceof UlwLoopError ? error : new UlwLoopError(error.message, "ULW_LOOP_ERROR"));
|
|
33
|
+
}
|
|
34
|
+
function isKnownRequest(request) {
|
|
35
|
+
return ULW_LOOP_MANIFEST.operations.some((operation) => operation.name === request.operation);
|
|
36
|
+
}
|
|
37
|
+
function unreachable(value) {
|
|
38
|
+
throw new UlwLoopError(`Unhandled operation: ${String(value)}`, "ULW_LOOP_OPERATION_UNHANDLED");
|
|
39
|
+
}
|
|
40
|
+
function nextActionsFrom(result) {
|
|
41
|
+
if (!("nextActions" in result) || !Array.isArray(result.nextActions))
|
|
42
|
+
return [];
|
|
43
|
+
return result.nextActions.filter((action) => typeof action === "string").slice(0, 8);
|
|
44
|
+
}
|
|
45
|
+
export function createAgentToolkit(context, deps = {}) {
|
|
46
|
+
validateContext(context);
|
|
47
|
+
const scope = { sessionId: context.sessionId };
|
|
48
|
+
const notify = async (operation, response) => {
|
|
49
|
+
if (deps.hooks?.onOperation !== undefined)
|
|
50
|
+
await deps.hooks.onOperation({ operation, context, response });
|
|
51
|
+
return response;
|
|
52
|
+
};
|
|
53
|
+
const invoke = async (operation, fn) => {
|
|
54
|
+
try {
|
|
55
|
+
const result = await fn();
|
|
56
|
+
const nextActions = typeof result === "object" && result !== null ? nextActionsFrom(result) : [];
|
|
57
|
+
return await notify(operation, { ok: true, operation, result, nextActions });
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const response = caught(operation, error instanceof Error ? error : new Error("ULW_LOOP_ERROR"));
|
|
61
|
+
return notify(operation, response);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const toolkit = {
|
|
65
|
+
dispatch: async (request) => {
|
|
66
|
+
if (!isKnownRequest(request))
|
|
67
|
+
return failure(request.operation, new UlwLoopError(`Unknown operation: ${request.operation}`, "ULW_LOOP_OPERATION_UNKNOWN"));
|
|
68
|
+
switch (request.operation) {
|
|
69
|
+
case "help":
|
|
70
|
+
return toolkit.help();
|
|
71
|
+
case "create-goals":
|
|
72
|
+
return toolkit.createGoals(request.args);
|
|
73
|
+
case "status":
|
|
74
|
+
return toolkit.status();
|
|
75
|
+
case "complete-goals":
|
|
76
|
+
return toolkit.completeGoals(request.args);
|
|
77
|
+
case "checkpoint":
|
|
78
|
+
return toolkit.checkpoint(request.args);
|
|
79
|
+
case "steer":
|
|
80
|
+
return toolkit.steer(request.args);
|
|
81
|
+
case "add-goal":
|
|
82
|
+
return toolkit.addGoal(request.args);
|
|
83
|
+
case "criteria":
|
|
84
|
+
return toolkit.criteria(request.args);
|
|
85
|
+
case "record-evidence":
|
|
86
|
+
return toolkit.recordEvidence(request.args);
|
|
87
|
+
case "record-review-blockers":
|
|
88
|
+
return toolkit.recordReviewBlockers(request.args);
|
|
89
|
+
default:
|
|
90
|
+
return unreachable(request);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
help: () => invoke("help", async () => ULW_LOOP_MANIFEST),
|
|
94
|
+
createGoals: (args) => invoke("create-goals", () => createUlwLoopPlan(context.cwd, args, scope)),
|
|
95
|
+
status: () => invoke("status", async () => {
|
|
96
|
+
const plan = await readUlwLoopPlan(context.cwd, scope);
|
|
97
|
+
const active = plan.goals.find((goal) => goal.id === plan.activeGoalId);
|
|
98
|
+
return {
|
|
99
|
+
plan,
|
|
100
|
+
summary: summarizeUlwLoopPlan(plan),
|
|
101
|
+
nextActions: statusNextActions(plan),
|
|
102
|
+
// Attempt directories are an evidence-layout v2 concept; a v1 plan must not advertise one.
|
|
103
|
+
...(active === undefined || plan.evidenceLayoutVersion !== 2
|
|
104
|
+
? {}
|
|
105
|
+
: { currentAttemptDir: ulwLoopAttemptEvidenceDir(active.id, active.attempt, scope) }),
|
|
106
|
+
};
|
|
107
|
+
}),
|
|
108
|
+
completeGoals: (args = {}) => invoke("complete-goals", () => startNextUlwLoop(context.cwd, args, scope)),
|
|
109
|
+
checkpoint: (args) => invoke("checkpoint", () => args.printTemplate === true
|
|
110
|
+
? checkpointTemplate(context.cwd, scope, args.goalId, { surface: context.surface })
|
|
111
|
+
: checkpointUlwLoop(context.cwd, args, scope, { surface: context.surface })),
|
|
112
|
+
steer: (args) => invoke("steer", () => steerUlwLoop(context.cwd, args, scope)),
|
|
113
|
+
addGoal: (args) => invoke("add-goal", () => addUlwLoopGoal(context.cwd, args, scope)),
|
|
114
|
+
criteria: (args) => invoke("criteria", async () => {
|
|
115
|
+
const plan = await readUlwLoopPlan(context.cwd, scope);
|
|
116
|
+
const goal = plan.goals.find((candidate) => candidate.id === args.goalId);
|
|
117
|
+
if (goal === undefined)
|
|
118
|
+
throw new UlwLoopError(`Unknown ulw-loop id: ${args.goalId}.`, "ULW_LOOP_GOAL_NOT_FOUND");
|
|
119
|
+
return { goalId: goal.id, criteria: goal.successCriteria };
|
|
120
|
+
}),
|
|
121
|
+
recordEvidence: (args) => invoke("record-evidence", () => recordEvidence(context.cwd, args, scope)),
|
|
122
|
+
recordReviewBlockers: (args) => invoke("record-review-blockers", () => recordFinalReviewBlockers(context.cwd, args, scope)),
|
|
123
|
+
};
|
|
124
|
+
return toolkit;
|
|
125
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const ULW_LOOP_OPERATIONS: readonly ["help", "create-goals", "status", "complete-goals", "checkpoint", "steer", "add-goal", "criteria", "record-evidence", "record-review-blockers"];
|
|
2
|
+
export type UlwLoopOperation = (typeof ULW_LOOP_OPERATIONS)[number];
|
|
3
|
+
export interface ToolkitOperationManifest {
|
|
4
|
+
readonly name: UlwLoopOperation;
|
|
5
|
+
readonly mutating: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ToolkitManifest {
|
|
8
|
+
readonly version: 1;
|
|
9
|
+
readonly name: "ulw-loop";
|
|
10
|
+
readonly operations: readonly ToolkitOperationManifest[];
|
|
11
|
+
}
|
|
12
|
+
export declare const ULW_LOOP_MANIFEST: {
|
|
13
|
+
version: 1;
|
|
14
|
+
name: "ulw-loop";
|
|
15
|
+
operations: ({
|
|
16
|
+
name: "help";
|
|
17
|
+
mutating: false;
|
|
18
|
+
} | {
|
|
19
|
+
name: "create-goals";
|
|
20
|
+
mutating: true;
|
|
21
|
+
} | {
|
|
22
|
+
name: "status";
|
|
23
|
+
mutating: false;
|
|
24
|
+
} | {
|
|
25
|
+
name: "complete-goals";
|
|
26
|
+
mutating: true;
|
|
27
|
+
} | {
|
|
28
|
+
name: "checkpoint";
|
|
29
|
+
mutating: true;
|
|
30
|
+
} | {
|
|
31
|
+
name: "steer";
|
|
32
|
+
mutating: true;
|
|
33
|
+
} | {
|
|
34
|
+
name: "add-goal";
|
|
35
|
+
mutating: true;
|
|
36
|
+
} | {
|
|
37
|
+
name: "criteria";
|
|
38
|
+
mutating: false;
|
|
39
|
+
} | {
|
|
40
|
+
name: "record-evidence";
|
|
41
|
+
mutating: true;
|
|
42
|
+
} | {
|
|
43
|
+
name: "record-review-blockers";
|
|
44
|
+
mutating: true;
|
|
45
|
+
})[];
|
|
46
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const ULW_LOOP_OPERATIONS = [
|
|
2
|
+
"help",
|
|
3
|
+
"create-goals",
|
|
4
|
+
"status",
|
|
5
|
+
"complete-goals",
|
|
6
|
+
"checkpoint",
|
|
7
|
+
"steer",
|
|
8
|
+
"add-goal",
|
|
9
|
+
"criteria",
|
|
10
|
+
"record-evidence",
|
|
11
|
+
"record-review-blockers",
|
|
12
|
+
];
|
|
13
|
+
export const ULW_LOOP_MANIFEST = {
|
|
14
|
+
version: 1,
|
|
15
|
+
name: "ulw-loop",
|
|
16
|
+
operations: [
|
|
17
|
+
{ name: "help", mutating: false },
|
|
18
|
+
{ name: "create-goals", mutating: true },
|
|
19
|
+
{ name: "status", mutating: false },
|
|
20
|
+
{ name: "complete-goals", mutating: true },
|
|
21
|
+
{ name: "checkpoint", mutating: true },
|
|
22
|
+
{ name: "steer", mutating: true },
|
|
23
|
+
{ name: "add-goal", mutating: true },
|
|
24
|
+
{ name: "criteria", mutating: false },
|
|
25
|
+
{ name: "record-evidence", mutating: true },
|
|
26
|
+
{ name: "record-review-blockers", mutating: true },
|
|
27
|
+
],
|
|
28
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { CheckpointUlwLoopArgs, CheckpointUlwLoopResult } from "../checkpoint.js";
|
|
2
|
+
import type { CheckpointTemplate } from "../checkpoint-template.js";
|
|
3
|
+
import type { recordEvidence } from "../evidence.js";
|
|
4
|
+
import type { addUlwLoopGoal, createUlwLoopPlan, startNextUlwLoop, summarizeUlwLoopPlan } from "../plan-crud.js";
|
|
5
|
+
import type { recordFinalReviewBlockers } from "../review-blockers.js";
|
|
6
|
+
import type { steerUlwLoop } from "../steering.js";
|
|
7
|
+
import type { UlwLoopSteeringProposal } from "../steering-types.js";
|
|
8
|
+
import type { UlwLoopToolkitSurface } from "../surface.js";
|
|
9
|
+
import type { UlwLoopPlan } from "../types.js";
|
|
10
|
+
import type { ULW_LOOP_MANIFEST, UlwLoopOperation } from "./manifest.js";
|
|
11
|
+
export type ToolkitSurface = UlwLoopToolkitSurface;
|
|
12
|
+
export interface ToolkitContext {
|
|
13
|
+
readonly cwd: string;
|
|
14
|
+
readonly sessionId: string;
|
|
15
|
+
readonly surface: ToolkitSurface;
|
|
16
|
+
}
|
|
17
|
+
export type CreateGoalsArgs = Parameters<typeof createUlwLoopPlan>[1];
|
|
18
|
+
export type CompleteGoalsArgs = NonNullable<Parameters<typeof startNextUlwLoop>[1]>;
|
|
19
|
+
export type SteerArgs = UlwLoopSteeringProposal;
|
|
20
|
+
export type AddGoalArgs = Parameters<typeof addUlwLoopGoal>[1];
|
|
21
|
+
export type RecordEvidenceArgs = Parameters<typeof recordEvidence>[1];
|
|
22
|
+
export type CriteriaArgs = {
|
|
23
|
+
readonly goalId: string;
|
|
24
|
+
};
|
|
25
|
+
export type CheckpointMutationArgs = CheckpointUlwLoopArgs & {
|
|
26
|
+
readonly printTemplate?: false;
|
|
27
|
+
};
|
|
28
|
+
export type CheckpointTemplateArgs = {
|
|
29
|
+
readonly printTemplate: true;
|
|
30
|
+
readonly goalId?: string;
|
|
31
|
+
};
|
|
32
|
+
export type CheckpointArgs = CheckpointMutationArgs | CheckpointTemplateArgs;
|
|
33
|
+
export type RecordReviewBlockersArgs = Parameters<typeof recordFinalReviewBlockers>[1];
|
|
34
|
+
export type ToolkitDispatchRequest = {
|
|
35
|
+
readonly operation: "help";
|
|
36
|
+
readonly args?: Record<never, never>;
|
|
37
|
+
} | {
|
|
38
|
+
readonly operation: "create-goals";
|
|
39
|
+
readonly args: CreateGoalsArgs;
|
|
40
|
+
} | {
|
|
41
|
+
readonly operation: "status";
|
|
42
|
+
readonly args?: Record<never, never>;
|
|
43
|
+
} | {
|
|
44
|
+
readonly operation: "complete-goals";
|
|
45
|
+
readonly args?: CompleteGoalsArgs;
|
|
46
|
+
} | {
|
|
47
|
+
readonly operation: "checkpoint";
|
|
48
|
+
readonly args: CheckpointArgs;
|
|
49
|
+
} | {
|
|
50
|
+
readonly operation: "steer";
|
|
51
|
+
readonly args: SteerArgs;
|
|
52
|
+
} | {
|
|
53
|
+
readonly operation: "add-goal";
|
|
54
|
+
readonly args: AddGoalArgs;
|
|
55
|
+
} | {
|
|
56
|
+
readonly operation: "criteria";
|
|
57
|
+
readonly args: CriteriaArgs;
|
|
58
|
+
} | {
|
|
59
|
+
readonly operation: "record-evidence";
|
|
60
|
+
readonly args: RecordEvidenceArgs;
|
|
61
|
+
} | {
|
|
62
|
+
readonly operation: "record-review-blockers";
|
|
63
|
+
readonly args: RecordReviewBlockersArgs;
|
|
64
|
+
};
|
|
65
|
+
export interface ToolkitUnknownRequest {
|
|
66
|
+
readonly operation: string;
|
|
67
|
+
readonly args?: Record<never, never>;
|
|
68
|
+
}
|
|
69
|
+
export interface ToolkitError {
|
|
70
|
+
readonly code: string;
|
|
71
|
+
readonly message: string;
|
|
72
|
+
readonly details?: Readonly<Record<string, string>>;
|
|
73
|
+
}
|
|
74
|
+
export interface ToolkitSuccess<Operation extends UlwLoopOperation, Result> {
|
|
75
|
+
readonly ok: true;
|
|
76
|
+
readonly operation: Operation;
|
|
77
|
+
readonly result: Result;
|
|
78
|
+
readonly nextActions: readonly string[];
|
|
79
|
+
readonly warnings?: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
export interface ToolkitFailure<Operation extends string = string> {
|
|
82
|
+
readonly ok: false;
|
|
83
|
+
readonly operation: Operation;
|
|
84
|
+
readonly error: ToolkitError;
|
|
85
|
+
}
|
|
86
|
+
type StatusResult = {
|
|
87
|
+
readonly plan: UlwLoopPlan;
|
|
88
|
+
readonly summary: ReturnType<typeof summarizeUlwLoopPlan>;
|
|
89
|
+
readonly nextActions: readonly string[];
|
|
90
|
+
readonly currentAttemptDir?: string;
|
|
91
|
+
};
|
|
92
|
+
type ResultFor<Operation extends UlwLoopOperation> = Operation extends "help" ? typeof ULW_LOOP_MANIFEST : Operation extends "create-goals" ? Awaited<ReturnType<typeof createUlwLoopPlan>> : Operation extends "status" ? StatusResult : Operation extends "complete-goals" ? Awaited<ReturnType<typeof startNextUlwLoop>> : Operation extends "checkpoint" ? CheckpointUlwLoopResult | CheckpointTemplate : Operation extends "steer" ? Awaited<ReturnType<typeof steerUlwLoop>> : Operation extends "add-goal" ? Awaited<ReturnType<typeof addUlwLoopGoal>> : Operation extends "criteria" ? {
|
|
93
|
+
readonly goalId: string;
|
|
94
|
+
readonly criteria: UlwLoopPlan["goals"][number]["successCriteria"];
|
|
95
|
+
} : Operation extends "record-evidence" ? Awaited<ReturnType<typeof recordEvidence>> : Operation extends "record-review-blockers" ? Awaited<ReturnType<typeof recordFinalReviewBlockers>> : never;
|
|
96
|
+
export type ToolkitResultFor<Operation extends UlwLoopOperation> = ResultFor<Operation>;
|
|
97
|
+
export type ToolkitResponseFor<Operation extends UlwLoopOperation> = ToolkitSuccess<Operation, ResultFor<Operation>> | ToolkitFailure<Operation>;
|
|
98
|
+
export type ToolkitOperationResponse = {
|
|
99
|
+
[Operation in UlwLoopOperation]: ToolkitResponseFor<Operation>;
|
|
100
|
+
}[UlwLoopOperation];
|
|
101
|
+
export type ToolkitDispatchResponse = ToolkitOperationResponse | ToolkitFailure<string>;
|
|
102
|
+
export type ToolkitResponse<Result> = ToolkitSuccess<UlwLoopOperation, Result> | ToolkitFailure<string>;
|
|
103
|
+
export interface ToolkitOperationHookEvent<Operation extends UlwLoopOperation = UlwLoopOperation> {
|
|
104
|
+
readonly operation: Operation;
|
|
105
|
+
readonly context: ToolkitContext;
|
|
106
|
+
readonly response: ToolkitResponseFor<Operation>;
|
|
107
|
+
}
|
|
108
|
+
export interface ToolkitHostHooks {
|
|
109
|
+
readonly onOperation?: <Operation extends UlwLoopOperation>(event: ToolkitOperationHookEvent<Operation>) => void | Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
export interface AgentToolkitDependencies {
|
|
112
|
+
readonly hooks?: ToolkitHostHooks;
|
|
113
|
+
}
|
|
114
|
+
export interface AgentToolkit {
|
|
115
|
+
readonly dispatch: (request: ToolkitDispatchRequest | ToolkitUnknownRequest) => Promise<ToolkitDispatchResponse>;
|
|
116
|
+
readonly help: () => Promise<ToolkitResponseFor<"help">>;
|
|
117
|
+
readonly createGoals: (args: CreateGoalsArgs) => Promise<ToolkitResponseFor<"create-goals">>;
|
|
118
|
+
readonly status: () => Promise<ToolkitResponseFor<"status">>;
|
|
119
|
+
readonly completeGoals: (args?: CompleteGoalsArgs) => Promise<ToolkitResponseFor<"complete-goals">>;
|
|
120
|
+
readonly checkpoint: (args: CheckpointArgs) => Promise<ToolkitResponseFor<"checkpoint">>;
|
|
121
|
+
readonly steer: (args: SteerArgs) => Promise<ToolkitResponseFor<"steer">>;
|
|
122
|
+
readonly addGoal: (args: AddGoalArgs) => Promise<ToolkitResponseFor<"add-goal">>;
|
|
123
|
+
readonly criteria: (args: CriteriaArgs) => Promise<ToolkitResponseFor<"criteria">>;
|
|
124
|
+
readonly recordEvidence: (args: RecordEvidenceArgs) => Promise<ToolkitResponseFor<"record-evidence">>;
|
|
125
|
+
readonly recordReviewBlockers: (args: RecordReviewBlockersArgs) => Promise<ToolkitResponseFor<"record-review-blockers">>;
|
|
126
|
+
}
|
|
127
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit --with-ultrawork",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
10
|
+
"statusMessage": "(OmO 5.0.0-beta.60) Checking Ulw-Loop Steering"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"type": "command",
|
|
21
21
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use-spawn",
|
|
22
22
|
"timeout": 5,
|
|
23
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
23
|
+
"statusMessage": "(OmO 5.0.0-beta.60) Recording Spawn Admission"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
26
|
}
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"type": "command",
|
|
34
34
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use",
|
|
35
35
|
"timeout": 5,
|
|
36
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
36
|
+
"statusMessage": "(OmO 5.0.0-beta.60) Enforcing Unlimited Ulw-Loop Budget"
|
|
37
37
|
}
|
|
38
38
|
]
|
|
39
39
|
},
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"type": "command",
|
|
45
45
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use-spawn",
|
|
46
46
|
"timeout": 5,
|
|
47
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
47
|
+
"statusMessage": "(OmO 5.0.0-beta.60) Guarding Ulw-Loop Spawns"
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
50
|
}
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"type": "command",
|
|
57
57
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook stop",
|
|
58
58
|
"timeout": 10,
|
|
59
|
-
"statusMessage": "(OmO 5.0.0-beta.
|
|
59
|
+
"statusMessage": "(OmO 5.0.0-beta.60) Checking Ulw-Loop Resume"
|
|
60
60
|
}
|
|
61
61
|
]
|
|
62
62
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-ulw-loop",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.60",
|
|
4
4
|
"description": "Codex plugin: durable repo-native multi-goal orchestration with embedded success criteria and observable evidence audit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.12.1",
|
|
@@ -68,3 +68,7 @@ Codex exposes ONE subagent surface per session — check your tool list. GPT-5.6
|
|
|
68
68
|
V1 fallback (gpt-5.5, gpt-5.6-luna): `multi_agent_v1.spawn_agent({...,"fork_context":false})`, `multi_agent_v1.send_input` (re-task), `multi_agent_v1.wait_agent({"targets":[...],"timeout_ms":...})`, `multi_agent_v1.close_agent`.
|
|
69
69
|
|
|
70
70
|
When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`.
|
|
71
|
+
|
|
72
|
+
## Driver goal lifecycle
|
|
73
|
+
|
|
74
|
+
The Codex thread goal is a DRIVER the loop instructs, never a gate. `checkpoint` takes an OPTIONAL `--codex-goal-json` snapshot, records it verbatim in the ledger, and never rejects on its status or objective; the advice arrives in `nextActions`. A driver completed early yields advice to `create_goal` again with the plan's objective verbatim; `paused`, `usage_limited`, and `budget_limited` yield resume advice; a differing objective is a warning, not a refusal. Malformed snapshot input is the only failure, reported as `ULW_LOOP_CODEX_GOAL_JSON_INVALID`.
|
|
@@ -11,6 +11,10 @@ export interface CheckpointTemplate {
|
|
|
11
11
|
readonly guidance?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export interface CheckpointTemplateDependencies {
|
|
15
|
+
readonly surface?: UlwLoopToolkitSurface;
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
function artifactPath(base: string, name: string): string {
|
|
15
19
|
return `${base}/${name}`;
|
|
16
20
|
}
|
|
@@ -97,8 +101,10 @@ export async function checkpointTemplate(
|
|
|
97
101
|
repoRoot: string,
|
|
98
102
|
scope?: UlwLoopScope,
|
|
99
103
|
goalId?: string,
|
|
104
|
+
dependencies?: CheckpointTemplateDependencies,
|
|
100
105
|
): Promise<CheckpointTemplate> {
|
|
101
106
|
const plan: UlwLoopPlan = await readUlwLoopPlan(repoRoot, scope);
|
|
107
|
+
const surface = dependencies?.surface ?? resolveToolkitSurface();
|
|
102
108
|
const targetId = goalId ?? plan.activeGoalId;
|
|
103
109
|
const active = plan.goals.find((goal) => goal.id === targetId);
|
|
104
110
|
if (goalId !== undefined && active === undefined)
|
|
@@ -110,7 +116,7 @@ export async function checkpointTemplate(
|
|
|
110
116
|
"Fill every <replace:...> value with plausible non-empty evidence and use real, non-empty artifact files.",
|
|
111
117
|
'Passing codex-goal-json example: {"goal":{"objective":"<plan codexObjective verbatim>","status":"complete"}}.',
|
|
112
118
|
'Passing quality-gate-json example requires gateReview {"by":"category:deep","recommendation":"APPROVE","evidence":"review passed","reportPath":"<attemptDir>/gate-review.md","blockers":[],"notes":[]}, manualQa.artifactRefs objects, iteration, and criteriaCoverage.',
|
|
113
|
-
...(
|
|
119
|
+
...(surface === "lazycodex"
|
|
114
120
|
? [
|
|
115
121
|
"Self-review defaults: manualQa.by and gateReview.by are main-session. Alternatives: manualQa.by accepts lazycodex-qa-executor; gateReview.by accepts lazycodex-gate-reviewer, category:deep, category:unspecified-high, or category:unspecified-low. Optional codeReview.by accepts lazycodex-code-reviewer or main-session.",
|
|
116
122
|
]
|
|
@@ -118,7 +124,7 @@ export async function checkpointTemplate(
|
|
|
118
124
|
...(hasAttempt ? [] : ["This plan is evidence-layout v1; artifacts go under .omo/evidence/."]),
|
|
119
125
|
].join(" ");
|
|
120
126
|
return {
|
|
121
|
-
qualityGateTemplate: gateTemplate(
|
|
127
|
+
qualityGateTemplate: gateTemplate(surface, attemptDir),
|
|
122
128
|
codexGoalTemplate: {
|
|
123
129
|
goal: { objective: plan.codexObjective ?? "<replace:codex objective>", status: "complete" },
|
|
124
130
|
},
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
sameBlockerOccurrences,
|
|
18
18
|
validateQualityGate,
|
|
19
19
|
} from "./quality-gate.js";
|
|
20
|
-
import { resolveToolkitSurface } from "./surface.js";
|
|
20
|
+
import { resolveToolkitSurface, type UlwLoopToolkitSurface } from "./surface.js";
|
|
21
21
|
import type {
|
|
22
22
|
UlwLoopAggregateCompletion,
|
|
23
23
|
UlwLoopItem,
|
|
@@ -35,6 +35,9 @@ export interface CheckpointUlwLoopArgs {
|
|
|
35
35
|
readonly codexGoalJson?: string;
|
|
36
36
|
readonly qualityGateJson?: string;
|
|
37
37
|
}
|
|
38
|
+
export interface CheckpointUlwLoopDependencies {
|
|
39
|
+
readonly surface?: UlwLoopToolkitSurface;
|
|
40
|
+
}
|
|
38
41
|
export interface CheckpointUlwLoopResult {
|
|
39
42
|
readonly plan: UlwLoopPlan;
|
|
40
43
|
readonly goal: UlwLoopItem;
|
|
@@ -151,6 +154,7 @@ export async function checkpointUlwLoop(
|
|
|
151
154
|
repoRoot: string,
|
|
152
155
|
args: CheckpointUlwLoopArgs,
|
|
153
156
|
scope?: UlwLoopScope,
|
|
157
|
+
dependencies?: CheckpointUlwLoopDependencies,
|
|
154
158
|
): Promise<CheckpointUlwLoopResult> {
|
|
155
159
|
return withUlwLoopMutationLock(repoRoot, scope, async () => {
|
|
156
160
|
const plan = await readUlwLoopPlan(repoRoot, scope);
|
|
@@ -198,7 +202,7 @@ export async function checkpointUlwLoop(
|
|
|
198
202
|
qualityGate = validateQualityGate(await readJsonInput(args.qualityGateJson, repoRoot), {
|
|
199
203
|
repoRoot,
|
|
200
204
|
fs: QUALITY_GATE_FS,
|
|
201
|
-
reviewerSurface: resolveToolkitSurface(),
|
|
205
|
+
reviewerSurface: dependencies?.surface ?? resolveToolkitSurface(),
|
|
202
206
|
...(plan.evidenceLayoutVersion === 2
|
|
203
207
|
? { currentAttemptDir: ulwLoopAttemptEvidenceDir(goal.id, goal.attempt, scope) }
|
|
204
208
|
: {}),
|
|
@@ -11,15 +11,12 @@ import {
|
|
|
11
11
|
import { blockedDecisionHandoff, normalizeCodexGoalMode, printJson, printStatus } from "./cli-output.js";
|
|
12
12
|
import { parseSteeringProposals, printSteerBatchResult, printSteerResult } from "./cli-steering.js";
|
|
13
13
|
import { buildCodexGoalInstruction } from "./codex-goal-instruction.js";
|
|
14
|
-
import { recordEvidence } from "./evidence.js";
|
|
15
14
|
import { isEssentialCriterion } from "./goal-status.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { recordFinalReviewBlockers } from "./review-blockers.js";
|
|
20
|
-
import { statusNextActions } from "./status-next-actions.js";
|
|
21
|
-
import { steerUlwLoop } from "./steering.js";
|
|
15
|
+
import type { UlwLoopScope } from "./paths.js";
|
|
16
|
+
import { summarizeUlwLoopPlan } from "./plan-crud.js";
|
|
17
|
+
import { type AgentToolkit, createAgentToolkit } from "./sdk.js";
|
|
22
18
|
import { steerUlwLoopBatch } from "./steering-batch.js";
|
|
19
|
+
import { resolveToolkitSurface } from "./surface.js";
|
|
23
20
|
import type { UlwLoopItem } from "./types.js";
|
|
24
21
|
import { UlwLoopError } from "./types.js";
|
|
25
22
|
|
|
@@ -42,15 +39,13 @@ export async function createGoals(
|
|
|
42
39
|
);
|
|
43
40
|
}
|
|
44
41
|
const validationBatchesJson = readValue(argv, "--validation-batch-json");
|
|
45
|
-
const plan =
|
|
46
|
-
repoRoot,
|
|
47
|
-
{
|
|
42
|
+
const plan = unwrap(
|
|
43
|
+
await toolkitFor(repoRoot, scope).createGoals({
|
|
48
44
|
brief,
|
|
49
45
|
codexGoalMode: normalizeCodexGoalMode(readValue(argv, "--codex-goal-mode")),
|
|
50
46
|
force: hasFlag(argv, "--force"),
|
|
51
47
|
...(validationBatchesJson === undefined ? {} : { validationBatchesJson }),
|
|
52
|
-
},
|
|
53
|
-
scope,
|
|
48
|
+
}),
|
|
54
49
|
);
|
|
55
50
|
if (json) printJson({ ok: true, plan, summary: summarizeUlwLoopPlan(plan) });
|
|
56
51
|
else {
|
|
@@ -62,21 +57,9 @@ export async function createGoals(
|
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
export async function status(repoRoot: string, json: boolean, scope?: UlwLoopScope): Promise<number> {
|
|
65
|
-
const
|
|
66
|
-
if (json) {
|
|
67
|
-
|
|
68
|
-
const currentAttemptDir =
|
|
69
|
-
plan.evidenceLayoutVersion === 2 && active
|
|
70
|
-
? ulwLoopAttemptEvidenceDir(active.id, active.attempt, scope)
|
|
71
|
-
: undefined;
|
|
72
|
-
printJson({
|
|
73
|
-
ok: true,
|
|
74
|
-
plan,
|
|
75
|
-
summary: summarizeUlwLoopPlan(plan),
|
|
76
|
-
...(currentAttemptDir === undefined ? {} : { currentAttemptDir }),
|
|
77
|
-
nextActions: statusNextActions(plan),
|
|
78
|
-
});
|
|
79
|
-
} else printStatus(plan);
|
|
60
|
+
const status = unwrap(await toolkitFor(repoRoot, scope).status());
|
|
61
|
+
if (json) printJson({ ok: true, ...status });
|
|
62
|
+
else printStatus(status.plan);
|
|
80
63
|
return 0;
|
|
81
64
|
}
|
|
82
65
|
|
|
@@ -86,7 +69,9 @@ export async function completeGoals(
|
|
|
86
69
|
json: boolean,
|
|
87
70
|
scope?: UlwLoopScope,
|
|
88
71
|
): Promise<number> {
|
|
89
|
-
const result =
|
|
72
|
+
const result = unwrap(
|
|
73
|
+
await toolkitFor(repoRoot, scope).completeGoals({ retryFailed: hasFlag(argv, "--retry-failed") }),
|
|
74
|
+
);
|
|
90
75
|
if ("done" in result) {
|
|
91
76
|
const handoff = blockedDecisionHandoff(result.plan);
|
|
92
77
|
if (json) {
|
|
@@ -116,7 +101,7 @@ export async function steer(
|
|
|
116
101
|
const proposals = await parseSteeringProposals(argv);
|
|
117
102
|
const single = proposals[0];
|
|
118
103
|
if (single !== undefined && proposals.length === 1 && readValue(argv, "--proposals-json") === undefined) {
|
|
119
|
-
const result = await
|
|
104
|
+
const result = unwrap(await toolkitFor(repoRoot, scope).steer(single));
|
|
120
105
|
printSteerResult(result, json);
|
|
121
106
|
return result.accepted ? 0 : 1;
|
|
122
107
|
}
|
|
@@ -131,10 +116,11 @@ export async function addGoal(
|
|
|
131
116
|
json: boolean,
|
|
132
117
|
scope?: UlwLoopScope,
|
|
133
118
|
): Promise<number> {
|
|
134
|
-
const result =
|
|
135
|
-
repoRoot,
|
|
136
|
-
|
|
137
|
-
|
|
119
|
+
const result = unwrap(
|
|
120
|
+
await toolkitFor(repoRoot, scope).addGoal({
|
|
121
|
+
title: required(argv, "--title"),
|
|
122
|
+
objective: required(argv, "--objective"),
|
|
123
|
+
}),
|
|
138
124
|
);
|
|
139
125
|
if (json) printJson({ ok: true, plan: result.plan, goal: result.goal, summary: summarizeUlwLoopPlan(result.plan) });
|
|
140
126
|
else {
|
|
@@ -151,10 +137,12 @@ export async function criteria(
|
|
|
151
137
|
scope?: UlwLoopScope,
|
|
152
138
|
): Promise<number> {
|
|
153
139
|
const goalId = required(argv, "--goal-id");
|
|
154
|
-
const
|
|
155
|
-
if (json) printJson({ ok: true, goalId:
|
|
140
|
+
const result = unwrap(await toolkitFor(repoRoot, scope).criteria({ goalId }));
|
|
141
|
+
if (json) printJson({ ok: true, goalId: result.goalId, criteria: result.criteria });
|
|
156
142
|
else {
|
|
157
|
-
process.stdout.write(
|
|
143
|
+
process.stdout.write(
|
|
144
|
+
`criteria for ${result.goalId}:\n${result.criteria.map(formatCriterionForCli).join("\n")}\n`,
|
|
145
|
+
);
|
|
158
146
|
}
|
|
159
147
|
return 0;
|
|
160
148
|
}
|
|
@@ -165,7 +153,7 @@ export async function captureEvidence(
|
|
|
165
153
|
json: boolean,
|
|
166
154
|
scope?: UlwLoopScope,
|
|
167
155
|
): Promise<number> {
|
|
168
|
-
const result = await
|
|
156
|
+
const result = unwrap(await toolkitFor(repoRoot, scope).recordEvidence(parseRecordEvidenceArgs(argv)));
|
|
169
157
|
if (json) printJson({ ok: true, ...result, summary: summarizeUlwLoopPlan(result.plan) });
|
|
170
158
|
else {
|
|
171
159
|
process.stdout.write(
|
|
@@ -185,16 +173,14 @@ export async function reviewBlockers(
|
|
|
185
173
|
if (codexGoalJson === undefined) {
|
|
186
174
|
throw new UlwLoopError("Missing --codex-goal-json.", "ULW_LOOP_CODEX_GOAL_JSON_REQUIRED");
|
|
187
175
|
}
|
|
188
|
-
const result =
|
|
189
|
-
repoRoot,
|
|
190
|
-
{
|
|
176
|
+
const result = unwrap(
|
|
177
|
+
await toolkitFor(repoRoot, scope).recordReviewBlockers({
|
|
191
178
|
goalId: required(argv, "--goal-id"),
|
|
192
179
|
title: required(argv, "--title"),
|
|
193
180
|
objective: required(argv, "--objective"),
|
|
194
181
|
evidence: required(argv, "--evidence"),
|
|
195
182
|
codexGoalJson,
|
|
196
|
-
},
|
|
197
|
-
scope,
|
|
183
|
+
}),
|
|
198
184
|
);
|
|
199
185
|
if (json) {
|
|
200
186
|
printJson({
|
|
@@ -215,6 +201,28 @@ export async function reviewBlockers(
|
|
|
215
201
|
return 0;
|
|
216
202
|
}
|
|
217
203
|
|
|
204
|
+
// Every subcommand runs through the SDK so state logic lives in one place; the CLI keeps only argv
|
|
205
|
+
// parsing and the legacy output shapes. A failed envelope is rethrown as the typed error the CLI's
|
|
206
|
+
// JSON error path already prints.
|
|
207
|
+
function toolkitFor(repoRoot: string, scope?: UlwLoopScope): AgentToolkit {
|
|
208
|
+
const sessionId = scope?.sessionId?.trim();
|
|
209
|
+
if (sessionId === undefined || sessionId.length === 0) {
|
|
210
|
+
throw new UlwLoopError("Missing --session-id.", "ULW_LOOP_SESSION_ID_REQUIRED", {
|
|
211
|
+
details: { flag: "--session-id" },
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return createAgentToolkit({ cwd: repoRoot, sessionId, surface: resolveToolkitSurface() });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function unwrap<Result>(
|
|
218
|
+
response:
|
|
219
|
+
| { readonly ok: true; readonly result: Result }
|
|
220
|
+
| { readonly ok: false; readonly error: { readonly code: string; readonly message: string } },
|
|
221
|
+
): Result {
|
|
222
|
+
if (response.ok) return response.result;
|
|
223
|
+
throw new UlwLoopError(response.error.message, response.error.code);
|
|
224
|
+
}
|
|
225
|
+
|
|
218
226
|
function formatCriterionForCli(criterion: UlwLoopItem["successCriteria"][number]): string {
|
|
219
227
|
const marker = isEssentialCriterion(criterion) ? "essential" : "non-essential";
|
|
220
228
|
return `- ${criterion.id} [${criterion.status}] [${marker}] (${criterion.userModel}) ${criterion.scenario} evidence: ${criterion.capturedEvidence ?? "pending"}`;
|
|
@@ -225,9 +233,3 @@ function required(argv: readonly string[], flag: string): string {
|
|
|
225
233
|
if (value) return value;
|
|
226
234
|
throw new UlwLoopError(`Missing ${flag}.`, "ULW_LOOP_ARGUMENT_MISSING", { details: { flag } });
|
|
227
235
|
}
|
|
228
|
-
|
|
229
|
-
function findGoal(plan: { readonly goals: readonly UlwLoopItem[] }, goalId: string): UlwLoopItem {
|
|
230
|
-
const goal = plan.goals.find((candidate) => candidate.id === goalId);
|
|
231
|
-
if (goal !== undefined) return goal;
|
|
232
|
-
throw new UlwLoopError(`Unknown ulw-loop id: ${goalId}.`, "ULW_LOOP_GOAL_NOT_FOUND", { details: { goalId } });
|
|
233
|
-
}
|