@kody-ade/kody-engine 0.4.323 → 0.4.324

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 +442 -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.324",
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,217 @@ 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) : [];
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 runSubjectType(data) {
6552
+ const value = data.runSubjectType;
6553
+ return isRunSubjectType(value) ? value : null;
6554
+ }
6555
+ function statusFromGoalEvent(event, decision) {
6556
+ const status = stringValue2(event.status)?.toLowerCase();
6557
+ const eventName = stringValue2(event.event)?.toLowerCase() ?? "";
6558
+ const decisionKind = stringValue2(decision?.kind)?.toLowerCase();
6559
+ if (status === "success" || status === "completed" || decisionKind === "done") return "success";
6560
+ if (status === "failure" || status === "failed" || eventName.includes("fail")) return "failed";
6561
+ if (status === "cancelled") return "cancelled";
6562
+ if (decisionKind === "blocked") return "blocked";
6563
+ if (status === "running" || status === "dispatch" || eventName.includes("dispatch")) return "running";
6564
+ return "recorded";
6565
+ }
6566
+ function triggerKindFromEnv() {
6567
+ const eventName = process.env.GITHUB_EVENT_NAME?.trim();
6568
+ if (!eventName) return void 0;
6569
+ if (eventName === "schedule") return "schedule";
6570
+ if (eventName === "workflow_dispatch") return "manual-workflow-dispatch";
6571
+ return eventName;
6572
+ }
6573
+ function triggerMode(kind) {
6574
+ if (!kind) return void 0;
6575
+ if (kind === "schedule") return "scheduled";
6576
+ if (kind === "manual-workflow-dispatch") return "manual";
6577
+ if (kind === "local") return "local";
6578
+ return "event";
6579
+ }
6580
+ function isConflict(err) {
6581
+ const msg = err instanceof Error ? err.message : String(err);
6582
+ return /HTTP 409/i.test(msg) || /HTTP 422/i.test(msg) || /does not match|is at|but expected/i.test(msg);
6583
+ }
6584
+ function recordValue2(value) {
6585
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
6586
+ }
6587
+ function stringValue2(value) {
6588
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6589
+ }
6590
+ function pruneUndefined(input) {
6591
+ for (const key of Object.keys(input)) {
6592
+ if (input[key] === void 0) delete input[key];
6593
+ }
6594
+ return input;
6595
+ }
6596
+ var RUN_INDEX_PATH, MAX_RUNS;
6597
+ var init_runIndex = __esm({
6598
+ "src/runIndex.ts"() {
6599
+ "use strict";
6600
+ init_stateRepo();
6601
+ RUN_INDEX_PATH = "runs/index.json";
6602
+ MAX_RUNS = 200;
6603
+ }
6604
+ });
6605
+
6395
6606
  // src/pushWithRetry.ts
6396
6607
  import { execFileSync as execFileSync6 } from "child_process";
