@gajae-code/coding-agent 0.2.5 → 0.3.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/CHANGELOG.md +10 -0
- package/dist/types/async/job-manager.d.ts +84 -2
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/settings-schema.d.ts +6 -0
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/deep-interview/render-middleware.d.ts +5 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +24 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +137 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +30 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +2 -29
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +8 -0
- package/dist/types/skill-state/active-state.d.ts +2 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +24 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/types.d.ts +55 -3
- package/dist/types/tools/subagent.d.ts +11 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +298 -6
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/harness.ts +592 -0
- package/src/commands/team.ts +36 -39
- package/src/config/settings-schema.ts +7 -0
- package/src/config/settings.ts +5 -0
- package/src/deep-interview/render-middleware.ts +366 -0
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +78 -11
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +40 -21
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +25 -10
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +132 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +733 -21
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +718 -0
- package/src/gjc-runtime/team-runtime.ts +1083 -89
- package/src/gjc-runtime/ultragoal-runtime.ts +348 -19
- package/src/gjc-runtime/workflow-manifest.generated.json +1497 -0
- package/src/gjc-runtime/workflow-manifest.ts +425 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +137 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +553 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +97 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +24 -41
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/hook-selector.ts +72 -2
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +9 -1
- package/src/modes/controllers/selector-controller.ts +2 -1
- package/src/modes/interactive-mode.ts +1 -0
- package/src/modes/types.ts +1 -0
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/tools/subagent.md +33 -3
- package/src/sdk.ts +4 -0
- package/src/session/agent-session.ts +231 -33
- package/src/session/session-manager.ts +13 -1
- package/src/skill-state/active-state.ts +58 -65
- package/src/skill-state/deep-interview-mutation-guard.ts +91 -13
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +26 -0
- package/src/task/executor.ts +50 -8
- package/src/task/index.ts +120 -8
- package/src/task/render.ts +6 -3
- package/src/task/types.ts +56 -3
- package/src/tools/ask.ts +28 -7
- package/src/tools/subagent.ts +255 -64
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* operate(goal) — autonomous owner-driven lifecycle (M9) integrating the recovery loop (M6).
|
|
3
|
+
*
|
|
4
|
+
* start -> submit(single-flight) -> [observe -> classify -> recover]* -> finalize(evidence-gated).
|
|
5
|
+
* Destructive recovery (restart-clean/preserve-delta/fallback) writes a valid `vanish` receipt
|
|
6
|
+
* BEFORE acting; dirty/unknown deltas are preserved, never clean-restarted. The loop is bounded
|
|
7
|
+
* by `maxIterations` and the per-classification retry budgets.
|
|
8
|
+
*
|
|
9
|
+
* External effects (RPC, observation, validation/git/gh) are injected so the whole lifecycle is
|
|
10
|
+
* unit/e2e-testable with a fake harness.
|
|
11
|
+
*/
|
|
12
|
+
import { randomBytes } from "node:crypto";
|
|
13
|
+
import { type FinalizeChecks, type FinalizeResult, runFinalize, type ValidationCommandSpec } from "./finalize";
|
|
14
|
+
import { type PreserveResult, preserveDirtyWorktree } from "./preserve";
|
|
15
|
+
import {
|
|
16
|
+
buildReceipt,
|
|
17
|
+
type ReceiptSubject,
|
|
18
|
+
requiresVanishBeforeAction,
|
|
19
|
+
type VanishEvidence,
|
|
20
|
+
validateReceipt,
|
|
21
|
+
} from "./receipts";
|
|
22
|
+
import { type HarnessRpc, singleFlightAccept } from "./rpc-adapter";
|
|
23
|
+
import { writeReceiptImmutable } from "./storage";
|
|
24
|
+
import {
|
|
25
|
+
DEFAULT_RETRY_BUDGET,
|
|
26
|
+
type HarnessLifecycle,
|
|
27
|
+
type Observation,
|
|
28
|
+
type RecoveryClassification,
|
|
29
|
+
type RetryBudget,
|
|
30
|
+
type Severity,
|
|
31
|
+
} from "./types";
|
|
32
|
+
|
|
33
|
+
export interface OperateOptions {
|
|
34
|
+
root: string;
|
|
35
|
+
sessionId: string;
|
|
36
|
+
workspace: string;
|
|
37
|
+
branch: string;
|
|
38
|
+
rpc: HarnessRpc;
|
|
39
|
+
/** Factory used to (re)create the RPC subprocess on restart recovery. Defaults to reusing `rpc`. */
|
|
40
|
+
rpcFactory?: () => HarnessRpc;
|
|
41
|
+
/** Bounded observation provider (scripted in tests; real = git + rpc state). */
|
|
42
|
+
observe: () => Promise<Observation>;
|
|
43
|
+
/** Real dirty-worktree preservation; injectable for tests. Defaults to git stash/diff capture. */
|
|
44
|
+
preserve?: (workspace: string) => PreserveResult;
|
|
45
|
+
finalizeChecks: FinalizeChecks;
|
|
46
|
+
validationCommands?: ValidationCommandSpec[];
|
|
47
|
+
retryBudget?: Partial<RetryBudget>;
|
|
48
|
+
acceptanceTimeoutMs?: number;
|
|
49
|
+
maxIterations?: number;
|
|
50
|
+
/** Injected event emitter. Production owner calls must pass the lease-guarded single-writer #emit. */
|
|
51
|
+
emit: (severity: Severity, kind: string, evidence: Record<string, unknown>) => Promise<void>;
|
|
52
|
+
clock?: () => number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface OperateResult {
|
|
56
|
+
completed: boolean;
|
|
57
|
+
lifecycle: HarnessLifecycle;
|
|
58
|
+
iterations: number;
|
|
59
|
+
classifications: RecoveryClassification[];
|
|
60
|
+
vanishReceiptIds: string[];
|
|
61
|
+
finalize?: FinalizeResult;
|
|
62
|
+
blockers: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function operate(goal: string, opts: OperateOptions): Promise<OperateResult> {
|
|
66
|
+
const budget: RetryBudget = { ...DEFAULT_RETRY_BUDGET, ...opts.retryBudget };
|
|
67
|
+
const acceptanceTimeoutMs = opts.acceptanceTimeoutMs ?? 30_000;
|
|
68
|
+
const maxIterations = opts.maxIterations ?? 10;
|
|
69
|
+
const subject: ReceiptSubject = { workspace: opts.workspace, branch: opts.branch, head: null, commit: null };
|
|
70
|
+
const classifications: RecoveryClassification[] = [];
|
|
71
|
+
const vanishReceiptIds: string[] = [];
|
|
72
|
+
const blockers: string[] = [];
|
|
73
|
+
|
|
74
|
+
let rpc = opts.rpc;
|
|
75
|
+
|
|
76
|
+
const now = (): string => new Date(opts.clock ? opts.clock() : Date.now()).toISOString();
|
|
77
|
+
let lifecycle: HarnessLifecycle = "started";
|
|
78
|
+
const emit = opts.emit;
|
|
79
|
+
|
|
80
|
+
const writeVanish = async (obs: Observation, classification: RecoveryClassification): Promise<boolean> => {
|
|
81
|
+
const dirty = obs.gitDelta === "dirty" || obs.gitDelta === "unknown";
|
|
82
|
+
let untrackedManifest: VanishEvidence["untrackedManifest"] = [];
|
|
83
|
+
let stashRef: string | null = null;
|
|
84
|
+
let snapshotComplete = true;
|
|
85
|
+
let gitStatusPorcelain = obs.observedSignals.join(",");
|
|
86
|
+
if (dirty) {
|
|
87
|
+
const preserve = opts.preserve ?? preserveDirtyWorktree;
|
|
88
|
+
const p = preserve(opts.workspace);
|
|
89
|
+
untrackedManifest = p.untrackedManifest;
|
|
90
|
+
stashRef = p.stashRef;
|
|
91
|
+
snapshotComplete = p.snapshotComplete;
|
|
92
|
+
gitStatusPorcelain = `tracked-diff-sha:${p.trackedDiffSha256};untracked:${p.untrackedManifest.length};stash:${p.stashRef ?? "none"}`;
|
|
93
|
+
}
|
|
94
|
+
const evidence: VanishEvidence = {
|
|
95
|
+
classification,
|
|
96
|
+
gitDelta: obs.gitDelta,
|
|
97
|
+
gitStatusPorcelain,
|
|
98
|
+
untrackedManifest,
|
|
99
|
+
preservation: dirty && stashRef ? "stash" : "snapshot",
|
|
100
|
+
stashRef,
|
|
101
|
+
snapshotComplete,
|
|
102
|
+
forbiddenActions: dirty ? ["restart-clean", "delete", "reset"] : [],
|
|
103
|
+
};
|
|
104
|
+
const receipt = buildReceipt<VanishEvidence>({
|
|
105
|
+
receiptId: `vanish-${Date.now()}-${randomBytes(4).toString("hex")}`,
|
|
106
|
+
sessionId: opts.sessionId,
|
|
107
|
+
family: "vanish",
|
|
108
|
+
source: "operate",
|
|
109
|
+
subject,
|
|
110
|
+
evidence,
|
|
111
|
+
createdAt: now(),
|
|
112
|
+
});
|
|
113
|
+
const outcome = validateReceipt(receipt);
|
|
114
|
+
await writeReceiptImmutable(opts.root, opts.sessionId, "vanish", receipt.receiptId, receipt);
|
|
115
|
+
vanishReceiptIds.push(receipt.receiptId);
|
|
116
|
+
await emit(outcome.valid ? "critical" : "warn", "vanish_receipt", { classification, valid: outcome.valid });
|
|
117
|
+
return outcome.valid;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const submit = async (): Promise<boolean> => {
|
|
121
|
+
const acc = await singleFlightAccept(rpc, goal, acceptanceTimeoutMs);
|
|
122
|
+
await emit(acc.accepted ? "info" : "warn", acc.accepted ? "prompt_accepted" : "prompt_not_accepted", {
|
|
123
|
+
reason: acc.reason,
|
|
124
|
+
});
|
|
125
|
+
return acc.accepted;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
await emit("info", "operate_started", { goal });
|
|
129
|
+
let accepted = await submit();
|
|
130
|
+
lifecycle = accepted ? "observing" : "submitted";
|
|
131
|
+
let iterations = 0;
|
|
132
|
+
|
|
133
|
+
while (iterations < maxIterations) {
|
|
134
|
+
iterations++;
|
|
135
|
+
const obs = await opts.observe();
|
|
136
|
+
const decision = classifyRecoveryLocal(obs, budget, accepted);
|
|
137
|
+
classifications.push(decision.classification);
|
|
138
|
+
await emit(decision.severity, `classified:${decision.classification}`, { reason: decision.reason });
|
|
139
|
+
|
|
140
|
+
if (decision.classification === "continue") {
|
|
141
|
+
if (obs.observedSignals.includes("completed") || obs.lifecycle === "finalizing") {
|
|
142
|
+
lifecycle = "finalizing";
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Destructive/recovery actions require a valid vanish receipt first.
|
|
149
|
+
if (requiresVanishBeforeAction(decision.classification)) {
|
|
150
|
+
const safe = await writeVanish(obs, decision.classification);
|
|
151
|
+
if (!safe) {
|
|
152
|
+
lifecycle = "blocked";
|
|
153
|
+
blockers.push("invalid-vanish-receipt");
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (decision.classification === "reinject-prompt") {
|
|
159
|
+
budget.reinjectPrompt = Math.max(0, budget.reinjectPrompt - 1);
|
|
160
|
+
accepted = await submit();
|
|
161
|
+
} else if (decision.classification === "restart-clean") {
|
|
162
|
+
budget.zeroDeltaVanish = Math.max(0, budget.zeroDeltaVanish - 1);
|
|
163
|
+
rpc = opts.rpcFactory ? opts.rpcFactory() : rpc;
|
|
164
|
+
accepted = await submit();
|
|
165
|
+
} else if (decision.classification === "restart-preserve-delta") {
|
|
166
|
+
budget.dirtyVanishPreserve = Math.max(0, budget.dirtyVanishPreserve - 1);
|
|
167
|
+
rpc = opts.rpcFactory ? opts.rpcFactory() : rpc;
|
|
168
|
+
accepted = await submit();
|
|
169
|
+
} else if (decision.classification === "fallback-codex-exec") {
|
|
170
|
+
lifecycle = "blocked";
|
|
171
|
+
blockers.push("fallback-codex-exec-requested");
|
|
172
|
+
break;
|
|
173
|
+
} else if (decision.classification === "human-check") {
|
|
174
|
+
lifecycle = "blocked";
|
|
175
|
+
blockers.push("human-check-required");
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (lifecycle === "blocked") {
|
|
181
|
+
await emit("critical", "operate_blocked", { blockers });
|
|
182
|
+
return { completed: false, lifecycle, iterations, classifications, vanishReceiptIds, blockers };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// B3: never finalize on loop-exhaustion — require an explicit observed completion.
|
|
186
|
+
if (lifecycle !== "finalizing") {
|
|
187
|
+
blockers.push("no-observed-completion");
|
|
188
|
+
await emit("critical", "operate_blocked", { blockers });
|
|
189
|
+
return { completed: false, lifecycle: "blocked", iterations, classifications, vanishReceiptIds, blockers };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const finalize = await runFinalize({
|
|
193
|
+
root: opts.root,
|
|
194
|
+
sessionId: opts.sessionId,
|
|
195
|
+
workspace: opts.workspace,
|
|
196
|
+
branch: opts.branch,
|
|
197
|
+
requireTests: true,
|
|
198
|
+
requireCommit: true,
|
|
199
|
+
requirePr: true,
|
|
200
|
+
validationCommands: opts.validationCommands,
|
|
201
|
+
checks: opts.finalizeChecks,
|
|
202
|
+
clock: opts.clock,
|
|
203
|
+
});
|
|
204
|
+
await emit(finalize.completed ? "info" : "critical", "operate_finalized", {
|
|
205
|
+
completed: finalize.completed,
|
|
206
|
+
blockers: finalize.blockers,
|
|
207
|
+
});
|
|
208
|
+
return {
|
|
209
|
+
completed: finalize.completed,
|
|
210
|
+
lifecycle: finalize.completed ? "completed" : "blocked",
|
|
211
|
+
iterations,
|
|
212
|
+
classifications,
|
|
213
|
+
vanishReceiptIds,
|
|
214
|
+
finalize,
|
|
215
|
+
blockers: finalize.blockers,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Local import indirection keeps the classifier dependency explicit at the call site.
|
|
220
|
+
import { classifyRecovery } from "./classifier";
|
|
221
|
+
import type { RecoveryDecision } from "./types";
|
|
222
|
+
|
|
223
|
+
function classifyRecoveryLocal(obs: Observation, budget: RetryBudget, acceptedPromptActive: boolean): RecoveryDecision {
|
|
224
|
+
return classifyRecovery({ observation: obs, retryBudget: budget, acceptedPromptActive });
|
|
225
|
+
}
|