@kody-ade/kody-engine 0.4.323 → 0.4.325

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +449 -90
  2. package/package.json +1 -1
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.323",
18
+ version: "0.4.325",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -6392,6 +6392,224 @@ var init_litellm = __esm({
6392
6392
  }
6393
6393
  });
6394
6394
 
6395
+ // src/runIndex.ts
6396
+ function upsertRunIndexRow(config, cwd, row) {
6397
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
6398
+ const current = readStateText(config, cwd, RUN_INDEX_PATH);
6399
+ const next = mergeRunIndexRow(current?.content, row);
6400
+ try {
6401
+ writeStateText(
6402
+ config,
6403
+ cwd,
6404
+ RUN_INDEX_PATH,
6405
+ JSON.stringify(next, null, 2),
6406
+ "chore(runs): update run index",
6407
+ current?.sha
6408
+ );
6409
+ return;
6410
+ } catch (err) {
6411
+ if (!isConflict(err) || attempt === 3) throw err;
6412
+ }
6413
+ }
6414
+ }
6415
+ function upsertRunIndexRowBestEffort(config, cwd, row) {
6416
+ if (!row) return;
6417
+ try {
6418
+ upsertRunIndexRow(config, cwd, row);
6419
+ } catch (err) {
6420
+ process.stderr.write(`[kody runs] index update failed: ${err instanceof Error ? err.message : String(err)}
6421
+ `);
6422
+ }
6423
+ }
6424
+ function mergeRunIndexRow(raw, row) {
6425
+ const parsed = parseRunIndex(raw);
6426
+ const runs = [row, ...parsed.runs.filter((existing) => existing.id !== row.id)].slice(0, MAX_RUNS);
6427
+ return {
6428
+ version: 1,
6429
+ updatedAt: row.updatedAt,
6430
+ runs
6431
+ };
6432
+ }
6433
+ function runIndexRowFromJobContext(input) {
6434
+ const subjectType = runSubjectType(input.data);
6435
+ const subjectId = stringValue2(input.data.runSubjectId);
6436
+ if (!subjectType || !subjectId) return null;
6437
+ const kodyRunId = stringValue2(input.data.jobId) ?? `${input.profileName}-${Date.now()}`;
6438
+ const workflow = stringValue2(input.data.runSubjectWorkflow) ?? stringValue2(input.data.workflowCapability);
6439
+ const title = stringValue2(input.data.runSubjectLabel) ?? subjectId;
6440
+ const githubRunId2 = process.env.GITHUB_RUN_ID?.trim() || void 0;
6441
+ const githubRunAttempt = process.env.GITHUB_RUN_ATTEMPT?.trim() || void 0;
6442
+ const githubRepository = process.env.GITHUB_REPOSITORY?.trim();
6443
+ const githubServer = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
6444
+ const triggerKind2 = triggerKindFromEnv();
6445
+ return pruneUndefined({
6446
+ version: 1,
6447
+ id: `${subjectType}:${subjectId}:${kodyRunId}`,
6448
+ subjectType,
6449
+ subjectId,
6450
+ subjectLabel: title,
6451
+ subjectModel: stringValue2(input.data.runSubjectModel) ?? void 0,
6452
+ status: input.status,
6453
+ title,
6454
+ summary: input.reason ?? stringValue2(input.data.workflowStepReason) ?? input.profile.describe,
6455
+ currentStep: stringValue2(input.data.workflowStep) ?? input.profileName,
6456
+ startedAt: input.startedAt,
6457
+ updatedAt: input.updatedAt,
6458
+ kodyRunId,
6459
+ githubRunId: githubRunId2,
6460
+ githubRunAttempt,
6461
+ githubRunUrl: githubRunId2 && githubRepository ? `${githubServer}/${githubRepository}/actions/runs/${githubRunId2}` : void 0,
6462
+ triggerKind: triggerKind2,
6463
+ triggerMode: triggerMode(triggerKind2),
6464
+ actor: process.env.GITHUB_ACTOR?.trim() || void 0,
6465
+ action: stringValue2(input.data.jobAction) ?? void 0,
6466
+ capability: stringValue2(input.data.jobCapability) ?? void 0,
6467
+ workflow: workflow ?? void 0,
6468
+ executable: stringValue2(input.data.jobExecutable) ?? input.profileName,
6469
+ agent: stringValue2(input.data.jobAgent) ?? input.profile.agent ?? void 0,
6470
+ model: stringValue2(input.data.jobModel) ?? void 0,
6471
+ modelProvider: stringValue2(input.data.jobModelProvider) ?? void 0,
6472
+ modelName: stringValue2(input.data.jobModelName) ?? void 0,
6473
+ reasoningEffort: stringValue2(input.data.jobReasoningEffort) ?? void 0,
6474
+ target: input.data.jobTarget,
6475
+ sourceType: "job"
6476
+ });
6477
+ }
6478
+ function runIndexRowFromGoalEvents(goalId, logPath, events) {
6479
+ if (events.length === 0) return null;
6480
+ const first = events[0];
6481
+ const last = events[events.length - 1];
6482
+ const goal = recordValue2(last.goal) ?? recordValue2(first.goal);
6483
+ const goalType = stringValue2(last.goalType) ?? stringValue2(first.goalType) ?? stringValue2(goal?.type);
6484
+ const subjectType = goalType === "agentLoop" ? "loop" : "goal";
6485
+ const run = recordValue2(first.run) ?? recordValue2(last.run);
6486
+ const job = recordValue2(last.job) ?? recordValue2(first.job);
6487
+ const trigger = recordValue2(last.trigger) ?? recordValue2(first.trigger);
6488
+ const links = recordValue2(last.links) ?? recordValue2(first.links);
6489
+ const decision = recordValue2(last.decision);
6490
+ const trace = recordValue2(last.trace);
6491
+ const traceResult = recordValue2(trace?.result);
6492
+ const kodyRunId = stringValue2(run?.id) ?? stringValue2(job?.id) ?? logPath;
6493
+ const updatedAt = stringValue2(last.time) ?? (/* @__PURE__ */ new Date()).toISOString();
6494
+ return pruneUndefined({
6495
+ version: 1,
6496
+ id: `${subjectType}:${goalId}:${kodyRunId}`,
6497
+ subjectType,
6498
+ subjectId: goalId,
6499
+ subjectLabel: goalId,
6500
+ subjectModel: goalType ?? void 0,
6501
+ status: statusFromGoalEvent(last, decision),
6502
+ title: goalId,
6503
+ summary: stringValue2(last.summary) ?? stringValue2(last.reason) ?? stringValue2(traceResult?.summary) ?? stringValue2(last.event) ?? void 0,
6504
+ currentStep: stringValue2(last.stage) ?? stringValue2(goal?.stage) ?? stringValue2(last.event) ?? void 0,
6505
+ decision: [stringValue2(decision?.kind), stringValue2(decision?.reason) ?? stringValue2(last.reason)].filter(Boolean).join(" - ") || void 0,
6506
+ startedAt: stringValue2(first.time) ?? void 0,
6507
+ updatedAt,
6508
+ kodyRunId,
6509
+ githubRunId: stringValue2(run?.githubRunId) ?? void 0,
6510
+ githubRunAttempt: stringValue2(run?.githubRunAttempt) ?? void 0,
6511
+ githubRunUrl: stringValue2(links?.workflowRun) ?? stringValue2(run?.url) ?? void 0,
6512
+ triggerKind: stringValue2(trigger?.kind) ?? void 0,
6513
+ triggerMode: triggerMode(stringValue2(trigger?.kind)),
6514
+ actor: stringValue2(trigger?.githubActor) ?? stringValue2(trigger?.actor) ?? void 0,
6515
+ action: stringValue2(job?.action) ?? void 0,
6516
+ capability: stringValue2(job?.capability) ?? void 0,
6517
+ executable: stringValue2(job?.executable) ?? void 0,
6518
+ agent: stringValue2(job?.agent) ?? void 0,
6519
+ model: stringValue2(job?.model) ?? void 0,
6520
+ modelProvider: stringValue2(job?.modelProvider) ?? void 0,
6521
+ modelName: stringValue2(job?.modelName) ?? void 0,
6522
+ reasoningEffort: stringValue2(job?.reasoningEffort) ?? void 0,
6523
+ target: last.target,
6524
+ sourceType: "goal-run-log",
6525
+ sourcePath: logPath,
6526
+ detailUrl: stringValue2(links?.log) ?? void 0,
6527
+ statePath: stringValue2(recordValue2(last.stateRepo)?.goalStatePath) ?? void 0
6528
+ });
6529
+ }
6530
+ function statusFromExitCode(exitCode) {
6531
+ return exitCode === 0 ? "success" : "failed";
6532
+ }
6533
+ function parseRunIndex(raw) {
6534
+ if (!raw) return { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), runs: [] };
6535
+ try {
6536
+ const parsed = JSON.parse(raw);
6537
+ const record2 = recordValue2(parsed);
6538
+ const runs = Array.isArray(record2?.runs) ? record2.runs.filter(isRunIndexRow).map(normalizeRunIndexRow) : [];
6539
+ return { version: 1, updatedAt: stringValue2(record2?.updatedAt) ?? (/* @__PURE__ */ new Date()).toISOString(), runs };
6540
+ } catch {
6541
+ return { version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), runs: [] };
6542
+ }
6543
+ }
6544
+ function isRunIndexRow(value) {
6545
+ const record2 = recordValue2(value);
6546
+ return record2?.version === 1 && isRunSubjectType(record2.subjectType) && typeof record2.subjectId === "string" && typeof record2.id === "string" && typeof record2.status === "string" && typeof record2.title === "string" && typeof record2.updatedAt === "string";
6547
+ }
6548
+ function isRunSubjectType(value) {
6549
+ return value === "goal" || value === "loop" || value === "workflow";
6550
+ }
6551
+ function normalizeRunIndexRow(row) {
6552
+ if (row.status === "running" && (row.decision?.toLowerCase().startsWith("dispatch") || row.summary?.toLowerCase().startsWith("dispatch") || row.currentStep?.toLowerCase().includes("dispatch"))) {
6553
+ return { ...row, status: "waiting" };
6554
+ }
6555
+ return row;
6556
+ }
6557
+ function runSubjectType(data) {
6558
+ const value = data.runSubjectType;
6559
+ return isRunSubjectType(value) ? value : null;
6560
+ }
6561
+ function statusFromGoalEvent(event, decision) {
6562
+ const status = stringValue2(event.status)?.toLowerCase();
6563
+ const eventName = stringValue2(event.event)?.toLowerCase() ?? "";
6564
+ const decisionKind = stringValue2(decision?.kind)?.toLowerCase();
6565
+ if (status === "success" || status === "completed" || decisionKind === "done") return "success";
6566
+ if (status === "failure" || status === "failed" || eventName.includes("fail")) return "failed";
6567
+ if (status === "cancelled") return "cancelled";
6568
+ if (decisionKind === "blocked") return "blocked";
6569
+ if (status === "dispatch" || decisionKind === "dispatch" || eventName.includes("dispatch")) return "waiting";
6570
+ if (status === "running") return "running";
6571
+ return "recorded";
6572
+ }
6573
+ function triggerKindFromEnv() {
6574
+ const eventName = process.env.GITHUB_EVENT_NAME?.trim();
6575
+ if (!eventName) return void 0;
6576
+ if (eventName === "schedule") return "schedule";
6577
+ if (eventName === "workflow_dispatch") return "manual-workflow-dispatch";
6578
+ return eventName;
6579
+ }
6580
+ function triggerMode(kind) {
6581
+ if (!kind) return void 0;
6582
+ if (kind === "schedule") return "scheduled";
6583
+ if (kind === "manual-workflow-dispatch") return "manual";
6584
+ if (kind === "local") return "local";
6585
+ return "event";
6586
+ }
6587
+ function isConflict(err) {
6588
+ const msg = err instanceof Error ? err.message : String(err);
6589
+ return /HTTP 409/i.test(msg) || /HTTP 422/i.test(msg) || /does not match|is at|but expected/i.test(msg);
6590
+ }
6591
+ function recordValue2(value) {
6592
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
6593
+ }
6594
+ function stringValue2(value) {
6595
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6596
+ }
6597
+ function pruneUndefined(input) {
6598
+ for (const key of Object.keys(input)) {
6599
+ if (input[key] === void 0) delete input[key];
6600
+ }
6601
+ return input;
6602
+ }
6603
+ var RUN_INDEX_PATH, MAX_RUNS;
6604
+ var init_runIndex = __esm({
6605
+ "src/runIndex.ts"() {
6606
+ "use strict";
6607
+ init_stateRepo();
6608
+ RUN_INDEX_PATH = "runs/index.json";
6609
+ MAX_RUNS = 200;
6610
+ }
6611
+ });
6612
+
6395
6613
  // src/pushWithRetry.ts