6397
6608
  function sleepSync2(ms) {
@@ -6904,6 +7115,32 @@ function planManagedGoalTick(goal) {
6904
7115
  goal.nextAction = "done";
6905
7116
  return { kind: "done" };
6906
7117
  }
7118
+ const workflow = goal.workflowRef;
7119
+ if (workflow) {
7120
+ const workflowStep = { evidence: missing, stage: "workflow", capability: workflow.id };
7121
+ const progressDecision2 = decisionFromEvidenceProgress(goal, workflowStep, missing);
7122
+ if (progressDecision2) return progressDecision2;
7123
+ const resolved2 = resolveWorkflowRefArgs(goal, workflow);
7124
+ if (!resolved2.ok) {
7125
+ goal.stage = "blocked";
7126
+ goal.reason = resolved2.reason;
7127
+ goal.nextAction = "fix workflow args";
7128
+ pushBlocker(goal, resolved2.reason);
7129
+ return { kind: "blocked", evidence: missing, stage: "workflow", reason: resolved2.reason };
7130
+ }
7131
+ goal.stage = "workflow";
7132
+ goal.facts.pendingEvidence = missing;
7133
+ goal.reason = `dispatch workflow ${workflow.id} for ${missing}`;
7134
+ goal.nextAction = "dispatch workflow";
7135
+ return {
7136
+ kind: "dispatchWorkflow",
7137
+ evidence: missing,
7138
+ stage: "workflow",
7139
+ workflow: workflow.id,
7140
+ cliArgs: resolved2.cliArgs,
7141
+ ...workflow.saveReport === true ? { saveReport: true } : {}
7142
+ };
7143
+ }
6907
7144
  const step = goal.route.find((candidate) => candidate.evidence === missing);
6908
7145
  if (!step) {
6909
7146
  if (isSimpleGoal(goal) && missing === SIMPLE_GOAL_EVIDENCE) {
@@ -6959,6 +7196,15 @@ function planManagedGoalTick(goal) {
6959
7196
  ...step.saveReport === true ? { saveReport: true } : {}
6960
7197
  };
6961
7198
  }
7199
+ function resolveWorkflowRefArgs(goal, workflow) {
7200
+ const step = {
7201
+ evidence: "__workflow__",
7202
+ stage: "workflow",
7203
+ capability: workflow.id,
7204
+ args: workflow.args
7205
+ };
7206
+ return resolveRouteArgs(goal, step);
7207
+ }
6962
7208
  function decisionFromEvidenceProgress(goal, step, evidence) {
6963
7209
  const progress = goal.evidenceState?.[evidence];
6964
7210
  if (!progress) return null;
@@ -7095,6 +7341,19 @@ function asLoopTarget(value) {
7095
7341
  if (typeof raw.id !== "string" || raw.id.trim().length === 0) return void 0;
7096
7342
  return { type: raw.type, id: raw.id };
7097
7343
  }
7344
+ function asWorkflowRef(value) {
7345
+ const raw = asRecord(value);
7346
+ if (!raw) return void 0;
7347
+ if (typeof raw.id !== "string" || raw.id.trim().length === 0) return void 0;
7348
+ const args = raw.args === void 0 ? void 0 : asRecord(raw.args);
7349
+ if (raw.args !== void 0 && !args) return void 0;
7350
+ return {
7351
+ id: raw.id.trim(),
7352
+ ...typeof raw.source === "string" && raw.source.trim().length > 0 ? { source: raw.source.trim() } : {},
7353
+ ...args ? { args } : {},
7354
+ ...raw.saveReport === true ? { saveReport: true } : {}
7355
+ };
7356
+ }
7098
7357
  function managedGoalFromState(state) {
7099
7358
  const extra = state.extra;
7100
7359
  const destination = asRecord(extra.destination);
@@ -7111,6 +7370,7 @@ function managedGoalFromState(state) {
7111
7370
  destination: { outcome: destination.outcome, evidence },
7112
7371
  capabilities,
7113
7372
  route,
7373
+ workflowRef: asWorkflowRef(extra.workflowRef),
7114
7374
  schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
7115
7375
  preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
7116
7376
  loopTarget: asLoopTarget(extra.loopTarget),
@@ -7131,6 +7391,7 @@ function writeManagedGoalToState(state, goal) {
7131
7391
  destination: goal.destination,
7132
7392
  capabilities: goal.capabilities,
7133
7393
  route: goal.route,
7394
+ workflowRef: goal.workflowRef,
7134
7395
  stage: goal.stage,
7135
7396
  facts: goal.facts,
7136
7397
  blockers: goal.blockers,
@@ -7220,9 +7481,15 @@ function flushGoalRunLogEvents(config, cwd, data) {
7220
7481
  const logs = goalRunLogs(data);
7221
7482
  for (const [goalId, log2] of Object.entries(logs)) {
7222
7483
  if (log2.events.length === 0) continue;
7223
- const lines = `${log2.events.map((event) => JSON.stringify(enrichGoalRunLogEvent(config, data, log2.path, event))).join("\n")}
7484
+ const enrichedEvents = log2.events.map((event) => enrichGoalRunLogEvent(config, data, log2.path, event));
7485
+ const lines = `${enrichedEvents.map((event) => JSON.stringify(event)).join("\n")}
7224
7486
  `;
7225
7487
  appendStateLine(config, cwd, log2.path, lines, `chore(goal-logs): append ${goalId}`);
7488
+ upsertRunIndexRowBestEffort(
7489
+ config,
7490
+ cwd,
7491
+ runIndexRowFromGoalEvents(goalId, log2.path, enrichedEvents)
7492
+ );
7226
7493
  log2.events = [];
7227
7494
  }
7228
7495
  }
@@ -7250,6 +7517,7 @@ function goalRunLogSnapshot(goalId, goalState, goal) {
7250
7517
  pendingEvidence,
7251
7518
  capabilities: [...goal.capabilities],
7252
7519
  route: goal.route.map(routeStepForLog),
7520
+ workflowRef: goal.workflowRef,
7253
7521
  schedule: goal.schedule,
7254
7522
  preferredRunTime: goal.preferredRunTime,
7255
7523
  loopTarget: goal.loopTarget,
@@ -7264,8 +7532,8 @@ function goalRunLogChange(before, after) {
7264
7532
  addScalarChange(change, "state", before.state, after.state);
7265
7533
  addScalarChange(change, "stage", before.stage, after.stage);
7266
7534
  addScalarChange(change, "pendingEvidence", before.pendingEvidence, after.pendingEvidence);
7267
- const beforeFacts = recordValue2(before.facts);
7268
- const afterFacts = recordValue2(after.facts);
7535
+ const beforeFacts = recordValue3(before.facts);
7536
+ const afterFacts = recordValue3(after.facts);
7269
7537
  if (beforeFacts || afterFacts) change.facts = diffRecordKeys(beforeFacts ?? {}, afterFacts ?? {});
7270
7538
  const beforeBlockers = stringArrayValue(before.blockers);
7271
7539
  const afterBlockers = stringArrayValue(after.blockers);
@@ -7279,8 +7547,8 @@ function goalRunLogChange(before, after) {
7279
7547
  const evidence = diffStringArrays(beforeSatisfied ?? [], afterSatisfied ?? []);
7280
7548
  if (evidence.added.length > 0 || evidence.removed.length > 0) change.satisfiedEvidence = evidence;
7281
7549
  }
7282
- const beforeEvidenceState = recordValue2(before.evidenceState);
7283
- const afterEvidenceState = recordValue2(after.evidenceState);
7550
+ const beforeEvidenceState = recordValue3(before.evidenceState);
7551
+ const afterEvidenceState = recordValue3(after.evidenceState);
7284
7552
  if (beforeEvidenceState || afterEvidenceState) {
7285
7553
  change.evidenceState = diffRecordKeys(beforeEvidenceState ?? {}, afterEvidenceState ?? {});
7286
7554
  }
@@ -7305,7 +7573,7 @@ function goalRunStartedAt(data) {
7305
7573
  function goalRunId(data) {
7306
7574
  const existing = data[LOG_RUN_KEY];
7307
7575
  if (typeof existing === "string" && existing.length > 0) return existing;
7308
- const raw = stringValue2(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
7576
+ const raw = stringValue3(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
7309
7577
  const safe = safePathSegment(raw);
7310
7578
  data[LOG_RUN_KEY] = safe;
7311
7579
  return safe;
@@ -7337,7 +7605,7 @@ function enrichGoalRunLogEvent(config, data, logPath, event) {
7337
7605
  const job = event.job ?? jobContext(data);
7338
7606
  const run = event.run ?? runContext(data);
7339
7607
  const links = event.links ?? linkContext(stateRepo);
7340
- const enriched = pruneUndefined({
7608
+ const enriched = pruneUndefined2({
7341
7609
  ...event,
7342
7610
  run,
7343
7611
  repo: event.repo ?? repoContext(config),
@@ -7351,23 +7619,23 @@ function enrichGoalRunLogEvent(config, data, logPath, event) {
7351
7619
  return enriched;
7352
7620
  }
7353
7621
  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({
7622
+ const run = recordValue3(event.run);
7623
+ const trigger = recordValue3(event.trigger);
7624
+ const goal = recordValue3(event.goal);
7625
+ const inspection = recordValue3(event.inspection);
7626
+ const capabilityOutput = recordValue3(inspection?.capabilityOutput);
7627
+ return pruneUndefined2({
7360
7628
  version: 1,
7361
- runId: stringValue2(run?.id) ?? void 0,
7362
- workflowRunId: stringValue2(run?.githubRunId) ?? void 0,
7363
- triggerKind: stringValue2(trigger?.kind) ?? void 0,
7629
+ runId: stringValue3(run?.id) ?? void 0,
7630
+ workflowRunId: stringValue3(run?.githubRunId) ?? void 0,
7631
+ triggerKind: stringValue3(trigger?.kind) ?? void 0,
7364
7632
  source: event.source,
7365
7633
  event: event.event,
7366
- goal: pruneUndefined({
7634
+ goal: pruneUndefined2({
7367
7635
  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
7636
+ type: event.goalType ?? stringValue3(goal?.type) ?? void 0,
7637
+ state: event.goalState ?? stringValue3(goal?.state) ?? void 0,
7638
+ stage: event.stage ?? stringValue3(goal?.stage) ?? void 0
7371
7639
  }),
7372
7640
  evidence: goalTraceEvidence(event, goal, inspection),
7373
7641
  capability: event.dispatch ?? capabilityDispatchFromOutput(capabilityOutput),
@@ -7377,30 +7645,30 @@ function goalRunTrace(event) {
7377
7645
  });
7378
7646
  }
7379
7647
  function goalTraceEvidence(event, goal, inspection) {
7380
- const expectedEvidence = recordValue2(inspection?.expectedEvidence);
7381
- const out = pruneUndefined({
7648
+ const expectedEvidence = recordValue3(inspection?.expectedEvidence);
7649
+ const out = pruneUndefined2({
7382
7650
  current: event.evidence,
7383
7651
  required: stringArrayValue(goal?.requiredEvidence) ?? stringArrayValue(inspection?.requiredEvidence) ?? void 0,
7384
7652
  satisfied: stringArrayValue(goal?.satisfiedEvidence) ?? stringArrayValue(inspection?.satisfiedEvidence) ?? void 0,
7385
7653
  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,
7654
+ pending: stringValue3(goal?.pendingEvidence) ?? stringValue3(inspection?.pendingEvidence) ?? stringValue3(expectedEvidence?.pendingBefore) ?? void 0,
7387
7655
  values: event.evidenceValues
7388
7656
  });
7389
7657
  return Object.keys(out).length > 0 ? out : void 0;
7390
7658
  }
7391
7659
  function capabilityDispatchFromOutput(output) {
7392
7660
  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
7661
+ const dispatch2 = pruneUndefined2({
7662
+ capability: stringValue3(output.capability) ?? void 0,
7663
+ executable: stringValue3(output.executable) ?? void 0,
7664
+ action: stringValue3(output.action) ?? void 0
7397
7665
  });
7398
7666
  return Object.keys(dispatch2).length > 0 ? dispatch2 : void 0;
7399
7667
  }
7400
7668
  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,
7669
+ const result = pruneUndefined2({
7670
+ status: event.status ?? stringValue3(capabilityOutput?.status) ?? void 0,
7671
+ summary: event.reason ?? stringValue3(capabilityOutput?.summary) ?? void 0,
7404
7672
  blockers: event.inspection ? stringArrayValue(capabilityOutput?.blockers) ?? void 0 : void 0,
7405
7673
  artifacts: event.artifacts
7406
7674
  });
@@ -7411,7 +7679,7 @@ function runContext(data) {
7411
7679
  const attempt = process.env.GITHUB_RUN_ATTEMPT?.trim();
7412
7680
  const repository = process.env.GITHUB_REPOSITORY?.trim();
7413
7681
  const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
7414
- return pruneUndefined({
7682
+ return pruneUndefined2({
7415
7683
  id: goalRunId(data),
7416
7684
  provider: runId ? "github-actions" : "local",
7417
7685
  githubRunId: runId || void 0,
@@ -7425,7 +7693,7 @@ function runContext(data) {
7425
7693
  function repoContext(config) {
7426
7694
  const owner = config.github?.owner;
7427
7695
  const repo = config.github?.repo;
7428
- return pruneUndefined({
7696
+ return pruneUndefined2({
7429
7697
  owner,
7430
7698
  repo,
7431
7699
  fullName: owner && repo ? `${owner}/${repo}` : process.env.GITHUB_REPOSITORY?.trim() || void 0,
@@ -7449,11 +7717,11 @@ function stateRepoContext(config, goalId, logPath) {
7449
7717
  }
7450
7718
  function triggerContext() {
7451
7719
  const event = readGithubEvent();
7452
- const inputs = recordValue2(event?.inputs);
7720
+ const inputs = recordValue3(event?.inputs);
7453
7721
  const eventName = process.env.GITHUB_EVENT_NAME?.trim() || void 0;
7454
7722
  const githubActor = process.env.GITHUB_ACTOR?.trim() || void 0;
7455
7723
  if (!eventName && !githubActor && !process.env.GITHUB_EVENT_PATH) return void 0;
7456
- const trigger = pruneUndefined({
7724
+ const trigger = pruneUndefined2({
7457
7725
  source: eventName ? "github-actions" : "local",
7458
7726
  kind: triggerKind(eventName),
7459
7727
  eventName,
@@ -7461,10 +7729,10 @@ function triggerContext() {
7461
7729
  githubActor,
7462
7730
  actorRole: triggerActorRole(eventName),
7463
7731
  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),
7732
+ issue: numberValue(recordValue3(event?.issue)?.number),
7733
+ pullRequest: numberValue(recordValue3(event?.pull_request)?.number),
7734
+ comment: numberValue(recordValue3(event?.comment)?.id),
7735
+ schedule: stringValue3(event?.schedule),
7468
7736
  inputs: inputs ? pickRecord(inputs, ["issue_number", "sessionId", "message", "model", "title", "executable", "base"]) : void 0
7469
7737
  });
7470
7738
  return Object.keys(trigger).length > 0 ? trigger : void 0;
@@ -7482,17 +7750,21 @@ function triggerActorRole(eventName) {
7482
7750
  return "github event actor";
7483
7751
  }
7484
7752
  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,
7753
+ const job = pruneUndefined2({
7754
+ id: stringValue3(data.jobId) ?? void 0,
7755
+ key: stringValue3(data.jobKey) ?? void 0,
7756
+ flavor: stringValue3(data.jobFlavor) ?? void 0,
7757
+ action: stringValue3(data.jobAction) ?? void 0,
7758
+ capability: stringValue3(data.jobCapability) ?? void 0,
7759
+ executable: stringValue3(data.jobExecutable) ?? void 0,
7760
+ agent: stringValue3(data.jobAgent) ?? void 0,
7761
+ schedule: stringValue3(data.jobSchedule) ?? void 0,
7494
7762
  target: data.jobTarget ?? void 0,
7495
- why: truncateString(stringValue2(data.jobWhy), 1e3),
7763
+ model: stringValue3(data.jobModel) ?? void 0,
7764
+ modelProvider: stringValue3(data.jobModelProvider) ?? void 0,
7765
+ modelName: stringValue3(data.jobModelName) ?? void 0,
7766
+ reasoningEffort: stringValue3(data.jobReasoningEffort) ?? void 0,
7767
+ why: truncateString(stringValue3(data.jobWhy), 1e3),
7496
7768
  saveReport: data.jobSaveReport === true ? true : void 0
7497
7769
  });
7498
7770
  return Object.keys(job).length > 0 ? job : void 0;
@@ -7500,23 +7772,23 @@ function jobContext(data) {
7500
7772
  function dispatchContext(event, trigger, job) {
7501
7773
  if (!event.dispatch && event.status !== "dispatch") return void 0;
7502
7774
  const dispatch2 = event.dispatch ?? {};
7503
- const context = pruneUndefined({
7775
+ const context = pruneUndefined2({
7504
7776
  triggeredBy: triggerLabel(trigger),
7505
- triggerKind: stringValue2(trigger?.kind),
7777
+ triggerKind: stringValue3(trigger?.kind),
7506
7778
  dispatchMode: dispatchMode(trigger),
7507
- githubActor: stringValue2(trigger?.githubActor) ?? stringValue2(trigger?.actor),
7508
- githubActorRole: stringValue2(trigger?.actorRole),
7779
+ githubActor: stringValue3(trigger?.githubActor) ?? stringValue3(trigger?.actor),
7780
+ githubActorRole: stringValue3(trigger?.actorRole),
7509
7781
  decidedBy: event.source,
7510
7782
  dispatchedBy: event.source,
7511
7783
  target: event.target,
7512
- action: dispatch2.action ?? stringValue2(job?.action),
7513
- capability: dispatch2.capability ?? stringValue2(job?.capability),
7784
+ action: dispatch2.action ?? stringValue3(job?.action),
7785
+ capability: dispatch2.capability ?? stringValue3(job?.capability),
7514
7786
  reason: event.reason
7515
7787
  });
7516
7788
  return Object.keys(context).length > 0 ? context : void 0;
7517
7789
  }
7518
7790
  function triggerLabel(trigger) {
7519
- const kind = stringValue2(trigger?.kind);
7791
+ const kind = stringValue3(trigger?.kind);
7520
7792
  if (kind === "schedule") return "GitHub schedule";
7521
7793
  if (kind === "manual-workflow-dispatch") return "manual workflow dispatch";
7522
7794
  if (kind === "issue_comment") return "GitHub issue comment";
@@ -7525,7 +7797,7 @@ function triggerLabel(trigger) {
7525
7797
  return "local run";
7526
7798
  }
7527
7799
  function dispatchMode(trigger) {
7528
- const kind = stringValue2(trigger?.kind);
7800
+ const kind = stringValue3(trigger?.kind);
7529
7801
  if (kind === "schedule") return "automated";
7530
7802
  if (kind === "manual-workflow-dispatch") return "manual";
7531
7803
  if (kind) return "event-driven";
@@ -7537,10 +7809,10 @@ function linkContext(stateRepo) {
7537
7809
  const repository = process.env.GITHUB_REPOSITORY?.trim();
7538
7810
  const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
7539
7811
  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);
7812
+ const repo = stringValue3(stateRepo?.repo);
7813
+ const branch = stringValue3(stateRepo?.branch);
7814
+ const goalStatePath2 = stringValue3(stateRepo?.goalStatePath);
7815
+ const logPath = stringValue3(stateRepo?.logPath);
7544
7816
  if (repo && branch && goalStatePath2) links.goalState = githubBlobUrl(repo, branch, goalStatePath2);
7545
7817
  if (repo && branch && logPath) links.log = githubBlobUrl(repo, branch, logPath);
7546
7818
  return Object.keys(links).length > 0 ? links : void 0;
@@ -7554,7 +7826,7 @@ function githubBlobUrl(repo, branch, filePath) {
7554
7826
  }
7555
7827
  }
7556
7828
  function routeStepForLog(step) {
7557
- return pruneUndefined({
7829
+ return pruneUndefined2({
7558
7830
  evidence: step.evidence,
7559
7831
  stage: step.stage,
7560
7832
  capability: step.capability,
@@ -7569,14 +7841,14 @@ function readGithubEvent() {
7569
7841
  try {
7570
7842
  if (!fs25.existsSync(eventPath)) return null;
7571
7843
  const parsed = JSON.parse(fs25.readFileSync(eventPath, "utf-8"));
7572
- return recordValue2(parsed);
7844
+ return recordValue3(parsed);
7573
7845
  } catch {
7574
7846
  return null;
7575
7847
  }
7576
7848
  }
7577
7849
  function addScalarChange(change, field, before, after) {
7578
7850
  if (before === after) return;
7579
- change[field] = pruneUndefined({ from: before, to: after });
7851
+ change[field] = pruneUndefined2({ from: before, to: after });
7580
7852
  }
7581
7853
  function diffRecordKeys(before, after) {
7582
7854
  const beforeKeys = new Set(Object.keys(before));
@@ -7594,7 +7866,7 @@ function diffStringArrays(before, after) {
7594
7866
  removed: before.filter((item) => !afterSet.has(item)).sort()
7595
7867
  };
7596
7868
  }
7597
- function recordValue2(value) {
7869
+ function recordValue3(value) {
7598
7870
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
7599
7871
  }
7600
7872
  function stringArrayValue(value) {
@@ -7610,7 +7882,7 @@ function pickRecord(input, keys) {
7610
7882
  }
7611
7883
  return out;
7612
7884
  }
7613
- function pruneUndefined(input) {
7885
+ function pruneUndefined2(input) {
7614
7886
  for (const key of Object.keys(input)) {
7615
7887
  if (input[key] === void 0) delete input[key];
7616
7888
  }
@@ -7620,7 +7892,7 @@ function truncateString(value, max) {
7620
7892
  if (!value) return void 0;
7621
7893
  return value.length > max ? `${value.slice(0, max)}...` : value;
7622
7894
  }
7623
- function stringValue2(value) {
7895
+ function stringValue3(value) {
7624
7896
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
7625
7897
  }
7626
7898
  function safePathSegment(value) {
@@ -7632,6 +7904,7 @@ var init_runLog = __esm({
7632
7904
  "src/goal/runLog.ts"() {
7633
7905
  "use strict";
7634
7906
  init_stateRepo();
7907
+ init_runIndex();
7635
7908
  init_state2();
7636
7909
  LOGS_KEY = "__goalRunLogs";
7637
7910
  LOG_RUN_KEY = "__goalRunLogRunId";
@@ -8824,7 +9097,7 @@ var init_goalCapabilityScheduling = __esm({
8824
9097
 
8825
9098
  // src/scripts/advanceManagedGoal.ts
8826
9099
  function stageManagedGoalDecision(data, goalId, goal, goalState, decision, details) {
8827
- if (decision.kind === "dispatch") {
9100
+ if (decision.kind === "dispatch" || decision.kind === "dispatchWorkflow") {
8828
9101
  stageGoalRunLogEvent(data, goalId, {
8829
9102
  source: "goal-manager",
8830
9103
  event: "goal.tick.dispatch",
@@ -8833,14 +9106,23 @@ function stageManagedGoalDecision(data, goalId, goal, goalState, decision, detai
8833
9106
  stage: decision.stage,
8834
9107
  evidence: decision.evidence,
8835
9108
  status: decision.kind,
8836
- dispatch: {
9109
+ dispatch: decision.kind === "dispatchWorkflow" ? {
9110
+ workflow: decision.workflow,
9111
+ cliArgs: decision.cliArgs
9112
+ } : {
8837
9113
  capability: decision.capability,
8838
9114
  cliArgs: decision.cliArgs,
8839
9115
  ...decision.executable ? { executable: decision.executable } : {}
8840
9116
  },
8841
9117
  goal: details.goalSnapshot,
8842
9118
  inspection: details.inspection,
8843
- decision: {
9119
+ decision: decision.kind === "dispatchWorkflow" ? {
9120
+ kind: decision.kind,
9121
+ evidence: decision.evidence,
9122
+ stage: decision.stage,
9123
+ workflow: decision.workflow,
9124
+ cliArgs: decision.cliArgs
9125
+ } : {
8844
9126
  kind: decision.kind,
8845
9127
  evidence: decision.evidence,
8846
9128
  stage: decision.stage,
@@ -8923,7 +9205,7 @@ function previousDispatchWasTargetInstance(managed, previousScheduleState) {
8923
9205
  return previous.targetId === targetId || previous.targetId.startsWith(`${targetId}-`);
8924
9206
  }
8925
9207
  function ensureIssueFactIfNeeded(goal, goalId, cwd) {
8926
- if (!routeNeedsIssueFact(goal)) return;
9208
+ if (!routeNeedsIssueFact(goal) && !workflowNeedsIssueFact(goal)) return;
8927
9209
  const existing = normalizeIssueNumber(goal.facts.issue);
8928
9210
  if (existing !== null) {
8929
9211
  goal.facts.issue = existing;
@@ -8940,6 +9222,14 @@ function routeNeedsIssueFact(goal) {
8940
9222
  })
8941
9223
  );
8942
9224
  }
9225
+ function workflowNeedsIssueFact(goal) {
9226
+ return Object.values(goal.workflowRef?.args ?? {}).some(isIssueFactReference);
9227
+ }
9228
+ function isIssueFactReference(value) {
9229
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
9230
+ const record2 = value;
9231
+ return Object.keys(record2).length === 1 && record2.fact === "issue";
9232
+ }
8943
9233
  function normalizeIssueNumber(value) {
8944
9234
  if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
8945
9235
  if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
@@ -9202,6 +9492,16 @@ var init_advanceManagedGoal = __esm({
9202
9492
  ctx.output.reason = decision.kind === "done" ? "managed goal complete" : decision.reason;
9203
9493
  return;
9204
9494
  }
9495
+ if (decision.kind === "dispatchWorkflow") {
9496
+ ctx.output.nextDispatch = {
9497
+ workflow: decision.workflow,
9498
+ cliArgs: decision.cliArgs,
9499
+ ...decision.saveReport === true ? { saveReport: true } : {},
9500
+ resultTarget: { type: "goal", id: goal.id }
9501
+ };
9502
+ ctx.output.reason = `dispatch workflow ${decision.workflow} for ${decision.evidence}`;
9503
+ return;
9504
+ }
9205
9505
  ctx.output.nextDispatch = {
9206
9506
  capability: decision.capability,
9207
9507
  cliArgs: decision.cliArgs,
@@ -14180,7 +14480,7 @@ var init_keys = __esm({
14180
14480
  // src/stateRepoGithub.ts
14181
14481
  import * as fs38 from "fs";
14182
14482
  import * as path36 from "path";
14183
- function recordValue3(value) {
14483
+ function recordValue4(value) {
14184
14484
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
14185
14485
  }
14186
14486
  function contentsUrl(owner, repo, filePath) {
@@ -14236,12 +14536,12 @@ async function loadGithubStateConfig(opts) {
14236
14536
  throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
14237
14537
  }
14238
14538
  }
14239
- const githubRaw = recordValue3(raw.github) ?? {};
14539
+ const githubRaw = recordValue4(raw.github) ?? {};
14240
14540
  const github = {
14241
14541
  owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
14242
14542
  repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
14243
14543
  };
14244
- const nestedState = recordValue3(raw.state) ?? {};
14544
+ const nestedState = recordValue4(raw.state) ?? {};
14245
14545
  const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
14246
14546
  const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
14247
14547
  const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
@@ -19040,8 +19340,10 @@ function jobReferenceBlock(profileName, profile, data) {
19040
19340
  }
19041
19341
  async function runExecutable(profileName, input) {
19042
19342
  const stageStartedAt = Date.now();
19343
+ let finishRunIndex = null;
19043
19344
  emitEvent(input.cwd, { executable: profileName, kind: "stage_start" });
19044
19345
  const finishAndEnd = (out) => {
19346
+ finishRunIndex?.(out);
19045
19347
  emitEvent(input.cwd, {
19046
19348
  executable: profileName,
19047
19349
  kind: "stage_end",
@@ -19126,6 +19428,41 @@ async function runExecutable(profileName, input) {
19126
19428
  data: { ...input.preloadedData ?? {} },
19127
19429
  output: { exitCode: 0 }
19128
19430
  };
19431
+ ctx.data.jobModel = modelSpec;
19432
+ ctx.data.jobModelProvider = model.provider;
19433
+ ctx.data.jobModelName = model.model;
19434
+ if (reasoningEffort) ctx.data.jobReasoningEffort = reasoningEffort;
19435
+ const runIndexStartedAt = new Date(stageStartedAt).toISOString();
19436
+ if (!input.skipConfig) {
19437
+ upsertRunIndexRowBestEffort(
19438
+ config,
19439
+ input.cwd,
19440
+ runIndexRowFromJobContext({
19441
+ data: ctx.data,
19442
+ profileName,
19443
+ profile,
19444
+ status: "running",
19445
+ startedAt: runIndexStartedAt,
19446
+ updatedAt: runIndexStartedAt
19447
+ })
19448
+ );
19449
+ finishRunIndex = (out) => {
19450
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
19451
+ upsertRunIndexRowBestEffort(
19452
+ config,
19453
+ input.cwd,
19454
+ runIndexRowFromJobContext({
19455
+ data: ctx.data,
19456
+ profileName,
19457
+ profile,
19458
+ status: statusFromExitCode(out.exitCode),
19459
+ startedAt: runIndexStartedAt,
19460
+ updatedAt: finishedAt,
19461
+ reason: out.reason
19462
+ })
19463
+ );
19464
+ };
19465
+ }
19129
19466
  const taskTarget = args.issue ?? args.pr;
19130
19467
  const taskArtifacts = typeof taskTarget === "number" && Number.isFinite(taskTarget) ? (() => {
19131
19468
  const taskType = args.issue ? "issue" : "pr";
@@ -19820,6 +20157,7 @@ var init_executor = __esm({
19820
20157
  init_litellm();
19821
20158
  init_profile();
19822
20159
  init_registry();
20160
+ init_runIndex();
19823
20161
  init_runtimePaths();
19824
20162
  init_scripts();
19825
20163
  init_stateWorkspace();
@@ -20095,6 +20433,10 @@ function shouldRunCapabilityWorkflow(job, workflow, capabilityIdentity, selected
20095
20433
  async function runCapabilityWorkflow(parent, workflow, capability, base) {
20096
20434
  let chainData = {
20097
20435
  ...base.preloadedData ?? {},
20436
+ runSubjectType: "workflow",
20437
+ runSubjectId: capability.slug,
20438
+ runSubjectLabel: capability.title,
20439
+ runSubjectWorkflow: capability.slug,
20098
20440
  workflowCapability: capability.slug,
20099
20441
  workflowTitle: capability.title,
20100
20442
  workflowStepCount: workflow.steps.length,
@@ -22655,6 +22997,10 @@ function emitSse(res, event) {
22655
22997
 
22656
22998
  `);
22657
22999
  }
23000
+ function openSseStream(res, chatId) {
23001
+ writeSseHeaders(res);
23002
+ emitSse(res, { type: "chat", chatId });
23003
+ }
22658
23004
  function envGithubToken() {
22659
23005
  return (process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT ?? "").trim();
22660
23006
  }
@@ -22664,16 +23010,6 @@ function parseRepoSlug(repo) {
22664
23010
  if (!owner || !name) return null;
22665
23011
  return { owner, repo: name };
22666
23012
  }
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
23013
  function translateChatEvent(event, chatId) {
22678
23014
  switch (event.event) {
22679
23015
  case "chat.message": {
@@ -22727,9 +23063,10 @@ function enqueue(chatId, fn) {
22727
23063
  );
22728
23064
  return next;
22729
23065
  }
22730
- function streamToRes(res, dir, chatId, since) {
22731
- writeSseHeaders(res);
22732
- emitSse(res, { type: "chat", chatId });
23066
+ function streamToRes(res, dir, chatId, since, opts = {}) {
23067
+ if (!opts.opened) {
23068
+ openSseStream(res, chatId);
23069
+ }
22733
23070
  let maxSent = since;
22734
23071
  const unsubscribe = subscribe(
22735
23072
  dir,
@@ -22773,7 +23110,17 @@ async function handleChatTurn(req, res, chatId, opts) {
22773
23110
  const storeRepoUrl = strField(body, "storeRepoUrl");
22774
23111
  const storeRef = strField(body, "storeRef");
22775
23112
  const allowCrossRepo = boolField(body, "allowCrossRepo");
23113
+ const firstTurn = boolField(body, "firstTurn");
22776
23114
  const agentIdentity = agentIdentityField(body);
23115
+ openSseStream(res, chatId);
23116
+ if (repo) {
23117
+ emitSse(res, {
23118
+ type: "tool_use",
23119
+ chatId,
23120
+ name: "prepare_repo",
23121
+ input: { repo }
23122
+ });
23123
+ }
22777
23124
  let agentCwd;
22778
23125
  try {
22779
23126
  agentCwd = await ensureRepoCwd({
@@ -22784,7 +23131,12 @@ async function handleChatTurn(req, res, chatId, opts) {
22784
23131
  cloneRepo: opts.cloneRepo
22785
23132
  });
22786
23133
  } catch (err) {
22787
- streamStartupError(res, opts.cwd, chatId, err);
23134
+ emitSse(res, {
23135
+ type: "error",
23136
+ chatId,
23137
+ error: err instanceof Error ? err.message : String(err)
23138
+ });
23139
+ res.end();
22788
23140
  return;
22789
23141
  }
22790
23142
  const stateToken = repoToken || envGithubToken();
@@ -22807,7 +23159,7 @@ async function handleChatTurn(req, res, chatId, opts) {
22807
23159
  }
22808
23160
  const sessionFile = sessionFilePath(agentCwd, chatId);
22809
23161
  const brainEventsFile = brainEventsFilePath(agentCwd, chatId);
22810
- if (stateConfig && stateToken) {
23162
+ if (!firstTurn && stateConfig && stateToken) {
22811
23163
  try {
22812
23164
  await syncJsonlFileFromGithubState({
22813
23165
  config: stateConfig,
@@ -22900,7 +23252,7 @@ async function handleChatTurn(req, res, chatId, opts) {
22900
23252
  }
22901
23253
  }
22902
23254
  });
22903
- streamToRes(res, agentCwd, chatId, sinceFloor);
23255
+ streamToRes(res, agentCwd, chatId, sinceFloor, { opened: true });
22904
23256
  }
22905
23257
  function buildServer(opts) {
22906
23258
  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.324",
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",