@tea-agent/loop-agent 0.17.0 → 0.17.1
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 +7 -0
- package/dist/worker/console/pi-readiness.js +190 -18
- package/dist/worker/console/static/assets/index-Rt0TqimP.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/materialize/harness-task-materializer.js +34 -0
- package/package.json +1 -1
- package/dist/worker/console/static/assets/index-Dnj0RVs8.css +0 -1
- /package/dist/worker/console/static/assets/{index-CbnMgdWa.js → index-BEIdBogJ.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## [0.17.1] - 2026-07-22
|
|
4
|
+
|
|
5
|
+
### 修复
|
|
6
|
+
|
|
7
|
+
- TaskSpec materialize 在写入 task.json、需求和约束后会自动 adopt 或刷新 managed Task Contract,避免生成 v4 DAG 后立刻因 binding drift 无法 validate/run。
|
|
8
|
+
- Operator Console 的 Pi readiness 改为真实探测 SDK、认证和可用模型;本机已配置 Pi 时不再误报 `setup-required`,界面同步对齐 Observe 暖白画布。
|
|
9
|
+
|
|
3
10
|
## [0.17.0] - 2026-07-22
|
|
4
11
|
|
|
5
12
|
### 新增
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Pi readiness gate for Official Console Happy Path.
|
|
3
3
|
* setup-required | ready | degraded — no pure form fallback.
|
|
4
4
|
*/
|
|
5
|
+
import path from "node:path";
|
|
5
6
|
const SETUP_BLOCKED = Object.freeze([
|
|
6
7
|
"newTask",
|
|
7
8
|
"importPrd",
|
|
@@ -20,6 +21,13 @@ const DEGRADED_BLOCKED = Object.freeze([
|
|
|
20
21
|
"runDag",
|
|
21
22
|
// new Interview may resume; brand-new apply/run still blocked
|
|
22
23
|
]);
|
|
24
|
+
/** Default Interview / DAG model matrix (provider, modelId). */
|
|
25
|
+
const INTERVIEW_MODEL_CANDIDATES = Object.freeze([
|
|
26
|
+
["openai", "gpt-5.5"],
|
|
27
|
+
["openai", "gpt-5.3-codex-spark"],
|
|
28
|
+
["zai", "glm-5.2"],
|
|
29
|
+
["zhipu", "glm-5.2"],
|
|
30
|
+
]);
|
|
23
31
|
export function classifyPiReadiness(probe) {
|
|
24
32
|
if (!probe.sdkAvailable ||
|
|
25
33
|
!probe.authReady ||
|
|
@@ -57,30 +65,194 @@ export function isActionBlockedByReadiness(state, action) {
|
|
|
57
65
|
}
|
|
58
66
|
return DEGRADED_BLOCKED.includes(action);
|
|
59
67
|
}
|
|
68
|
+
function countAvailableModels(snapshot) {
|
|
69
|
+
return Array.isArray(snapshot.available) ? snapshot.available.length : 0;
|
|
70
|
+
}
|
|
71
|
+
function hasProviderConfig(snapshot) {
|
|
72
|
+
const authKeys = snapshot.auth && typeof snapshot.auth === "object"
|
|
73
|
+
? Object.keys(snapshot.auth).length
|
|
74
|
+
: 0;
|
|
75
|
+
const configured = snapshot.configuredProviders &&
|
|
76
|
+
typeof snapshot.configuredProviders === "object"
|
|
77
|
+
? Object.keys(snapshot.configuredProviders).length
|
|
78
|
+
: 0;
|
|
79
|
+
const stored = snapshot.storedProviders && typeof snapshot.storedProviders === "object"
|
|
80
|
+
? Object.keys(snapshot.storedProviders).length
|
|
81
|
+
: 0;
|
|
82
|
+
return authKeys > 0 || configured > 0 || stored > 0;
|
|
83
|
+
}
|
|
60
84
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
85
|
+
* Discover live Pi SDK / auth / model readiness via ModelRuntime.
|
|
86
|
+
* Kept inside worker/console (dynamic import) so Worker does not depend on
|
|
87
|
+
* executors; contract matches pi-sdk-executor 0.80.10.
|
|
64
88
|
*/
|
|
65
|
-
export async function
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
export async function discoverPiReadinessProbe() {
|
|
90
|
+
let sdk;
|
|
91
|
+
try {
|
|
92
|
+
sdk = (await import("@earendil-works/pi-coding-agent"));
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
+
return {
|
|
97
|
+
sdkAvailable: false,
|
|
98
|
+
authReady: false,
|
|
99
|
+
modelsAvailable: false,
|
|
100
|
+
interviewSessionOk: false,
|
|
101
|
+
details: {
|
|
102
|
+
sdkMessage: `Pi SDK import failed: ${message}`,
|
|
103
|
+
authMessage: "skipped (sdk unavailable)",
|
|
104
|
+
modelsMessage: "skipped (sdk unavailable)",
|
|
105
|
+
sessionMessage: "skipped (sdk unavailable)",
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const ModelRuntime = sdk.ModelRuntime;
|
|
110
|
+
if (typeof sdk.createAgentSession !== "function" ||
|
|
111
|
+
typeof sdk.getAgentDir !== "function" ||
|
|
112
|
+
typeof ModelRuntime?.create !== "function") {
|
|
113
|
+
return {
|
|
114
|
+
sdkAvailable: false,
|
|
115
|
+
authReady: false,
|
|
116
|
+
modelsAvailable: false,
|
|
117
|
+
interviewSessionOk: false,
|
|
118
|
+
details: {
|
|
119
|
+
sdkMessage: "Pi SDK incompatible: requires createAgentSession, getAgentDir, and ModelRuntime.create (0.80.10)",
|
|
120
|
+
authMessage: "skipped (sdk incompatible)",
|
|
121
|
+
modelsMessage: "skipped (sdk incompatible)",
|
|
122
|
+
sessionMessage: "skipped (sdk incompatible)",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
let agentDir;
|
|
127
|
+
try {
|
|
128
|
+
agentDir = sdk.getAgentDir();
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
132
|
+
return {
|
|
133
|
+
sdkAvailable: false,
|
|
134
|
+
authReady: false,
|
|
135
|
+
modelsAvailable: false,
|
|
136
|
+
interviewSessionOk: false,
|
|
137
|
+
details: {
|
|
138
|
+
sdkMessage: `getAgentDir failed: ${message}`,
|
|
139
|
+
authMessage: "skipped",
|
|
140
|
+
modelsMessage: "skipped",
|
|
141
|
+
sessionMessage: "skipped",
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
let runtime;
|
|
146
|
+
try {
|
|
147
|
+
runtime = await ModelRuntime.create({
|
|
148
|
+
authPath: path.join(agentDir, "auth.json"),
|
|
149
|
+
modelsPath: path.join(agentDir, "models.json"),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
154
|
+
return {
|
|
155
|
+
sdkAvailable: true,
|
|
156
|
+
authReady: false,
|
|
157
|
+
modelsAvailable: false,
|
|
158
|
+
interviewSessionOk: false,
|
|
159
|
+
details: {
|
|
160
|
+
sdkMessage: "Pi SDK 0.80.10 contract available",
|
|
161
|
+
authMessage: `ModelRuntime.create failed: ${message}`,
|
|
162
|
+
modelsMessage: "skipped (runtime init failed)",
|
|
163
|
+
sessionMessage: "skipped (runtime init failed)",
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const snapshot = runtime.snapshot ?? {};
|
|
168
|
+
const availableCount = countAvailableModels(snapshot);
|
|
169
|
+
const providerConfigured = hasProviderConfig(snapshot);
|
|
170
|
+
const resolvedInterviewModels = [];
|
|
171
|
+
if (typeof runtime.getModel === "function") {
|
|
172
|
+
for (const [provider, modelId] of INTERVIEW_MODEL_CANDIDATES) {
|
|
173
|
+
try {
|
|
174
|
+
if (runtime.getModel(provider, modelId)) {
|
|
175
|
+
resolvedInterviewModels.push(`${provider}/${modelId}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// ignore per-model resolution errors
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const authReady = availableCount > 0 || providerConfigured;
|
|
184
|
+
const modelsAvailable = availableCount > 0 || resolvedInterviewModels.length > 0;
|
|
185
|
+
let interviewSessionOk = false;
|
|
186
|
+
let sessionMessage;
|
|
187
|
+
if (!authReady || !modelsAvailable) {
|
|
188
|
+
sessionMessage =
|
|
189
|
+
"Interview session preflight skipped (auth/models not ready)";
|
|
190
|
+
}
|
|
191
|
+
else if (typeof sdk.SessionManager?.inMemory !== "function") {
|
|
192
|
+
sessionMessage =
|
|
193
|
+
"SessionManager.inMemory unavailable; Interview session preflight failed";
|
|
68
194
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
195
|
+
else {
|
|
196
|
+
try {
|
|
197
|
+
sdk.SessionManager.inMemory(process.cwd());
|
|
198
|
+
interviewSessionOk = true;
|
|
199
|
+
sessionMessage = `Interview session preflight ok (${availableCount} available model(s); matrix=${resolvedInterviewModels.join(",") || "none"})`;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
203
|
+
sessionMessage = `Interview session preflight failed: ${message}`;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
sdkAvailable: true,
|
|
208
|
+
authReady,
|
|
209
|
+
modelsAvailable,
|
|
210
|
+
interviewSessionOk,
|
|
76
211
|
details: {
|
|
77
|
-
sdkMessage:
|
|
78
|
-
authMessage:
|
|
79
|
-
|
|
80
|
-
|
|
212
|
+
sdkMessage: `Pi SDK 0.80.10 contract available (agentDir=${agentDir})`,
|
|
213
|
+
authMessage: authReady
|
|
214
|
+
? `credentials/providers ready (available=${availableCount}, providerConfig=${providerConfigured})`
|
|
215
|
+
: "no available models and no stored/configured provider credentials (set Pi auth or provider API keys)",
|
|
216
|
+
modelsMessage: modelsAvailable
|
|
217
|
+
? `models ready (available=${availableCount}, interviewMatrix=${resolvedInterviewModels.join(",") || "none"})`
|
|
218
|
+
: "no available models for Interview",
|
|
219
|
+
sessionMessage,
|
|
81
220
|
},
|
|
82
221
|
};
|
|
83
|
-
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Default probe for process boot. Discovers live Pi ModelRuntime when no
|
|
225
|
+
* override is injected. Tests inject override for determinism.
|
|
226
|
+
*/
|
|
227
|
+
export async function probePiReadiness(options) {
|
|
228
|
+
if (options?.override) {
|
|
229
|
+
return buildPiReadinessReport(options.override);
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const probe = await discoverPiReadinessProbe();
|
|
233
|
+
if (options?.siblingControllerOk && !probe.details) {
|
|
234
|
+
probe.details = {};
|
|
235
|
+
}
|
|
236
|
+
if (options?.siblingControllerOk && probe.details) {
|
|
237
|
+
probe.details.sdkMessage = `${probe.details.sdkMessage ?? "sdk probed"}; sibling controller identity ok`;
|
|
238
|
+
}
|
|
239
|
+
return buildPiReadinessReport(probe);
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
243
|
+
return buildPiReadinessReport({
|
|
244
|
+
sdkAvailable: false,
|
|
245
|
+
authReady: false,
|
|
246
|
+
modelsAvailable: false,
|
|
247
|
+
interviewSessionOk: false,
|
|
248
|
+
details: {
|
|
249
|
+
sdkMessage: `Pi readiness discovery error: ${message}`,
|
|
250
|
+
authMessage: "no credential probe completed",
|
|
251
|
+
modelsMessage: "no model list available",
|
|
252
|
+
sessionMessage: "Interview session preflight not run",
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
84
256
|
}
|
|
85
257
|
export function readinessGateFailure(report, action) {
|
|
86
258
|
if (!isActionBlockedByReadiness(report.state, action))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--canvas: #f7f7f4;--canvas-soft: #fafaf7;--surface: #ffffff;--surface-muted: #f0efe9;--ink: #26251e;--body: #5a5852;--muted: #807d72;--muted-soft: #a09c92;--hairline: #e6e5e0;--hairline-strong: #cfcdc4;--orange: #f54e00;--orange-active: #d04200;--blue: #3f6ea4;--green: #1f8a65;--amber: #a66a00;--red: #c72d53;--peach: #f1d8cb;--mint: #d8ead5;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 8px;--transition-fast: .16s ease;--shadow-panel: 0 1px 2px rgb(38 37 30 / 4%);color-scheme:light;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;line-height:1.5}*{box-sizing:border-box}html{background:var(--canvas)}body{margin:0;min-height:100vh;min-width:320px;background:var(--canvas);color:var(--ink);font-size:14px}button,input,select,textarea{font:inherit}a{color:inherit}code,.cmd,.kv dd code{font-family:JetBrains Mono,SFMono-Regular,Consolas,monospace}.console-shell{width:min(100% - 48px,1080px);margin:0 auto;padding:var(--space-8) 0 var(--space-12, 48px)}.console-header{display:grid;gap:var(--space-5);grid-template-columns:1.4fr 1fr;align-items:start;margin-bottom:var(--space-6);padding-bottom:var(--space-5);border-bottom:1px solid var(--hairline)}.eyebrow{margin:0 0 var(--space-2);color:var(--orange);font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}h1{margin:0;color:var(--ink);font-size:28px;font-weight:500;line-height:1.16;letter-spacing:0}.subtitle{max-width:66ch;margin:var(--space-2) 0 0;color:var(--body)}.muted{color:var(--muted)}.readiness{border:1px solid var(--hairline);background:var(--surface);border-radius:var(--radius-lg);padding:var(--space-5);box-shadow:var(--shadow-panel)}.readiness[data-state=setup-required]{border-color:color-mix(in srgb,var(--amber) 45%,var(--hairline));background:color-mix(in srgb,var(--peach) 35%,var(--surface))}.readiness[data-state=ready]{border-color:color-mix(in srgb,var(--green) 40%,var(--hairline));background:color-mix(in srgb,var(--mint) 40%,var(--surface))}.readiness[data-state=degraded]{border-color:color-mix(in srgb,var(--amber) 45%,var(--hairline));background:color-mix(in srgb,var(--peach) 28%,var(--surface))}.readiness-label{display:block;margin-bottom:var(--space-1);color:var(--muted);font-size:12px;font-weight:600}.readiness strong{color:var(--ink);font-size:16px;font-weight:650}.readiness[data-state=ready] strong{color:var(--green)}.readiness[data-state=setup-required] strong,.readiness[data-state=degraded] strong{color:var(--amber)}.readiness .muted{margin:var(--space-2) 0 0}.console-main{display:grid;gap:var(--space-4)}.panel{min-width:0;border:1px solid var(--hairline);background:var(--surface);border-radius:var(--radius-lg);padding:var(--space-5);box-shadow:var(--shadow-panel)}.panel h2{margin:0 0 var(--space-4);color:var(--ink);font-size:16px;font-weight:700}.panel h3{margin:0 0 var(--space-3);color:var(--ink);font-size:14px;font-weight:700}.empty-state{border:1px dashed var(--hairline-strong);border-radius:var(--radius-md);padding:var(--space-5);background:var(--canvas-soft);color:var(--body)}.empty-title{margin:0 0 var(--space-2);color:var(--ink);font-weight:700}.checklist{margin:var(--space-3) 0 0;padding-left:1.2rem;color:var(--body)}.kv{display:grid;gap:var(--space-2);margin:0}.kv div{display:grid;grid-template-columns:10rem 1fr;gap:var(--space-2);padding:var(--space-2) 0;border-bottom:1px solid var(--hairline)}.kv div:last-child{border-bottom:none}.kv dt{color:var(--muted);font-size:12px;font-weight:600}.kv dd{margin:0;color:var(--ink);overflow-wrap:anywhere}code{font-size:.85rem;color:var(--ink)}.cmd{display:block;margin:0;padding:var(--space-3);border:1px solid var(--hairline);border-radius:var(--radius-md);background:#f0ebe3;color:var(--ink);font-size:.85rem;overflow-x:auto;word-break:break-all}.external-link{color:var(--orange)}.error{color:var(--red)}.wizard-steps{display:flex;flex-wrap:wrap;gap:var(--space-2);list-style:none;padding:0;margin:0 0 var(--space-4);font-size:12px;color:var(--muted)}.wizard-steps li{min-height:28px;display:inline-flex;align-items:center;border:1px solid var(--hairline);border-radius:var(--radius-sm);padding:0 10px;background:var(--canvas-soft);font-weight:600}.wizard-steps li[data-active=true]{border-color:var(--orange);background:var(--surface);color:var(--ink)}.field{display:grid;gap:var(--space-1);margin:0 0 var(--space-4)}.field span{font-size:12px;font-weight:600}.field input{min-height:36px;padding:0 var(--space-3);border:1px solid var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);color:var(--ink)}.field input:focus-visible{outline:2px solid color-mix(in srgb,var(--orange) 55%,white);outline-offset:2px;border-color:var(--orange)}.field input:disabled{background:var(--surface-muted);color:var(--muted);cursor:not-allowed}.cta-row,.wizard-body,.recovery-cta-row{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:flex-start}.cta-row{margin-top:var(--space-3)}.cta-row .cmd{flex:1 1 100%}button{appearance:none;display:inline-flex;align-items:center;justify-content:center;min-height:36px;padding:0 14px;border:1px solid var(--hairline-strong);border-radius:var(--radius-md);background:var(--surface);color:var(--ink);font-size:13px;font-weight:600;cursor:pointer;transition:border-color var(--transition-fast),background-color var(--transition-fast),color var(--transition-fast)}button:hover:not(:disabled),button:focus-visible:not(:disabled){border-color:var(--orange);background:var(--surface);color:var(--ink);outline:none}button:disabled{opacity:.5;cursor:not-allowed}.wizard-body button,.cta-row>button:first-child,button.primary{border-color:var(--orange);background:var(--orange);color:#fff}.wizard-body button:hover:not(:disabled),.cta-row>button:first-child:hover:not(:disabled),button.primary:hover:not(:disabled){border-color:var(--orange-active);background:var(--orange-active);color:#fff}.wizard-body{display:block}.wizard-body>div{display:grid;gap:var(--space-2);justify-items:start}.wizard-body button+button{margin-left:0}.recovery-cta-row{margin-top:var(--space-3)}.recovery-cta-row button[disabled]{opacity:.4}@media(max-width:800px){.console-shell{width:min(100% - 24px,1080px);padding-top:var(--space-5)}.console-header,.kv div{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Loop Operator Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BEIdBogJ.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Rt0TqimP.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -4,6 +4,9 @@ import path from "node:path";
|
|
|
4
4
|
import YAML from "yaml";
|
|
5
5
|
import { writeTaskConfig, writeTaskArtifactFile } from "../../infrastructure/harness/task-store.js";
|
|
6
6
|
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
7
|
+
import { adoptTaskContract } from "../../task/contract/adopt.js";
|
|
8
|
+
import { observeTaskContractState } from "../../task/contract/observe.js";
|
|
9
|
+
import { sha256Utf8 } from "../../task/contract/hash.js";
|
|
7
10
|
import { resolveLoopAgentProfile } from "../profile-mapping.js";
|
|
8
11
|
import { mapRiskLevelToComplexity } from "../task-spec/complexity-mapping.js";
|
|
9
12
|
import { resolveWorkflow } from "../task-spec/workflow-routing.js";
|
|
@@ -89,6 +92,11 @@ export async function materializeTaskSpec(options) {
|
|
|
89
92
|
}), "utf-8");
|
|
90
93
|
await writeFile(sourcePaths.constraintsPath, renderConstraintsMarkdown(options.taskSpec), "utf-8");
|
|
91
94
|
await writeFile(sourcePaths.taskYamlPath, YAML.stringify(options.taskSpec), "utf-8");
|
|
95
|
+
await ensureMaterializedTaskContractManaged({
|
|
96
|
+
repoRoot: options.repoRoot,
|
|
97
|
+
taskId: harnessTaskId,
|
|
98
|
+
taskSpecPath: options.taskSpecPath,
|
|
99
|
+
});
|
|
92
100
|
const profileMapping = resolveLoopAgentProfile(options.taskSpec);
|
|
93
101
|
const manifest = {
|
|
94
102
|
schemaVersion: 1,
|
|
@@ -138,6 +146,32 @@ function slugify(value) {
|
|
|
138
146
|
.replace(/-{2,}/g, "-");
|
|
139
147
|
}
|
|
140
148
|
/** Exported for unit tests: derived contract must point back to immutable references. */
|
|
149
|
+
async function ensureMaterializedTaskContractManaged(input) {
|
|
150
|
+
const state = await observeTaskContractState(input.repoRoot, input.taskId);
|
|
151
|
+
if (state.effectiveStatus === "managed")
|
|
152
|
+
return;
|
|
153
|
+
if (state.effectiveStatus === "transaction-incomplete") {
|
|
154
|
+
throw new Error(`task contract transaction incomplete for ${input.taskId}; recover before materializing`);
|
|
155
|
+
}
|
|
156
|
+
if (!state.observedCanonicalHash) {
|
|
157
|
+
throw new Error(`unable to observe task contract hash for ${input.taskId}: ${state.observationErrors.map((error) => error.message).join("; ")}`);
|
|
158
|
+
}
|
|
159
|
+
const expectedRevision = state.ref?.revision ?? 0;
|
|
160
|
+
const requestPayload = JSON.stringify({
|
|
161
|
+
taskId: input.taskId,
|
|
162
|
+
taskSpecPath: input.taskSpecPath,
|
|
163
|
+
expectedRevision,
|
|
164
|
+
observedCanonicalHash: state.observedCanonicalHash,
|
|
165
|
+
});
|
|
166
|
+
await adoptTaskContract({
|
|
167
|
+
repoRoot: input.repoRoot,
|
|
168
|
+
taskId: input.taskId,
|
|
169
|
+
expectedRevision,
|
|
170
|
+
expectedObservedHash: state.observedCanonicalHash,
|
|
171
|
+
requestId: `worker-materialize-${sha256Utf8(requestPayload).slice(0, 24)}`,
|
|
172
|
+
requestPayloadSha256: sha256Utf8(requestPayload),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
141
175
|
export function renderRequirementMarkdown(taskSpec, options = {}) {
|
|
142
176
|
const lines = [
|
|
143
177
|
`# ${taskSpec.title}`,
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{color-scheme:light dark;--bg: #0f1419;--panel: #1a222c;--border: #2c3845;--text: #e7eef7;--muted: #9aabbc;--accent: #5b9fd4;--warn: #d4a15b;--error: #d45b5b;font-family:IBM Plex Sans,Segoe UI,system-ui,sans-serif;line-height:1.5}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:radial-gradient(1200px 600px at 10% -10%,#1b2a3a,var(--bg));color:var(--text)}.console-shell{max-width:1080px;margin:0 auto;padding:2rem 1.25rem 3rem}.console-header{display:grid;gap:1rem;grid-template-columns:1.4fr 1fr;margin-bottom:1.5rem}.eyebrow{margin:0;text-transform:uppercase;letter-spacing:.08em;font-size:.75rem;color:var(--accent)}h1{margin:.25rem 0;font-size:1.75rem}.subtitle,.muted{color:var(--muted)}.readiness{border:1px solid var(--border);background:var(--panel);border-radius:12px;padding:1rem}.readiness[data-state=setup-required]{border-color:var(--warn)}.readiness-label{display:block;font-size:.8rem;color:var(--muted)}.console-main{display:grid;gap:1rem}.panel{border:1px solid var(--border);background:color-mix(in srgb,var(--panel) 92%,black);border-radius:12px;padding:1.25rem}.empty-state{border:1px dashed var(--border);border-radius:10px;padding:1rem;background:#00000026}.empty-title{margin:0 0 .5rem;font-weight:600}.checklist{margin:.75rem 0 0;padding-left:1.2rem;color:var(--muted)}.kv{display:grid;gap:.5rem;margin:0}.kv div{display:grid;grid-template-columns:10rem 1fr;gap:.5rem}.kv dt{color:var(--muted)}.kv dd{margin:0;overflow-wrap:anywhere}code,.cmd{font-family:IBM Plex Mono,ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.85rem}.cmd{display:block;padding:.75rem;border-radius:8px;background:#0b1016;overflow-x:auto}.external-link{color:var(--accent)}.error{color:var(--error)}.wizard-steps{display:flex;flex-wrap:wrap;gap:.5rem;list-style:none;padding:0;margin:0 0 1rem;font-size:.85rem;color:var(--muted)}.wizard-steps li{border:1px solid var(--border);border-radius:999px;padding:.25rem .65rem}.wizard-steps li[data-active=true]{border-color:var(--accent);color:var(--text)}.wizard-body h3{margin-top:0}.wizard-body button{margin-top:.75rem;background:var(--accent);color:#0b1016;border:none;border-radius:8px;padding:.5rem .9rem;font-weight:600;cursor:pointer}.wizard-body button:disabled{opacity:.5;cursor:not-allowed}.readiness[data-state=ready]{border-color:#5bd49a}.readiness[data-state=degraded]{border-color:var(--warn)}@media(max-width:800px){.console-header,.kv div{grid-template-columns:1fr}}
|
|
File without changes
|