kcode-pi 0.1.24 → 0.1.30
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/README.md +3 -0
- package/docs/CHANGELOG.md +82 -0
- package/docs/COMMAND_REFERENCE.md +55 -0
- package/docs/HARNESS_WORKFLOW.md +33 -0
- package/extensions/kingdee-harness.ts +127 -5
- package/extensions/kingdee-header.ts +14 -1
- package/extensions/kingdee-subagents.ts +430 -0
- package/extensions/kingdee-tools.ts +19 -5
- package/package.json +2 -1
- package/prompts/kd-verify.md +1 -1
- package/skills/kd-verify/SKILL.md +2 -2
- package/src/harness/artifacts.ts +11 -3
- package/src/harness/delegation.ts +297 -0
- package/src/harness/evidence.ts +16 -5
- package/src/harness/gates.ts +10 -4
- package/src/harness/prompt.ts +13 -1
- package/src/harness/repair.ts +224 -0
- package/src/harness/state.ts +97 -11
- package/src/harness/types.ts +10 -0
package/src/harness/state.ts
CHANGED
|
@@ -100,7 +100,7 @@ export function addQuestion(
|
|
|
100
100
|
run: ActiveRun,
|
|
101
101
|
input: { question: string; reason?: string; choices?: string[]; blocking?: boolean },
|
|
102
102
|
): KdQuestion {
|
|
103
|
-
const existing = run.questions
|
|
103
|
+
const existing = Array.isArray(run.questions) ? run.questions : [];
|
|
104
104
|
const question: KdQuestion = {
|
|
105
105
|
id: createQuestionId(existing.length + 1),
|
|
106
106
|
phase: run.phase,
|
|
@@ -118,7 +118,7 @@ export function addQuestion(
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export function answerQuestion(cwd: string, run: ActiveRun, id: string, answer: string): KdQuestion | undefined {
|
|
121
|
-
const question = (run.questions
|
|
121
|
+
const question = (Array.isArray(run.questions) ? run.questions : []).find((item) => item.id === id);
|
|
122
122
|
if (!question) return undefined;
|
|
123
123
|
question.status = "answered";
|
|
124
124
|
question.answer = answer.trim();
|
|
@@ -129,7 +129,7 @@ export function answerQuestion(cwd: string, run: ActiveRun, id: string, answer:
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
export function openBlockingQuestions(run: ActiveRun): KdQuestion[] {
|
|
132
|
-
return (run.questions
|
|
132
|
+
return (Array.isArray(run.questions) ? run.questions : []).filter((question) => question.status === "open" && question.blocking);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
export function updateProductProfile(cwd: string, run: ActiveRun, productInput: string, version?: string): ActiveRun {
|
|
@@ -189,6 +189,21 @@ export function advanceRun(cwd: string, run: ActiveRun, requestedPhase?: KdPhase
|
|
|
189
189
|
return { run, message: `已推进 Kingdee run 到阶段:${target}` };
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
export function advanceRunIfReady(cwd: string, run: ActiveRun): { run: ActiveRun; advanced: boolean; message: string } {
|
|
193
|
+
const current = readRun(cwd, run.id) ?? run;
|
|
194
|
+
const target = nextPhase(current.phase);
|
|
195
|
+
if (!target) {
|
|
196
|
+
return { run: current, advanced: false, message: `Run 已处于最终阶段:${current.phase}` };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const result = advanceRun(cwd, current, target);
|
|
200
|
+
return {
|
|
201
|
+
run: result.run,
|
|
202
|
+
advanced: result.run.phase === target,
|
|
203
|
+
message: result.message,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
192
207
|
export function refreshGate(cwd: string, run: ActiveRun): ActiveRun {
|
|
193
208
|
run.gate = inspectGate(cwd, run);
|
|
194
209
|
writeActiveRun(cwd, run);
|
|
@@ -202,19 +217,90 @@ function writeRunState(cwd: string, run: ActiveRun): void {
|
|
|
202
217
|
}
|
|
203
218
|
|
|
204
219
|
function hydrateRun(parsed: ActiveRun): ActiveRun | undefined {
|
|
205
|
-
if (!parsed
|
|
206
|
-
parsed.
|
|
207
|
-
parsed.
|
|
208
|
-
parsed.
|
|
209
|
-
parsed.
|
|
210
|
-
parsed.
|
|
220
|
+
if (!parsed || typeof parsed !== "object") return undefined;
|
|
221
|
+
if (typeof parsed.id !== "string" || !parsed.id.trim()) return undefined;
|
|
222
|
+
if (typeof parsed.phase !== "string" || !isKdPhase(parsed.phase)) return undefined;
|
|
223
|
+
parsed.id = parsed.id.trim();
|
|
224
|
+
parsed.artifacts = isPlainObject(parsed.artifacts) ? sanitizeArtifacts(parsed.artifacts) : {};
|
|
225
|
+
parsed.questions = Array.isArray(parsed.questions) ? parsed.questions.map(sanitizeQuestion).filter((question): question is KdQuestion => Boolean(question)) : [];
|
|
226
|
+
parsed.status = parsed.status === "paused" || parsed.status === "done" ? parsed.status : "active";
|
|
227
|
+
parsed.goal = typeof parsed.goal === "string" ? parsed.goal : undefined;
|
|
228
|
+
parsed.version = typeof parsed.version === "string" ? parsed.version : undefined;
|
|
229
|
+
parsed.createdAt = typeof parsed.createdAt === "string" ? parsed.createdAt : typeof parsed.updatedAt === "string" ? parsed.updatedAt : undefined;
|
|
230
|
+
parsed.updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : parsed.createdAt;
|
|
211
231
|
const legacyEdition = (parsed as ActiveRun & { edition?: string }).edition;
|
|
212
|
-
parsed.profile
|
|
232
|
+
const product = typeof parsed.profile?.product === "string" ? parsed.profile.product : typeof parsed.product === "string" ? parsed.product : resolveProductProfile(typeof legacyEdition === "string" ? legacyEdition : undefined).product;
|
|
233
|
+
parsed.profile = profileForProduct(product);
|
|
213
234
|
parsed.product = parsed.profile.product;
|
|
214
|
-
parsed.
|
|
235
|
+
parsed.riskAssessment = sanitizeRiskAssessment(parsed.riskAssessment);
|
|
236
|
+
parsed.repair = sanitizeRepairState(parsed.repair);
|
|
237
|
+
parsed.gate = sanitizeGate(parsed.gate);
|
|
215
238
|
return parsed;
|
|
216
239
|
}
|
|
217
240
|
|
|
241
|
+
function sanitizeArtifacts(value: Record<string, unknown>): Record<string, string> {
|
|
242
|
+
return Object.fromEntries(Object.entries(value).filter((entry): entry is [string, string] => typeof entry[0] === "string" && typeof entry[1] === "string"));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function sanitizeQuestion(value: unknown): KdQuestion | undefined {
|
|
246
|
+
if (!isPlainObject(value)) return undefined;
|
|
247
|
+
const phase = typeof value.phase === "string" && isKdPhase(value.phase) ? value.phase : undefined;
|
|
248
|
+
const question = typeof value.question === "string" ? value.question.trim() : "";
|
|
249
|
+
if (!phase || !question) return undefined;
|
|
250
|
+
const id = typeof value.id === "string" && value.id.trim() ? value.id.trim() : createQuestionId(1);
|
|
251
|
+
return {
|
|
252
|
+
id,
|
|
253
|
+
phase,
|
|
254
|
+
question,
|
|
255
|
+
reason: typeof value.reason === "string" && value.reason.trim() ? value.reason.trim() : undefined,
|
|
256
|
+
choices: Array.isArray(value.choices) ? value.choices.filter((choice): choice is string => typeof choice === "string" && Boolean(choice.trim())).map((choice) => choice.trim()) : undefined,
|
|
257
|
+
blocking: value.blocking !== false,
|
|
258
|
+
status: value.status === "answered" ? "answered" : "open",
|
|
259
|
+
answer: typeof value.answer === "string" ? value.answer : undefined,
|
|
260
|
+
createdAt: typeof value.createdAt === "string" ? value.createdAt : new Date().toISOString(),
|
|
261
|
+
answeredAt: typeof value.answeredAt === "string" ? value.answeredAt : undefined,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function sanitizeRiskAssessment(value: unknown): ActiveRun["riskAssessment"] {
|
|
266
|
+
if (!isPlainObject(value)) return undefined;
|
|
267
|
+
const level = value.level;
|
|
268
|
+
if (level !== "low" && level !== "medium" && level !== "high") return undefined;
|
|
269
|
+
return {
|
|
270
|
+
level,
|
|
271
|
+
reason: typeof value.reason === "string" ? value.reason : "",
|
|
272
|
+
source: value.source === "verify" || value.source === "ship" ? value.source : "manual",
|
|
273
|
+
updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : new Date().toISOString(),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function sanitizeRepairState(value: unknown): ActiveRun["repair"] {
|
|
278
|
+
if (!isPlainObject(value)) return undefined;
|
|
279
|
+
const attempts = typeof value.attempts === "number" && Number.isFinite(value.attempts) ? Math.max(0, Math.trunc(value.attempts)) : 0;
|
|
280
|
+
const maxAttempts = typeof value.maxAttempts === "number" && Number.isFinite(value.maxAttempts) ? Math.max(1, Math.trunc(value.maxAttempts)) : 3;
|
|
281
|
+
return {
|
|
282
|
+
attempts,
|
|
283
|
+
maxAttempts,
|
|
284
|
+
lastFailureEvidence: typeof value.lastFailureEvidence === "string" ? value.lastFailureEvidence : undefined,
|
|
285
|
+
lastFailureSignature: typeof value.lastFailureSignature === "string" ? value.lastFailureSignature : undefined,
|
|
286
|
+
status: value.status === "repairing" || value.status === "blocked" ? value.status : "idle",
|
|
287
|
+
updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : new Date().toISOString(),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function sanitizeGate(value: unknown): ActiveRun["gate"] {
|
|
292
|
+
if (!isPlainObject(value)) return { passed: false, checkedAt: new Date().toISOString() };
|
|
293
|
+
return {
|
|
294
|
+
passed: value.passed === true,
|
|
295
|
+
reason: typeof value.reason === "string" ? value.reason : undefined,
|
|
296
|
+
checkedAt: typeof value.checkedAt === "string" ? value.checkedAt : new Date().toISOString(),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
301
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
302
|
+
}
|
|
303
|
+
|
|
218
304
|
function createRunId(goal: string): string {
|
|
219
305
|
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+/, "").replace("T", "-");
|
|
220
306
|
const slug = goal
|
package/src/harness/types.ts
CHANGED
|
@@ -18,6 +18,15 @@ export interface GateResult {
|
|
|
18
18
|
checkedAt: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export interface RepairState {
|
|
22
|
+
attempts: number;
|
|
23
|
+
maxAttempts: number;
|
|
24
|
+
lastFailureEvidence?: string;
|
|
25
|
+
lastFailureSignature?: string;
|
|
26
|
+
status: "idle" | "repairing" | "blocked";
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
export interface KdQuestion {
|
|
22
31
|
id: string;
|
|
23
32
|
phase: KdPhase;
|
|
@@ -43,6 +52,7 @@ export interface ActiveRun {
|
|
|
43
52
|
version?: string;
|
|
44
53
|
profile?: ProductProfile;
|
|
45
54
|
riskAssessment?: RiskAssessment;
|
|
55
|
+
repair?: RepairState;
|
|
46
56
|
artifacts: Record<string, string>;
|
|
47
57
|
gate: GateResult;
|
|
48
58
|
questions?: KdQuestion[];
|