6396
6614
  import { execFileSync as execFileSync6 } from "child_process";
6397
6615
  function sleepSync2(ms) {
@@ -6904,6 +7122,32 @@ function planManagedGoalTick(goal) {
6904
7122
  goal.nextAction = "done";
6905
7123
  return { kind: "done" };
6906
7124
  }
7125
+ const workflow = goal.workflowRef;
7126
+ if (workflow) {
7127
+ const workflowStep = { evidence: missing, stage: "workflow", capability: workflow.id };
7128
+ const progressDecision2 = decisionFromEvidenceProgress(goal, workflowStep, missing);
7129
+ if (progressDecision2) return progressDecision2;
7130
+ const resolved2 = resolveWorkflowRefArgs(goal, workflow);
7131
+ if (!resolved2.ok) {
7132
+ goal.stage = "blocked";
7133
+ goal.reason = resolved2.reason;
7134
+ goal.nextAction = "fix workflow args";
7135
+ pushBlocker(goal, resolved2.reason);
7136
+ return { kind: "blocked", evidence: missing, stage: "workflow", reason: resolved2.reason };
7137
+ }
7138
+ goal.stage = "workflow";
7139
+ goal.facts.pendingEvidence = missing;
7140
+ goal.reason = `dispatch workflow ${workflow.id} for ${missing}`;
7141
+ goal.nextAction = "dispatch workflow";
7142
+ return {
7143
+ kind: "dispatchWorkflow",
7144
+ evidence: missing,
7145
+ stage: "workflow",
7146
+ workflow: workflow.id,
7147
+ cliArgs: resolved2.cliArgs,
7148
+ ...workflow.saveReport === true ? { saveReport: true } : {}
7149
+ };
7150
+ }
6907
7151
  const step = goal.route.find((candidate) => candidate.evidence === missing);
6908
7152
  if (!step) {
6909
7153
  if (isSimpleGoal(goal) && missing === SIMPLE_GOAL_EVIDENCE) {
@@ -6959,6 +7203,15 @@ function planManagedGoalTick(goal) {
6959
7203
  ...step.saveReport === true ? { saveReport: true } : {}
6960
7204
  };
6961
7205
  }
7206
+ function resolveWorkflowRefArgs(goal, workflow) {
7207
+ const step = {
7208
+ evidence: "__workflow__",
7209
+ stage: "workflow",
7210
+ capability: workflow.id,
7211
+ args: workflow.args
7212
+ };
7213
+ return resolveRouteArgs(goal, step);
7214
+ }
6962
7215
  function decisionFromEvidenceProgress(goal, step, evidence) {
6963
7216
  const progress = goal.evidenceState?.[evidence];
6964
7217
  if (!progress) return null;
@@ -7095,6 +7348,19 @@ function asLoopTarget(value) {
7095
7348
  if (typeof raw.id !== "string" || raw.id.trim().length === 0) return void 0;
7096
7349
  return { type: raw.type, id: raw.id };
7097
7350
  }
7351
+ function asWorkflowRef(value) {
7352
+ const raw = asRecord(value);
7353
+ if (!raw) return void 0;
7354
+ if (typeof raw.id !== "string" || raw.id.trim().length === 0) return void 0;
7355
+ const args = raw.args === void 0 ? void 0 : asRecord(raw.args);
7356
+ if (raw.args !== void 0 && !args) return void 0;
7357
+ return {
7358
+ id: raw.id.trim(),
7359
+ ...typeof raw.source === "string" && raw.source.trim().length > 0 ? { source: raw.source.trim() } : {},
7360
+ ...args ? { args } : {},
7361
+ ...raw.saveReport === true ? { saveReport: true } : {}
7362
+ };
7363
+ }
7098
7364
  function managedGoalFromState(state) {
7099
7365
  const extra = state.extra;
7100
7366
  const destination = asRecord(extra.destination);
@@ -7111,6 +7377,7 @@ function managedGoalFromState(state) {
7111
7377
  destination: { outcome: destination.outcome, evidence },
7112
7378
  capabilities,
7113
7379
  route,
7380
+ workflowRef: asWorkflowRef(extra.workflowRef),
7114
7381
  schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
7115
7382
  preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
7116
7383
  loopTarget: asLoopTarget(extra.loopTarget),
@@ -7131,6 +7398,7 @@ function writeManagedGoalToState(state, goal) {
7131
7398
  destination: goal.destination,
7132
7399
  capabilities: goal.capabilities,
7133
7400
  route: goal.route,
7401
+ workflowRef: goal.workflowRef,
7134
7402
  stage: goal.stage,
7135
7403
  facts: goal.facts,
7136
7404
  blockers: goal.blockers,
@@ -7220,9 +7488,15 @@ function flushGoalRunLogEvents(config, cwd, data) {
7220
7488
  const logs = goalRunLogs(data);
7221
7489
  for (const [goalId, log2] of Object.entries(logs)) {
7222
7490
  if (log2.events.length === 0) continue;
7223
- const lines = `${log2.events.map((event) => JSON.stringify(enrichGoalRunLogEvent(config, data, log2.path, event))).join("\n")}
7491
+ const enrichedEvents = log2.events.map((event) => enrichGoalRunLogEvent(config, data, log2.path, event));
7492
+ const lines = `${enrichedEvents.map((event) => JSON.stringify(event)).join("\n")}
7224
7493
  `;
7225
7494
  appendStateLine(config, cwd, log2.path, lines, `chore(goal-logs): append ${goalId}`);
7495
+ upsertRunIndexRowBestEffort(
7496
+ config,
7497
+ cwd,
7498
+ runIndexRowFromGoalEvents(goalId, log2.path, enrichedEvents)
7499
+ );
7226
7500
  log2.events = [];
7227
7501
  }
7228
7502
  }
@@ -7250,6 +7524,7 @@ function goalRunLogSnapshot(goalId, goalState, goal) {
7250
7524
  pendingEvidence,
7251
7525
  capabilities: [...goal.capabilities],
7252
7526
  route: goal.route.map(routeStepForLog),
7527
+ workflowRef: goal.workflowRef,
7253
7528
  schedule: goal.schedule,
7254
7529
  preferredRunTime: goal.preferredRunTime,
7255
7530
  loopTarget: goal.loopTarget,
@@ -7264,8 +7539,8 @@ function goalRunLogChange(before, after) {
7264
7539
  addScalarChange(change, "state", before.state, after.state);
7265
7540
  addScalarChange(change, "stage", before.stage, after.stage);
7266
7541
  addScalarChange(change, "pendingEvidence", before.pendingEvidence, after.pendingEvidence);
7267
- const beforeFacts = recordValue2(before.facts);
7268
- const afterFacts = recordValue2(after.facts);
7542
+ const beforeFacts = recordValue3(before.facts);
7543
+ const afterFacts = recordValue3(after.facts);
7269
7544
  if (beforeFacts || afterFacts) change.facts = diffRecordKeys(beforeFacts ?? {}, afterFacts ?? {});
7270
7545
  const beforeBlockers = stringArrayValue(before.blockers);
7271
7546
  const afterBlockers = stringArrayValue(after.blockers);
@@ -7279,8 +7554,8 @@ function goalRunLogChange(before, after) {
7279
7554
  const evidence = diffStringArrays(beforeSatisfied ?? [], afterSatisfied ?? []);
7280
7555
  if (evidence.added.length > 0 || evidence.removed.length > 0) change.satisfiedEvidence = evidence;
7281
7556
  }
7282
- const beforeEvidenceState = recordValue2(before.evidenceState);
7283
- const afterEvidenceState = recordValue2(after.evidenceState);
7557
+ const beforeEvidenceState = recordValue3(before.evidenceState);
7558
+ const afterEvidenceState = recordValue3(after.evidenceState);
7284
7559
  if (beforeEvidenceState || afterEvidenceState) {
7285
7560
  change.evidenceState = diffRecordKeys(beforeEvidenceState ?? {}, afterEvidenceState ?? {});
7286
7561
  }
@@ -7305,7 +7580,7 @@ function goalRunStartedAt(data) {
7305
7580
  function goalRunId(data) {
7306
7581
  const existing = data[LOG_RUN_KEY];
7307
7582
  if (typeof existing === "string" && existing.length > 0) return existing;
7308
- const raw = stringValue2(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
7583
+ const raw = stringValue3(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
7309
7584
  const safe = safePathSegment(raw);
7310
7585
  data[LOG_RUN_KEY] = safe;
7311
7586
  return safe;
@@ -7337,7 +7612,7 @@ function enrichGoalRunLogEvent(config, data, logPath, event) {
7337
7612
  const job = event.job ?? jobContext(data);
7338
7613
  const run = event.run ?? runContext(data);
7339
7614
  const links = event.links ?? linkContext(stateRepo);
7340
- const enriched = pruneUndefined({
7615
+ const enriched = pruneUndefined2({
7341
7616
  ...event,
7342
7617
  run,
7343
7618
  repo: event.repo ?? repoContext(config),
@@ -7351,23 +7626,23 @@ function enrichGoalRunLogEvent(config, data, logPath, event) {
7351
7626
  return enriched;
7352
7627
  }
7353
7628
  function goalRunTrace(event) {
7354
- const run = recordValue2(event.run);
7355
- const trigger = recordValue2(event.trigger);
7356
- const goal = recordValue2(event.goal);
7357
- const inspection = recordValue2(event.inspection);
7358
- const capabilityOutput = recordValue2(inspection?.capabilityOutput);
7359
- return pruneUndefined({
7629
+ const run = recordValue3(event.run);
7630
+ const trigger = recordValue3(event.trigger);
7631
+ const goal = recordValue3(event.goal);
7632
+ const inspection = recordValue3(event.inspection);
7633
+ const capabilityOutput = recordValue3(inspection?.capabilityOutput);
7634
+ return pruneUndefined2({
7360
7635
  version: 1,
7361
- runId: stringValue2(run?.id) ?? void 0,
7362
- workflowRunId: stringValue2(run?.githubRunId) ?? void 0,
7363
- triggerKind: stringValue2(trigger?.kind) ?? void 0,
7636
+ runId: stringValue3(run?.id) ?? void 0,
7637
+ workflowRunId: stringValue3(run?.githubRunId) ?? void 0,
7638
+ triggerKind: stringValue3(trigger?.kind) ?? void 0,
7364
7639
  source: event.source,
7365
7640
  event: event.event,
7366
- goal: pruneUndefined({
7641
+ goal: pruneUndefined2({
7367
7642
  id: event.goalId,
7368
- type: event.goalType ?? stringValue2(goal?.type) ?? void 0,
7369
- state: event.goalState ?? stringValue2(goal?.state) ?? void 0,
7370
- stage: event.stage ?? stringValue2(goal?.stage) ?? void 0
7643
+ type: event.goalType ?? stringValue3(goal?.type) ?? void 0,
7644
+ state: event.goalState ?? stringValue3(goal?.state) ?? void 0,
7645
+ stage: event.stage ?? stringValue3(goal?.stage) ?? void 0
7371
7646
  }),
7372
7647
  evidence: goalTraceEvidence(event, goal, inspection),
7373
7648
  capability: event.dispatch ?? capabilityDispatchFromOutput(capabilityOutput),
@@ -7377,30 +7652,30 @@ function goalRunTrace(event) {
7377
7652
  });
7378
7653
  }
7379
7654
  function goalTraceEvidence(event, goal, inspection) {
7380
- const expectedEvidence = recordValue2(inspection?.expectedEvidence);
7381
- const out = pruneUndefined({
7655
+ const expectedEvidence = recordValue3(inspection?.expectedEvidence);
7656
+ const out = pruneUndefined2({
7382
7657
  current: event.evidence,
7383
7658
  required: stringArrayValue(goal?.requiredEvidence) ?? stringArrayValue(inspection?.requiredEvidence) ?? void 0,
7384
7659
  satisfied: stringArrayValue(goal?.satisfiedEvidence) ?? stringArrayValue(inspection?.satisfiedEvidence) ?? void 0,
7385
7660
  missing: stringArrayValue(goal?.missingEvidence) ?? stringArrayValue(inspection?.missingEvidence) ?? stringArrayValue(expectedEvidence?.missingBefore) ?? void 0,
7386
- pending: stringValue2(goal?.pendingEvidence) ?? stringValue2(inspection?.pendingEvidence) ?? stringValue2(expectedEvidence?.pendingBefore) ?? void 0,
7661
+ pending: stringValue3(goal?.pendingEvidence) ?? stringValue3(inspection?.pendingEvidence) ?? stringValue3(expectedEvidence?.pendingBefore) ?? void 0,
7387
7662
  values: event.evidenceValues
7388
7663
  });
7389
7664
  return Object.keys(out).length > 0 ? out : void 0;
7390
7665
  }
7391
7666
  function capabilityDispatchFromOutput(output) {
7392
7667
  if (!output) return void 0;
7393
- const dispatch2 = pruneUndefined({
7394
- capability: stringValue2(output.capability) ?? void 0,
7395
- executable: stringValue2(output.executable) ?? void 0,
7396
- action: stringValue2(output.action) ?? void 0
7668
+ const dispatch2 = pruneUndefined2({
7669
+ capability: stringValue3(output.capability) ?? void 0,
7670
+ executable: stringValue3(output.executable) ?? void 0,
7671
+ action: stringValue3(output.action) ?? void 0
7397
7672
  });
7398
7673
  return Object.keys(dispatch2).length > 0 ? dispatch2 : void 0;
7399
7674
  }
7400
7675
  function goalTraceResult(event, capabilityOutput) {
7401
- const result = pruneUndefined({
7402
- status: event.status ?? stringValue2(capabilityOutput?.status) ?? void 0,
7403
- summary: event.reason ?? stringValue2(capabilityOutput?.summary) ?? void 0,
7676
+ const result = pruneUndefined2({
7677
+ status: event.status ?? stringValue3(capabilityOutput?.status) ?? void 0,
7678
+ summary: event.reason ?? stringValue3(capabilityOutput?.summary) ?? void 0,
7404
7679
  blockers: event.inspection ? stringArrayValue(capabilityOutput?.blockers) ?? void 0 : void 0,
7405
7680
  artifacts: event.artifacts
7406
7681
  });
@@ -7411,7 +7686,7 @@ function runContext(data) {
7411
7686
  const attempt = process.env.GITHUB_RUN_ATTEMPT?.trim();
7412
7687
  const repository = process.env.GITHUB_REPOSITORY?.trim();
7413
7688
  const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
7414
- return pruneUndefined({
7689
+ return pruneUndefined2({
7415
7690
  id: goalRunId(data),
7416
7691
  provider: runId ? "github-actions" : "local",
7417
7692
  githubRunId: runId || void 0,
@@ -7425,7 +7700,7 @@ function runContext(data) {
7425
7700
  function repoContext(config) {
7426
7701
  const owner = config.github?.owner;
7427
7702
  const repo = config.github?.repo;
7428
- return pruneUndefined({
7703
+ return pruneUndefined2({
7429
7704
  owner,
7430
7705
  repo,
7431
7706
  fullName: owner && repo ? `${owner}/${repo}` : process.env.GITHUB_REPOSITORY?.trim() || void 0,
@@ -7449,11 +7724,11 @@ function stateRepoContext(config, goalId, logPath) {
7449
7724
  }
7450
7725
  function triggerContext() {
7451
7726
  const event = readGithubEvent();
7452
- const inputs = recordValue2(event?.inputs);
7727
+ const inputs = recordValue3(event?.inputs);
7453
7728
  const eventName = process.env.GITHUB_EVENT_NAME?.trim() || void 0;
7454
7729
  const githubActor = process.env.GITHUB_ACTOR?.trim() || void 0;
7455
7730
  if (!eventName && !githubActor && !process.env.GITHUB_EVENT_PATH) return void 0;
7456
- const trigger = pruneUndefined({
7731
+ const trigger = pruneUndefined2({
7457
7732
  source: eventName ? "github-actions" : "local",
7458
7733
  kind: triggerKind(eventName),
7459
7734
  eventName,
@@ -7461,10 +7736,10 @@ function triggerContext() {
7461
7736
  githubActor,
7462
7737
  actorRole: triggerActorRole(eventName),
7463
7738
  eventPath: process.env.GITHUB_EVENT_PATH?.trim() || void 0,
7464
- issue: numberValue(recordValue2(event?.issue)?.number),
7465
- pullRequest: numberValue(recordValue2(event?.pull_request)?.number),
7466
- comment: numberValue(recordValue2(event?.comment)?.id),
7467
- schedule: stringValue2(event?.schedule),
7739
+ issue: numberValue(recordValue3(event?.issue)?.number),
7740
+ pullRequest: numberValue(recordValue3(event?.pull_request)?.number),
7741
+ comment: numberValue(recordValue3(event?.comment)?.id),
7742
+ schedule: stringValue3(event?.schedule),
7468
7743
  inputs: inputs ? pickRecord(inputs, ["issue_number", "sessionId", "message", "model", "title", "executable", "base"]) : void 0
7469
7744
  });
7470
7745
  return Object.keys(trigger).length > 0 ? trigger : void 0;
@@ -7482,17 +7757,21 @@ function triggerActorRole(eventName) {
7482
7757
  return "github event actor";
7483
7758
  }
7484
7759
  function jobContext(data) {
7485
- const job = pruneUndefined({
7486
- id: stringValue2(data.jobId) ?? void 0,
7487
- key: stringValue2(data.jobKey) ?? void 0,
7488
- flavor: stringValue2(data.jobFlavor) ?? void 0,
7489
- action: stringValue2(data.jobAction) ?? void 0,
7490
- capability: stringValue2(data.jobCapability) ?? void 0,
7491
- executable: stringValue2(data.jobExecutable) ?? void 0,
7492
- agent: stringValue2(data.jobAgent) ?? void 0,
7493
- schedule: stringValue2(data.jobSchedule) ?? void 0,
7760
+ const job = pruneUndefined2({
7761
+ id: stringValue3(data.jobId) ?? void 0,
7762
+ key: stringValue3(data.jobKey) ?? void 0,
7763
+ flavor: stringValue3(data.jobFlavor) ?? void 0,
7764
+ action: stringValue3(data.jobAction) ?? void 0,
7765
+ capability: stringValue3(data.jobCapability) ?? void 0,
7766
+ executable: stringValue3(data.jobExecutable) ?? void 0,
7767
+ agent: stringValue3(data.jobAgent) ?? void 0,
7768
+ schedule: stringValue3(data.jobSchedule) ?? void 0,
7494
7769
  target: data.jobTarget ?? void 0,
7495
- why: truncateString(stringValue2(data.jobWhy), 1e3),
7770
+ model: stringValue3(data.jobModel) ?? void 0,
7771
+ modelProvider: stringValue3(data.jobModelProvider) ?? void 0,
7772
+ modelName: stringValue3(data.jobModelName) ?? void 0,
7773
+ reasoningEffort: stringValue3(data.jobReasoningEffort) ?? void 0,
7774
+ why: truncateString(stringValue3(data.jobWhy), 1e3),
7496
7775
  saveReport: data.jobSaveReport === true ? true : void 0
7497
7776
  });
7498
7777
  return Object.keys(job).length > 0 ? job : void 0;
@@ -7500,23 +7779,23 @@ function jobContext(data) {
7500
7779
  function dispatchContext(event, trigger, job) {
7501
7780
  if (!event.dispatch && event.status !== "dispatch") return void 0;
7502
7781
  const dispatch2 = event.dispatch ?? {};
7503
- const context = pruneUndefined({
7782
+ const context = pruneUndefined2({
7504
7783
  triggeredBy: triggerLabel(trigger),
7505
- triggerKind: stringValue2(trigger?.kind),
7784
+ triggerKind: stringValue3(trigger?.kind),
7506
7785
  dispatchMode: dispatchMode(trigger),
7507
- githubActor: stringValue2(trigger?.githubActor) ?? stringValue2(trigger?.actor),
7508
- githubActorRole: stringValue2(trigger?.actorRole),
7786
+ githubActor: stringValue3(trigger?.githubActor) ?? stringValue3(trigger?.actor),
7787
+ githubActorRole: stringValue3(trigger?.actorRole),
7509
7788
  decidedBy: event.source,
7510
7789
  dispatchedBy: event.source,
7511
7790
  target: event.target,
7512
- action: dispatch2.action ?? stringValue2(job?.action),
7513
- capability: dispatch2.capability ?? stringValue2(job?.capability),
7791
+ action: dispatch2.action ?? stringValue3(job?.action),
7792
+ capability: dispatch2.capability ?? stringValue3(job?.capability),
7514
7793
  reason: event.reason
7515
7794
  });
7516
7795
  return Object.keys(context).length > 0 ? context : void 0;
7517
7796
  }
7518
7797
  function triggerLabel(trigger) {
7519
- const kind = stringValue2(trigger?.kind);
7798
+ const kind = stringValue3(trigger?.kind);
7520
7799
  if (kind === "schedule") return "GitHub schedule";
7521
7800
  if (kind === "manual-workflow-dispatch") return "manual workflow dispatch";
7522
7801
  if (kind === "issue_comment") return "GitHub issue comment";
@@ -7525,7 +7804,7 @@ function triggerLabel(trigger) {
7525
7804
  return "local run";
7526
7805
  }
7527
7806
  function dispatchMode(trigger) {
7528
- const kind = stringValue2(trigger?.kind);
7807
+ const kind = stringValue3(trigger?.kind);
7529
7808
  if (kind === "schedule") return "automated";
7530
7809
  if (kind === "manual-workflow-dispatch") return "manual";
7531
7810
  if (kind) return "event-driven";
@@ -7537,10 +7816,10 @@ function linkContext(stateRepo) {
7537
7816
  const repository = process.env.GITHUB_REPOSITORY?.trim();
7538
7817
  const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
7539
7818
  if (runId && repository) links.workflowRun = `${server}/${repository}/actions/runs/${runId}`;
7540
- const repo = stringValue2(stateRepo?.repo);
7541
- const branch = stringValue2(stateRepo?.branch);
7542
- const goalStatePath2 = stringValue2(stateRepo?.goalStatePath);
7543
- const logPath = stringValue2(stateRepo?.logPath);
7819
+ const repo = stringValue3(stateRepo?.repo);
7820
+ const branch = stringValue3(stateRepo?.branch);
7821
+ const goalStatePath2 = stringValue3(stateRepo?.goalStatePath);
7822
+ const logPath = stringValue3(stateRepo?.logPath);
7544
7823
  if (repo && branch && goalStatePath2) links.goalState = githubBlobUrl(repo, branch, goalStatePath2);
7545
7824
  if (repo && branch && logPath) links.log = githubBlobUrl(repo, branch, logPath);
7546
7825
  return Object.keys(links).length > 0 ? links : void 0;
@@ -7554,7 +7833,7 @@ function githubBlobUrl(repo, branch, filePath) {
7554
7833
  }
7555
7834
  }
7556
7835
  function routeStepForLog(step) {
7557
- return pruneUndefined({
7836
+ return pruneUndefined2({
7558
7837
  evidence: step.evidence,
7559
7838
  stage: step.stage,
7560
7839
  capability: step.capability,
@@ -7569,14 +7848,14 @@ function readGithubEvent() {
7569
7848
  try {
7570
7849
  if (!fs25.existsSync(eventPath)) return null;
7571
7850
  const parsed = JSON.parse(fs25.readFileSync(eventPath, "utf-8"));
7572
- return recordValue2(parsed);
7851
+ return recordValue3(parsed);
7573
7852
  } catch {
7574
7853
  return null;
7575
7854
  }
7576
7855
  }
7577
7856
  function addScalarChange(change, field, before, after) {
7578
7857
  if (before === after) return;
7579
- change[field] = pruneUndefined({ from: before, to: after });
7858
+ change[field] = pruneUndefined2({ from: before, to: after });
7580
7859
  }
7581
7860
  function diffRecordKeys(before, after) {
7582
7861
  const beforeKeys = new Set(Object.keys(before));
@@ -7594,7 +7873,7 @@ function diffStringArrays(before, after) {
7594
7873
  removed: before.filter((item) => !afterSet.has(item)).sort()
7595
7874
  };
7596
7875
  }
7597
- function recordValue2(value) {
7876
+ function recordValue3(value) {
7598
7877
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
7599
7878
  }
7600
7879
  function stringArrayValue(value) {
@@ -7610,7 +7889,7 @@ function pickRecord(input, keys) {
7610
7889
  }
7611
7890
  return out;
7612
7891
  }
7613
- function pruneUndefined(input) {
7892
+ function pruneUndefined2(input) {
7614
7893
  for (const key of Object.keys(input)) {
7615
7894
  if (input[key] === void 0) delete input[key];
7616
7895
  }
@@ -7620,7 +7899,7 @@ function truncateString(value, max) {
7620
7899
  if (!value) return void 0;
7621
7900
  return value.length > max ? `${value.slice(0, max)}...` : value;
7622
7901
  }
7623
- function stringValue2(value) {
7902
+ function stringValue3(value) {
7624
7903
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
7625
7904
  }
7626
7905
  function safePathSegment(value) {
@@ -7632,6 +7911,7 @@ var init_runLog = __esm({
7632
7911
  "src/goal/runLog.ts"() {
7633
7912
  "use strict";
7634
7913
  init_stateRepo();
7914
+ init_runIndex();
7635
7915
  init_state2();
7636
7916
  LOGS_KEY = "__goalRunLogs";
7637
7917
  LOG_RUN_KEY = "__goalRunLogRunId";
@@ -8824,7 +9104,7 @@ var init_goalCapabilityScheduling = __esm({
8824
9104
 
8825
9105
  // src/scripts/advanceManagedGoal.ts
8826
9106
  function stageManagedGoalDecision(data, goalId, goal, goalState, decision, details) {
8827
- if (decision.kind === "dispatch") {
9107
+ if (decision.kind === "dispatch" || decision.kind === "dispatchWorkflow") {
8828
9108
  stageGoalRunLogEvent(data, goalId, {
8829
9109
  source: "goal-manager",
8830
9110
  event: "goal.tick.dispatch",
@@ -8833,14 +9113,23 @@ function stageManagedGoalDecision(data, goalId, goal, goalState, decision, detai
8833
9113
  stage: decision.stage,
8834
9114
  evidence: decision.evidence,
8835
9115
  status: decision.kind,
8836
- dispatch: {
9116
+ dispatch: decision.kind === "dispatchWorkflow" ? {
9117
+ workflow: decision.workflow,
9118
+ cliArgs: decision.cliArgs
9119
+ } : {
8837
9120
  capability: decision.capability,
8838
9121
  cliArgs: decision.cliArgs,
8839
9122
  ...decision.executable ? { executable: decision.executable } : {}
8840
9123
  },
8841
9124
  goal: details.goalSnapshot,
8842
9125
  inspection: details.inspection,
8843
- decision: {
9126
+ decision: decision.kind === "dispatchWorkflow" ? {
9127
+ kind: decision.kind,
9128
+ evidence: decision.evidence,
9129
+ stage: decision.stage,
9130
+ workflow: decision.workflow,
9131
+ cliArgs: decision.cliArgs
9132
+ } : {
8844
9133
  kind: decision.kind,
8845
9134
  evidence: decision.evidence,
8846
9135
  stage: decision.stage,
@@ -8923,7 +9212,7 @@ function previousDispatchWasTargetInstance(managed, previousScheduleState) {
8923
9212
  return previous.targetId === targetId || previous.targetId.startsWith(`${targetId}-`);
8924
9213
  }
8925
9214
  function ensureIssueFactIfNeeded(goal, goalId, cwd) {
8926
- if (!routeNeedsIssueFact(goal)) return;
9215
+ if (!routeNeedsIssueFact(goal) && !workflowNeedsIssueFact(goal)) return;
8927
9216
  const existing = normalizeIssueNumber(goal.facts.issue);
8928
9217
  if (existing !== null) {
8929
9218
  goal.facts.issue = existing;
@@ -8940,6 +9229,14 @@ function routeNeedsIssueFact(goal) {
8940
9229
  })
8941
9230
  );
8942
9231
  }
9232
+ function workflowNeedsIssueFact(goal) {
9233
+ return Object.values(goal.workflowRef?.args ?? {}).some(isIssueFactReference);
9234
+ }
9235
+ function isIssueFactReference(value) {
9236
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9237
+ const record2 = value;
9238
+ return Object.keys(record2).length === 1 && record2.fact === "issue";
9239
+ }
8943
9240
  function normalizeIssueNumber(value) {
8944
9241
  if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
8945
9242
  if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
@@ -9202,6 +9499,16 @@ var init_advanceManagedGoal = __esm({
9202
9499
  ctx.output.reason = decision.kind === "done" ? "managed goal complete" : decision.reason;
9203
9500
  return;
9204
9501
  }
9502
+ if (decision.kind === "dispatchWorkflow") {
9503
+ ctx.output.nextDispatch = {
9504
+ workflow: decision.workflow,
9505
+ cliArgs: decision.cliArgs,
9506
+ ...decision.saveReport === true ? { saveReport: true } : {},
9507
+ resultTarget: { type: "goal", id: goal.id }
9508
+ };
9509
+ ctx.output.reason = `dispatch workflow ${decision.workflow} for ${decision.evidence}`;
9510
+ return;
9511
+ }
9205
9512
  ctx.output.nextDispatch = {
9206
9513
  capability: decision.capability,
9207
9514
  cliArgs: decision.cliArgs,
@@ -14180,7 +14487,7 @@ var init_keys = __esm({
14180
14487
  // src/stateRepoGithub.ts
14181
14488
  import * as fs38 from "fs";
14182
14489
  import * as path36 from "path";
14183
- function recordValue3(value) {
14490
+ function recordValue4(value) {
14184
14491
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
14185
14492
  }
14186
14493
  function contentsUrl(owner, repo, filePath) {
@@ -14236,12 +14543,12 @@ async function loadGithubStateConfig(opts) {
14236
14543
  throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
14237
14544
  }
14238
14545
  }
14239
- const githubRaw = recordValue3(raw.github) ?? {};
14546
+ const githubRaw = recordValue4(raw.github) ?? {};
14240
14547
  const github = {
14241
14548
  owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
14242
14549
  repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
14243
14550
  };
14244
- const nestedState = recordValue3(raw.state) ?? {};
14551
+ const nestedState = recordValue4(raw.state) ?? {};
14245
14552
  const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
14246
14553
  const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
14247
14554
  const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
@@ -19040,8 +19347,10 @@ function jobReferenceBlock(profileName, profile, data) {
19040
19347
  }
19041
19348
  async function runExecutable(profileName, input) {
19042
19349
  const stageStartedAt = Date.now();
19350
+ let finishRunIndex = null;
19043
19351
  emitEvent(input.cwd, { executable: profileName, kind: "stage_start" });
19044
19352
  const finishAndEnd = (out) => {
19353
+ finishRunIndex?.(out);
19045
19354
  emitEvent(input.cwd, {
19046
19355
  executable: profileName,
19047
19356
  kind: "stage_end",
@@ -19126,6 +19435,41 @@ async function runExecutable(profileName, input) {
19126
19435
  data: { ...input.preloadedData ?? {} },
19127
19436
  output: { exitCode: 0 }
19128
19437
  };
19438
+ ctx.data.jobModel = modelSpec;
19439
+ ctx.data.jobModelProvider = model.provider;
19440
+ ctx.data.jobModelName = model.model;
19441
+ if (reasoningEffort) ctx.data.jobReasoningEffort = reasoningEffort;
19442
+ const runIndexStartedAt = new Date(stageStartedAt).toISOString();
19443
+ if (!input.skipConfig) {
19444
+ upsertRunIndexRowBestEffort(
19445
+ config,
19446
+ input.cwd,
19447
+ runIndexRowFromJobContext({
19448
+ data: ctx.data,
19449
+ profileName,
19450
+ profile,
19451
+ status: "running",
19452
+ startedAt: runIndexStartedAt,
19453
+ updatedAt: runIndexStartedAt
19454
+ })
19455
+ );
19456
+ finishRunIndex = (out) => {
19457
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
19458
+ upsertRunIndexRowBestEffort(
19459
+ config,
19460
+ input.cwd,
19461
+ runIndexRowFromJobContext({
19462
+ data: ctx.data,
19463
+ profileName,
19464
+ profile,
19465
+ status: statusFromExitCode(out.exitCode),
19466
+ startedAt: runIndexStartedAt,
19467
+ updatedAt: finishedAt,
19468
+ reason: out.reason
19469
+ })
19470
+ );
19471
+ };
19472
+ }
19129
19473
  const taskTarget = args.issue ?? args.pr;
19130
19474
  const taskArtifacts = typeof taskTarget === "number" && Number.isFinite(taskTarget) ? (() => {
19131
19475
  const taskType = args.issue ? "issue" : "pr";
@@ -19820,6 +20164,7 @@ var init_executor = __esm({
19820
20164
  init_litellm();
19821
20165
  init_profile();
19822
20166
  init_registry();
20167
+ init_runIndex();
19823
20168
  init_runtimePaths();
19824
20169
  init_scripts();
19825
20170
  init_stateWorkspace();
@@ -20095,6 +20440,10 @@ function shouldRunCapabilityWorkflow(job, workflow, capabilityIdentity, selected
20095
20440
  async function runCapabilityWorkflow(parent, workflow, capability, base) {
20096
20441
  let chainData = {
20097
20442
  ...base.preloadedData ?? {},
20443
+ runSubjectType: "workflow",
20444
+ runSubjectId: capability.slug,
20445
+ runSubjectLabel: capability.title,
20446
+ runSubjectWorkflow: capability.slug,
20098
20447
  workflowCapability: capability.slug,
20099
20448
  workflowTitle: capability.title,
20100
20449
  workflowStepCount: workflow.steps.length,
@@ -22655,6 +23004,10 @@ function emitSse(res, event) {
22655
23004
 
22656
23005
  `);
22657
23006
  }
23007
+ function openSseStream(res, chatId) {
23008
+ writeSseHeaders(res);
23009
+ emitSse(res, { type: "chat", chatId });
23010
+ }
22658
23011
  function envGithubToken() {
22659
23012
  return (process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT ?? "").trim();
22660
23013
  }
@@ -22664,16 +23017,6 @@ function parseRepoSlug(repo) {
22664
23017
  if (!owner || !name) return null;
22665
23018
  return { owner, repo: name };
22666
23019
  }
22667
- function streamStartupError(res, dir, chatId, err) {
22668
- const sinceFloor = getLastSeq(dir, chatId);
22669
- const emitToLog = beginTurn(dir, chatId);
22670
- emitToLog({
22671
- type: "error",
22672
- chatId,
22673
- error: err instanceof Error ? err.message : String(err)
22674
- });
22675
- streamToRes(res, dir, chatId, sinceFloor);
22676
- }
22677
23020
  function translateChatEvent(event, chatId) {
22678
23021
  switch (event.event) {
22679
23022
  case "chat.message": {
@@ -22727,9 +23070,10 @@ function enqueue(chatId, fn) {
22727
23070
  );
22728
23071
  return next;
22729
23072
  }
22730
- function streamToRes(res, dir, chatId, since) {
22731
- writeSseHeaders(res);
22732
- emitSse(res, { type: "chat", chatId });
23073
+ function streamToRes(res, dir, chatId, since, opts = {}) {
23074
+ if (!opts.opened) {
23075
+ openSseStream(res, chatId);
23076
+ }
22733
23077
  let maxSent = since;
22734
23078
  const unsubscribe = subscribe(
22735
23079
  dir,
@@ -22773,7 +23117,17 @@ async function handleChatTurn(req, res, chatId, opts) {
22773
23117
  const storeRepoUrl = strField(body, "storeRepoUrl");
22774
23118
  const storeRef = strField(body, "storeRef");
22775
23119
  const allowCrossRepo = boolField(body, "allowCrossRepo");
23120
+ const firstTurn = boolField(body, "firstTurn");
22776
23121
  const agentIdentity = agentIdentityField(body);
23122
+ openSseStream(res, chatId);
23123
+ if (repo) {
23124
+ emitSse(res, {
23125
+ type: "tool_use",
23126
+ chatId,
23127
+ name: "prepare_repo",
23128
+ input: { repo }
23129
+ });
23130
+ }
22777
23131
  let agentCwd;
22778
23132
  try {
22779
23133
  agentCwd = await ensureRepoCwd({
@@ -22784,7 +23138,12 @@ async function handleChatTurn(req, res, chatId, opts) {
22784
23138
  cloneRepo: opts.cloneRepo
22785
23139
  });
22786
23140
  } catch (err) {
22787
- streamStartupError(res, opts.cwd, chatId, err);
23141
+ emitSse(res, {
23142
+ type: "error",
23143
+ chatId,
23144
+ error: err instanceof Error ? err.message : String(err)
23145
+ });
23146
+ res.end();
22788
23147
  return;
22789
23148
  }
22790
23149
  const stateToken = repoToken || envGithubToken();
@@ -22807,7 +23166,7 @@ async function handleChatTurn(req, res, chatId, opts) {
22807
23166
  }
22808
23167
  const sessionFile = sessionFilePath(agentCwd, chatId);
22809
23168
  const brainEventsFile = brainEventsFilePath(agentCwd, chatId);
22810
- if (stateConfig && stateToken) {
23169
+ if (!firstTurn && stateConfig && stateToken) {
22811
23170
  try {
22812
23171
  await syncJsonlFileFromGithubState({
22813
23172
  config: stateConfig,
@@ -22900,7 +23259,7 @@ async function handleChatTurn(req, res, chatId, opts) {
22900
23259
  }
22901
23260
  }
22902
23261
  });
22903
- streamToRes(res, agentCwd, chatId, sinceFloor);
23262
+ streamToRes(res, agentCwd, chatId, sinceFloor, { opened: true });
22904
23263
  }
22905
23264
  function buildServer(opts) {
22906
23265
  const runTurn = opts.runTurn ?? runChatTurn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.323",
3
+ "version": "0.4.325",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",