@motir/cli 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2867 -413
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ var __export = (target, all) => {
9
9
  import { Command as Command2, Option } from "commander";
10
10
 
11
11
  // package.json
12
- var version = "0.10.0";
12
+ var version = "0.11.1";
13
13
 
14
14
  // src/version.ts
15
15
  var CLI_VERSION = version;
@@ -824,7 +824,7 @@ function displayTokenPrefix(token) {
824
824
 
825
825
  // src/api/operations.ts
826
826
  var API_MAJOR = 1;
827
- var GENERATED_AGAINST = "1.60.0";
827
+ var GENERATED_AGAINST = "1.61.0";
828
828
  var V1_OPERATIONS = {
829
829
  "appendDispatchRunEvents": {
830
830
  method: "POST",
@@ -33312,7 +33312,7 @@ function validate58(data, { instancePath = "", parentData, parentDataProperty, r
33312
33312
  }
33313
33313
  validate58.evaluated = { "props": { "totalComments": true, "totalChanges": true, "items": true, "nextCursor": true, "totalCount": true }, "dynamicProps": false, "dynamicItems": false };
33314
33314
  var operation_getWorkItemApprovalGate = validate59;
33315
- var schema97 = { "type": "object", "properties": { "workItemKey": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "workItemTitle": { "type": "string" }, "kind": { "type": "string", "enum": ["design_result", "decision_approval", "pull_request_approval", "pull_request_merge", "acceptance_result", "decision_choice", "decision_confirmation", "plan_approval", "agent_review"] }, "gate": { "anyOf": [{ "type": "object", "properties": { "id": { "type": "string" }, "kind": { "type": "string", "enum": ["design_result", "decision_approval", "pull_request_approval", "pull_request_merge", "acceptance_result", "decision_choice", "decision_confirmation", "plan_approval", "agent_review"] }, "state": { "type": "string", "enum": ["awaiting", "approved", "changes_requested", "superseded", "overturned", "declined"] }, "noteMd": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "decidedAt": { "anyOf": [{ "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" }] }, "decidedByLabel": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "decidedUnderAuthority": { "anyOf": [{ "type": "string", "enum": ["assignee", "reporter", "admin", "github_review", "plan_permission", "review_agent", "project_setting"] }, { "type": "null" }] }, "decisionSource": { "anyOf": [{ "type": "string", "enum": ["ui", "api", "mcp", "github", "system"] }, { "type": "null" }] }, "subjectVersion": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "supersededCause": { "anyOf": [{ "type": "string", "enum": ["republished", "withdrawn", "head_moved", "member_closed", "member_drafted", "conflict", "set_changed", "pulled_back", "ci_failed", "ci_rerunning", "unknown", "plan_stale", "plan_discarded", "queue_failed", "review_agent_disabled"] }, { "type": "null" }] }, "outcomeRef": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "refusalVerdict": { "anyOf": [{ "type": "string", "enum": ["revise", "re_plan"] }, { "type": "null" }] }, "offersRefusalVerdict": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": ["id", "kind", "state", "noteMd", "decidedAt", "decidedByLabel", "decidedUnderAuthority", "decisionSource", "subjectVersion", "supersededCause", "outcomeRef", "refusalVerdict", "offersRefusalVerdict", "createdAt", "updatedAt"] }, { "type": "null" }] }, "routedToLabel": { "anyOf": [{ "type": "string" }, { "type": "null" }] } }, "required": ["workItemKey", "workItemTitle", "kind", "gate", "routedToLabel"], "$id": "motir://v1/operations/getWorkItemApprovalGate" };
33315
+ var schema97 = { "type": "object", "properties": { "workItemKey": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "workItemTitle": { "type": "string" }, "kind": { "type": "string", "enum": ["design_result", "decision_approval", "pull_request_approval", "pull_request_merge", "acceptance_result", "decision_choice", "decision_confirmation", "plan_approval", "agent_review"] }, "gate": { "anyOf": [{ "type": "object", "properties": { "id": { "type": "string" }, "kind": { "type": "string", "enum": ["design_result", "decision_approval", "pull_request_approval", "pull_request_merge", "acceptance_result", "decision_choice", "decision_confirmation", "plan_approval", "agent_review"] }, "state": { "type": "string", "enum": ["awaiting", "approved", "changes_requested", "superseded", "overturned", "declined"] }, "noteMd": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "decidedAt": { "anyOf": [{ "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" }] }, "decidedByLabel": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "decidedUnderAuthority": { "anyOf": [{ "type": "string", "enum": ["assignee", "reporter", "admin", "github_review", "plan_permission", "review_agent", "project_setting"] }, { "type": "null" }] }, "decisionSource": { "anyOf": [{ "type": "string", "enum": ["ui", "api", "mcp", "github", "system"] }, { "type": "null" }] }, "subjectVersion": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "supersededCause": { "anyOf": [{ "type": "string", "enum": ["republished", "withdrawn", "head_moved", "member_closed", "member_drafted", "conflict", "set_changed", "pulled_back", "ci_failed", "ci_rerunning", "unknown", "plan_stale", "plan_discarded", "queue_failed", "review_agent_disabled", "subject_gone"] }, { "type": "null" }] }, "outcomeRef": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "refusalVerdict": { "anyOf": [{ "type": "string", "enum": ["revise", "re_plan"] }, { "type": "null" }] }, "offersRefusalVerdict": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "updatedAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": ["id", "kind", "state", "noteMd", "decidedAt", "decidedByLabel", "decidedUnderAuthority", "decisionSource", "subjectVersion", "supersededCause", "outcomeRef", "refusalVerdict", "offersRefusalVerdict", "createdAt", "updatedAt"] }, { "type": "null" }] }, "routedToLabel": { "anyOf": [{ "type": "string" }, { "type": "null" }] } }, "required": ["workItemKey", "workItemTitle", "kind", "gate", "routedToLabel"], "$id": "motir://v1/operations/getWorkItemApprovalGate" };
33316
33316
  function validate59(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
33317
33317
  ;
33318
33318
  let vErrors = null;
@@ -33822,7 +33822,7 @@ function validate59(data, { instancePath = "", parentData, parentDataProperty, r
33822
33822
  }
33823
33823
  errors++;
33824
33824
  }
33825
- if (!(data13 === "republished" || data13 === "withdrawn" || data13 === "head_moved" || data13 === "member_closed" || data13 === "member_drafted" || data13 === "conflict" || data13 === "set_changed" || data13 === "pulled_back" || data13 === "ci_failed" || data13 === "ci_rerunning" || data13 === "unknown" || data13 === "plan_stale" || data13 === "plan_discarded" || data13 === "queue_failed" || data13 === "review_agent_disabled")) {
33825
+ if (!(data13 === "republished" || data13 === "withdrawn" || data13 === "head_moved" || data13 === "member_closed" || data13 === "member_drafted" || data13 === "conflict" || data13 === "set_changed" || data13 === "pulled_back" || data13 === "ci_failed" || data13 === "ci_rerunning" || data13 === "unknown" || data13 === "plan_stale" || data13 === "plan_discarded" || data13 === "queue_failed" || data13 === "review_agent_disabled" || data13 === "subject_gone")) {
33826
33826
  const err28 = { instancePath: instancePath + "/gate/supersededCause", schemaPath: "#/properties/gate/anyOf/0/properties/supersededCause/anyOf/0/enum", keyword: "enum", params: { allowedValues: schema97.properties.gate.anyOf[0].properties.supersededCause.anyOf[0].enum }, message: "must be equal to one of the allowed values" };
33827
33827
  if (vErrors === null) {
33828
33828
  vErrors = [err28];
@@ -34670,7 +34670,7 @@ function validate60(data, { instancePath = "", parentData, parentDataProperty, r
34670
34670
  }
34671
34671
  validate60.evaluated = { "props": { "verdict": true, "designCardKey": true, "designCardTitle": true, "design": true, "reason": true }, "dynamicProps": false, "dynamicItems": false };
34672
34672
  var operation_getWorkItemDispatchPrompt = validate61;
34673
- var schema101 = { "type": "object", "properties": { "key": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "prompt": { "type": "string" }, "parentKey": { "anyOf": [{ "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, { "type": "null" }] }, "targetRepo": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "targetRepoCloneUrl": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "targetRepoDefaultBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "targetRepos": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "cloneUrl": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "defaultBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "delivery": { "anyOf": [{ "type": "string", "enum": ["delivered", "awaiting", "unknown", "unestablished", "excluded"] }, { "type": "null" }] } }, "required": ["name", "cloneUrl", "defaultBranch", "delivery"] } }, "workflowMode": { "type": "string", "enum": ["per_item_pr", "session_lineage"] }, "sessionBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "workBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "branch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "advisories": { "type": "array", "items": { "anyOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "criterionIndex": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "phrase": { "type": "string" }, "path": { "type": "string" }, "repo": { "type": "string" }, "reason": { "type": "string" } }, "required": ["kind", "item", "severity", "criterionIndex"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "threshold": { "type": "string" }, "storyPoints": { "anyOf": [{ "type": "number" }, { "type": "null" }] }, "estimateMinutes": { "anyOf": [{ "type": "number" }, { "type": "null" }] } }, "required": ["kind", "item", "severity", "threshold", "storyPoints", "estimateMinutes"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "designCriterionIndex": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "surfaceCriterionIndex": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["kind", "item", "severity", "designCriterionIndex", "surfaceCriterionIndex"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "bodyEdit": { "type": "object", "properties": { "at": { "type": "string" }, "fields": { "type": "array", "items": { "type": "string" } } }, "required": ["at", "fields"] }, "fieldMove": { "type": "object", "properties": { "at": { "type": "string" }, "fields": { "type": "array", "items": { "type": "string" } } }, "required": ["at", "fields"] } }, "required": ["kind", "item", "severity", "bodyEdit", "fieldMove"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "claim": { "type": "string" }, "claimedCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "blockerCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["kind", "item", "severity", "claim", "claimedCount", "blockerCount"] }, { "anyOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "subsumption" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "path": { "type": "string" }, "pullRequest": { "type": "string" }, "pullRequestTitle": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "state": { "type": "string", "const": "merged" }, "mergedAt": { "type": "string" } }, "required": ["kind", "item", "severity", "path", "pullRequest", "pullRequestTitle", "state", "mergedAt"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "subsumption" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "path": { "type": "string" }, "pullRequest": { "type": "string" }, "pullRequestTitle": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "state": { "type": "string", "const": "open" }, "mergedAt": { "type": "null" } }, "required": ["kind", "item", "severity", "path", "pullRequest", "pullRequestTitle", "state", "mergedAt"] }] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "reference" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "referenced": { "type": "string" }, "referencedStatus": { "type": "string" }, "severity": { "type": "string" } }, "required": ["item", "referenced", "referencedStatus", "severity"] }] } } }, "required": ["key", "prompt", "parentKey", "targetRepo", "targetRepoCloneUrl", "targetRepoDefaultBranch", "targetRepos", "workflowMode", "sessionBranch", "advisories"], "$id": "motir://v1/components/DispatchPrompt" };
34673
+ var schema101 = { "type": "object", "properties": { "key": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "prompt": { "type": "string" }, "parentKey": { "anyOf": [{ "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, { "type": "null" }] }, "targetRepo": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "targetRepoCloneUrl": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "targetRepoDefaultBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "targetRepos": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "cloneUrl": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "defaultBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "delivery": { "anyOf": [{ "type": "string", "enum": ["delivered", "awaiting", "unknown", "unestablished", "excluded", "delivered_without_change_request"] }, { "type": "null" }] } }, "required": ["name", "cloneUrl", "defaultBranch", "delivery"] } }, "workflowMode": { "type": "string", "enum": ["per_item_pr", "session_lineage"] }, "sessionBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "workBranch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "branch": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "advisories": { "type": "array", "items": { "anyOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "criterionIndex": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "phrase": { "type": "string" }, "path": { "type": "string" }, "repo": { "type": "string" }, "reason": { "type": "string" } }, "required": ["kind", "item", "severity", "criterionIndex"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "threshold": { "type": "string" }, "storyPoints": { "anyOf": [{ "type": "number" }, { "type": "null" }] }, "estimateMinutes": { "anyOf": [{ "type": "number" }, { "type": "null" }] } }, "required": ["kind", "item", "severity", "threshold", "storyPoints", "estimateMinutes"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "designCriterionIndex": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 }, "surfaceCriterionIndex": { "type": "integer", "minimum": -9007199254740991, "maximum": 9007199254740991 } }, "required": ["kind", "item", "severity", "designCriterionIndex", "surfaceCriterionIndex"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "bodyEdit": { "type": "object", "properties": { "at": { "type": "string" }, "fields": { "type": "array", "items": { "type": "string" } } }, "required": ["at", "fields"] }, "fieldMove": { "type": "object", "properties": { "at": { "type": "string" }, "fields": { "type": "array", "items": { "type": "string" } } }, "required": ["at", "fields"] } }, "required": ["kind", "item", "severity", "bodyEdit", "fieldMove"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "shape" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "claim": { "type": "string" }, "claimedCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "blockerCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["kind", "item", "severity", "claim", "claimedCount", "blockerCount"] }, { "anyOf": [{ "type": "object", "properties": { "kind": { "type": "string", "const": "subsumption" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "path": { "type": "string" }, "pullRequest": { "type": "string" }, "pullRequestTitle": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "state": { "type": "string", "const": "merged" }, "mergedAt": { "type": "string" } }, "required": ["kind", "item", "severity", "path", "pullRequest", "pullRequestTitle", "state", "mergedAt"] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "subsumption" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "severity": { "type": "string" }, "path": { "type": "string" }, "pullRequest": { "type": "string" }, "pullRequestTitle": { "anyOf": [{ "type": "string" }, { "type": "null" }] }, "state": { "type": "string", "const": "open" }, "mergedAt": { "type": "null" } }, "required": ["kind", "item", "severity", "path", "pullRequest", "pullRequestTitle", "state", "mergedAt"] }] }, { "type": "object", "properties": { "kind": { "type": "string", "const": "reference" }, "item": { "type": "string", "pattern": "^[A-Z][A-Z0-9]*-\\d+$" }, "referenced": { "type": "string" }, "referencedStatus": { "type": "string" }, "severity": { "type": "string" } }, "required": ["item", "referenced", "referencedStatus", "severity"] }] } } }, "required": ["key", "prompt", "parentKey", "targetRepo", "targetRepoCloneUrl", "targetRepoDefaultBranch", "targetRepos", "workflowMode", "sessionBranch", "advisories"], "$id": "motir://v1/components/DispatchPrompt" };
34674
34674
  function validate61(data, { instancePath = "", parentData, parentDataProperty, rootData = data, dynamicAnchors = {} } = {}) {
34675
34675
  ;
34676
34676
  let vErrors = null;
@@ -35099,7 +35099,7 @@ function validate61(data, { instancePath = "", parentData, parentDataProperty, r
35099
35099
  }
35100
35100
  errors++;
35101
35101
  }
35102
- if (!(data11 === "delivered" || data11 === "awaiting" || data11 === "unknown" || data11 === "unestablished" || data11 === "excluded")) {
35102
+ if (!(data11 === "delivered" || data11 === "awaiting" || data11 === "unknown" || data11 === "unestablished" || data11 === "excluded" || data11 === "delivered_without_change_request")) {
35103
35103
  const err20 = { instancePath: instancePath + "/targetRepos/" + i0 + "/delivery", schemaPath: "motir://v1/components/DispatchPrompt/properties/targetRepos/items/properties/delivery/anyOf/0/enum", keyword: "enum", params: { allowedValues: schema101.properties.targetRepos.items.properties.delivery.anyOf[0].enum }, message: "must be equal to one of the allowed values" };
35104
35104
  if (vErrors === null) {
35105
35105
  vErrors = [err20];
@@ -55659,25 +55659,25 @@ function isInteractive() {
55659
55659
  function promptLine(question, defaultValue) {
55660
55660
  const suffix = defaultValue ? ` [${defaultValue}]` : "";
55661
55661
  const rl = createInterface({ input: process.stdin, output: process.stderr });
55662
- return new Promise((resolve6) => {
55662
+ return new Promise((resolve8) => {
55663
55663
  rl.question(`${question}${suffix}: `, (answer) => {
55664
55664
  rl.close();
55665
55665
  const trimmed = answer.trim();
55666
- resolve6(trimmed.length > 0 ? trimmed : defaultValue ?? "");
55666
+ resolve8(trimmed.length > 0 ? trimmed : defaultValue ?? "");
55667
55667
  });
55668
55668
  });
55669
55669
  }
55670
55670
  function promptLineOrNull(question) {
55671
55671
  const rl = createInterface({ input: process.stdin, output: process.stderr });
55672
- return new Promise((resolve6) => {
55672
+ return new Promise((resolve8) => {
55673
55673
  let answered = false;
55674
55674
  rl.question(`${question}`, (answer) => {
55675
55675
  answered = true;
55676
55676
  rl.close();
55677
- resolve6(answer);
55677
+ resolve8(answer);
55678
55678
  });
55679
55679
  rl.on("close", () => {
55680
- if (!answered) resolve6(null);
55680
+ if (!answered) resolve8(null);
55681
55681
  });
55682
55682
  });
55683
55683
  }
@@ -55686,11 +55686,11 @@ function promptSecret(question) {
55686
55686
  rl._writeToOutput = () => {
55687
55687
  };
55688
55688
  process.stderr.write(`${question}: `);
55689
- return new Promise((resolve6) => {
55689
+ return new Promise((resolve8) => {
55690
55690
  rl.question("", (answer) => {
55691
55691
  rl.close();
55692
55692
  process.stderr.write("\n");
55693
- resolve6(answer.trim());
55693
+ resolve8(answer.trim());
55694
55694
  });
55695
55695
  });
55696
55696
  }
@@ -55849,14 +55849,14 @@ function openUrl(url, opts = {}) {
55849
55849
  const env = opts.env ?? process.env;
55850
55850
  if (!hasDisplay(env, platform)) return Promise.resolve(false);
55851
55851
  const { cmd, args } = launcher(platform);
55852
- return new Promise((resolve6) => {
55852
+ return new Promise((resolve8) => {
55853
55853
  try {
55854
55854
  const child = spawn(cmd, [...args, url], { stdio: "ignore", detached: true });
55855
- child.on("error", () => resolve6(false));
55855
+ child.on("error", () => resolve8(false));
55856
55856
  child.unref();
55857
- setTimeout(() => resolve6(true), 0);
55857
+ setTimeout(() => resolve8(true), 0);
55858
55858
  } catch {
55859
- resolve6(false);
55859
+ resolve8(false);
55860
55860
  }
55861
55861
  });
55862
55862
  }
@@ -56102,11 +56102,11 @@ async function awaitApproval(input) {
56102
56102
  }
56103
56103
  async function loginCommand(opts, deps = {}) {
56104
56104
  const sleep = deps.sleep ?? delay;
56105
- const open = deps.openUrl ?? openUrl;
56105
+ const open2 = deps.openUrl ?? openUrl;
56106
56106
  const serverUrl = resolveServerUrl(opts.server);
56107
56107
  const grant = await startDeviceGrant({ serverUrl, hostname: hostname() });
56108
56108
  printGrant(grant);
56109
- const opened = opts.browser === false ? false : await open(grant.verification_uri_complete);
56109
+ const opened = opts.browser === false ? false : await open2(grant.verification_uri_complete);
56110
56110
  if (opened) info("Opened your browser \u2014 approve the request there.");
56111
56111
  else info("Open that URL on any device, sign in, and enter the code.");
56112
56112
  info("Waiting for approval\u2026");
@@ -56779,16 +56779,16 @@ function renderSprintsTable(sprints, nameWidth = 40) {
56779
56779
  ${formatTable(SPRINTS_HEADERS, rows, { rightAlign: [3, 4] })}`;
56780
56780
  }
56781
56781
  function renderSprintHeader(sprint) {
56782
- const lines = [`${sprint.name} [${sprint.state}]`];
56782
+ const lines2 = [`${sprint.name} [${sprint.state}]`];
56783
56783
  const window = formatSprintWindow(sprint);
56784
- if (window) lines.push(window);
56785
- if (sprint.goal) lines.push(`goal: ${sprint.goal}`);
56784
+ if (window) lines2.push(window);
56785
+ if (sprint.goal) lines2.push(`goal: ${sprint.goal}`);
56786
56786
  if (sprint.committedIssueCount !== null || sprint.committedPoints !== null) {
56787
56787
  const issues = sprint.committedIssueCount === null ? NONE : String(sprint.committedIssueCount);
56788
56788
  const points = sprint.committedPoints === null ? NONE : String(sprint.committedPoints);
56789
- lines.push(`committed: ${issues} issues \xB7 ${points} points`);
56789
+ lines2.push(`committed: ${issues} issues \xB7 ${points} points`);
56790
56790
  }
56791
- return lines.join("\n");
56791
+ return lines2.join("\n");
56792
56792
  }
56793
56793
  var SPRINT_ITEM_HEADERS = ["KEY", "KIND", "STATUS", "PRIORITY", "TITLE"];
56794
56794
  var SPRINT_ITEM_EDGE_HEADERS = [
@@ -56850,25 +56850,25 @@ function prefixMatch(lower) {
56850
56850
  return (sprint) => sprint.name.toLowerCase().startsWith(lower);
56851
56851
  }
56852
56852
  function renderStatusBlock(pulse) {
56853
- const lines = [
56853
+ const lines2 = [
56854
56854
  `Project: ${pulse.projectKey}`,
56855
56855
  `Ready: ${pulse.readyCount}`,
56856
56856
  `In flight: ${pulse.inFlightCount} (in progress + in review)`
56857
56857
  ];
56858
56858
  if (pulse.activeSprint) {
56859
56859
  const s = pulse.activeSprint;
56860
- lines.push(
56860
+ lines2.push(
56861
56861
  `Sprint: ${s.name} [active, ${s.issueCount} issue${s.issueCount === 1 ? "" : "s"}]`
56862
56862
  );
56863
56863
  const window = formatSprintWindow(s);
56864
- if (window) lines.push(` ${window}`);
56865
- if (s.goal) lines.push(` goal: ${s.goal}`);
56864
+ if (window) lines2.push(` ${window}`);
56865
+ if (s.goal) lines2.push(` goal: ${s.goal}`);
56866
56866
  } else if (pulse.totalSprints === 0) {
56867
- lines.push("Sprint: (no sprints)");
56867
+ lines2.push("Sprint: (no sprints)");
56868
56868
  } else {
56869
- lines.push("Sprint: (none active)");
56869
+ lines2.push("Sprint: (none active)");
56870
56870
  }
56871
- return lines.join("\n");
56871
+ return lines2.join("\n");
56872
56872
  }
56873
56873
  var CHILD_HEADERS = ["KEY", "KIND", "STATUS", "TITLE"];
56874
56874
  var EDGE_HEADERS = ["KEY", "STATUS", "TITLE"];
@@ -56953,18 +56953,18 @@ function renderChildrenSection(children, titleWidth = 60) {
56953
56953
  }
56954
56954
  const waves = assignChildWaves(children);
56955
56955
  const rows = waveChildRows(waves, titleWidth);
56956
- const lines = [
56956
+ const lines2 = [
56957
56957
  renderRelationTable("CHILDREN", rows, "no children", WAVE_CHILD_HEADERS, "build order")
56958
56958
  ];
56959
56959
  const legend = markLegend(rows.map((row) => row[4] ?? ""));
56960
- if (legend) lines.push(legend);
56960
+ if (legend) lines2.push(legend);
56961
56961
  const cycle = cycleMembers(waves);
56962
56962
  if (cycle.length > 0) {
56963
- lines.push(
56963
+ lines2.push(
56964
56964
  `\u26A0 dependency CYCLE \u2014 ${cycle.join(", ")} block each other and have no build order. Fix the blocked_by edges.`
56965
56965
  );
56966
56966
  }
56967
- return lines.join("\n");
56967
+ return lines2.join("\n");
56968
56968
  }
56969
56969
  function renderItemHeader(item) {
56970
56970
  const kindType = item.type ? `${item.kind}/${item.type}` : item.kind;
@@ -57282,7 +57282,7 @@ function writeHostedGitConfig(stateDir2, issued, cli) {
57282
57282
  const identityDir = join4(stateDir2, "identity");
57283
57283
  mkdirSync2(identityDir, { recursive: true, mode: 448 });
57284
57284
  const helper = `!${[...cli, "git-credential", "--state", stateDir2].map(shellQuote).join(" ")}`;
57285
- const lines = [
57285
+ const lines2 = [
57286
57286
  "# Written by `motir` for a hosted run (MOTIR-6559). Git reaches GitHub only",
57287
57287
  "# through the helper below, and commits as the App that writes each repository.",
57288
57288
  "[user]",
@@ -57306,14 +57306,14 @@ function writeHostedGitConfig(stateDir2, issued, cli) {
57306
57306
  { mode: 384 }
57307
57307
  );
57308
57308
  for (const suffix of ["", ".git"]) {
57309
- lines.push(
57309
+ lines2.push(
57310
57310
  `[includeIf "hasconfig:remote.*.url:https://${GITHUB_HOST}/${entry.repository}${suffix}"]`,
57311
57311
  ` path = ${gitValue(identity)}`
57312
57312
  );
57313
57313
  }
57314
57314
  });
57315
57315
  const path = join4(stateDir2, GITCONFIG_FILE);
57316
- writePrivate(path, `${lines.join("\n")}
57316
+ writePrivate(path, `${lines2.join("\n")}
57317
57317
  `);
57318
57318
  return path;
57319
57319
  }
@@ -58637,22 +58637,22 @@ function plural2(n, one, many) {
58637
58637
  }
58638
58638
  function renderDoctorReport(report) {
58639
58639
  const labelWidth = Math.max(...report.checks.map((c) => c.label.length), 0);
58640
- const lines = ["motir doctor \u2014 BYOK preflight", ""];
58640
+ const lines2 = ["motir doctor \u2014 BYOK preflight", ""];
58641
58641
  for (const check of report.checks) {
58642
- lines.push(
58642
+ lines2.push(
58643
58643
  `${check.status.toUpperCase().padEnd(4)} ${check.label.padEnd(labelWidth)} ${check.detail}`
58644
58644
  );
58645
- if (check.remediation) lines.push(`${" ".repeat(6 + labelWidth + 2)}\u21B3 ${check.remediation}`);
58645
+ if (check.remediation) lines2.push(`${" ".repeat(6 + labelWidth + 2)}\u21B3 ${check.remediation}`);
58646
58646
  }
58647
58647
  const counts = countByStatus(report.checks);
58648
58648
  const parts = STATUS_ORDER.filter((s) => counts[s] > 0).map(
58649
58649
  (s) => s === "fail" ? plural2(counts.fail, "failed", "failed") : s === "warn" ? plural2(counts.warn, "warning", "warnings") : plural2(counts.pass, "passed", "passed")
58650
58650
  );
58651
- lines.push(
58651
+ lines2.push(
58652
58652
  "",
58653
58653
  report.ok ? `All hard checks passed \u2014 ${parts.join(", ")}.` : `${parts.join(", ")}.`
58654
58654
  );
58655
- return lines.join("\n");
58655
+ return lines2.join("\n");
58656
58656
  }
58657
58657
 
58658
58658
  // src/commands/doctor.ts
@@ -58907,7 +58907,7 @@ async function runAgent(opts) {
58907
58907
  }
58908
58908
  }
58909
58909
  try {
58910
- const result = await new Promise((resolve6, reject) => {
58910
+ const result = await new Promise((resolve8, reject) => {
58911
58911
  const tee = opts.onOutput;
58912
58912
  const promptOnStdin = opts.command.promptOnStdin !== false;
58913
58913
  const args = [...opts.command.args, ...opts.command.promptArgs?.(prompt, promptFile) ?? []];
@@ -58966,7 +58966,7 @@ async function runAgent(opts) {
58966
58966
  stdin.end(prompt);
58967
58967
  }
58968
58968
  child.on("close", (code, signal) => {
58969
- resolve6({ exitCode: signal ? 1 : code ?? 1, signal });
58969
+ resolve8({ exitCode: signal ? 1 : code ?? 1, signal });
58970
58970
  });
58971
58971
  });
58972
58972
  return { ...result, model: readAgentReport(reportFile) };
@@ -59004,18 +59004,25 @@ var HOSTED_AGENT_ENV_KEYS = [
59004
59004
  var MAX_ARGV_PROMPT_BYTES2 = 100 * 1024;
59005
59005
  var EGRESS_DOCUMENT = {
59006
59006
  $schema: "https://opencode.ai/config.json",
59007
- enabled_providers: ["anthropic"],
59007
+ enabled_providers: ["anthropic", "deepseek"],
59008
59008
  provider: {
59009
59009
  anthropic: {
59010
59010
  options: {
59011
59011
  baseURL: "{env:MOTIR_GATEWAY_URL}/v1",
59012
59012
  apiKey: "{env:MOTIR_RUN_KEY}"
59013
59013
  }
59014
+ },
59015
+ deepseek: {
59016
+ options: {
59017
+ baseURL: "{env:MOTIR_GATEWAY_URL}/v1",
59018
+ apiKey: "{env:MOTIR_RUN_KEY}"
59019
+ }
59014
59020
  }
59015
59021
  },
59016
59022
  share: "disabled",
59017
59023
  autoupdate: false
59018
59024
  };
59025
+ var HOSTED_MODEL_PROVIDERS = EGRESS_DOCUMENT.enabled_providers;
59019
59026
  function egressConfig() {
59020
59027
  return JSON.stringify(EGRESS_DOCUMENT);
59021
59028
  }
@@ -59044,8 +59051,13 @@ function hostedOpenCodeAgent(env = process.env, opts = {}) {
59044
59051
  hint: "The hosted image is booted with the run model, the gateway URL and the run key."
59045
59052
  });
59046
59053
  }
59047
- if (!/^anthropic\/[A-Za-z0-9._-]+$/.test(model)) {
59048
- throw new CliError(`${HOSTED_MODEL_ENV} must be "anthropic/<model id>", got "${model}".`);
59054
+ const slash = model.indexOf("/");
59055
+ const provider = slash > 0 ? model.slice(0, slash) : "";
59056
+ const id = slash > 0 ? model.slice(slash + 1) : "";
59057
+ if (!HOSTED_MODEL_PROVIDERS.includes(provider) || !/^[A-Za-z0-9._-]+$/.test(id)) {
59058
+ throw new CliError(
59059
+ `${HOSTED_MODEL_ENV} must be "<provider>/<model id>" with provider one of ${HOSTED_MODEL_PROVIDERS.join(", ")}, got "${model}".`
59060
+ );
59049
59061
  }
59050
59062
  const args = ["run", "--model", model, "--auto"];
59051
59063
  return {
@@ -59250,17 +59262,17 @@ function materializeDispatchCheckouts(rootDir, targets, opts = {}) {
59250
59262
  };
59251
59263
  }
59252
59264
  function renderMaterialization(result) {
59253
- const lines = [];
59254
- for (const repo of result.cloned) lines.push(`Cloned: ${repo}`);
59265
+ const lines2 = [];
59266
+ for (const repo of result.cloned) lines2.push(`Cloned: ${repo}`);
59255
59267
  for (const failure2 of result.failures) {
59256
- lines.push(`Blocked: ${failure2.repo} \u2014 ${failure2.detail}`);
59257
- if (failure2.gitMessage) lines.push(` git said: ${failure2.gitMessage}`);
59268
+ lines2.push(`Blocked: ${failure2.repo} \u2014 ${failure2.detail}`);
59269
+ if (failure2.gitMessage) lines2.push(` git said: ${failure2.gitMessage}`);
59258
59270
  }
59259
59271
  if (result.failures.length > 0) {
59260
- lines.push(" No agent was started: the prompt it would be handed opens with");
59261
- lines.push(" `git worktree add`, which cannot run without the checkout.");
59272
+ lines2.push(" No agent was started: the prompt it would be handed opens with");
59273
+ lines2.push(" `git worktree add`, which cannot run without the checkout.");
59262
59274
  }
59263
- return lines;
59275
+ return lines2;
59264
59276
  }
59265
59277
  function checkBootstrapCheckout(target, opts = {}) {
59266
59278
  const exists = opts.exists ?? existsSync7;
@@ -59307,9 +59319,9 @@ function renderDispatchAdvisories(dispatch) {
59307
59319
  const shapes = advisories.filter(isOrderingAdvisory);
59308
59320
  const straddles = advisories.filter(isRepoStraddleAdvisory);
59309
59321
  const subsumed = advisories.filter(isSubsumptionAdvisory);
59310
- const lines = [];
59322
+ const lines2 = [];
59311
59323
  if (references.length > 0) {
59312
- lines.push(
59324
+ lines2.push(
59313
59325
  `Advisory: ${dispatch.key}'s acceptance criteria name work items it has no blocked_by edge to.`,
59314
59326
  ...references.map((a) => ` - ${a.referenced} (${a.referencedStatus})`),
59315
59327
  " This is NOT a blocker \u2014 the dispatch proceeds. Before branching,",
@@ -59318,7 +59330,7 @@ function renderDispatchAdvisories(dispatch) {
59318
59330
  );
59319
59331
  }
59320
59332
  for (const s of shapes) {
59321
- lines.push(
59333
+ lines2.push(
59322
59334
  `Advisory: ${dispatch.key}'s acceptance criterion ${s.criterionIndex} says "${s.phrase}" \u2014`,
59323
59335
  " state that exists only after this work item's own PR has merged.",
59324
59336
  " This is NOT a blocker \u2014 the dispatch proceeds. Your boundary ends at",
@@ -59327,7 +59339,7 @@ function renderDispatchAdvisories(dispatch) {
59327
59339
  );
59328
59340
  }
59329
59341
  for (const s of straddles) {
59330
- lines.push(
59342
+ lines2.push(
59331
59343
  `Advisory: ${dispatch.key}'s acceptance criterion ${s.criterionIndex} names ${s.path},`,
59332
59344
  ` which lives in ${s.repo}${s.reason === "contradiction" ? " \u2014 not this work item's pinned repo." : ", and this item pins no repo while its criteria name more than one."}`,
59333
59345
  " This is NOT a blocker \u2014 the dispatch proceeds. One subtask, one repo,",
@@ -59337,7 +59349,7 @@ function renderDispatchAdvisories(dispatch) {
59337
59349
  );
59338
59350
  }
59339
59351
  for (const s of subsumed) {
59340
- lines.push(
59352
+ lines2.push(
59341
59353
  `Advisory: ${dispatch.key}'s body names ${s.path}, which ${s.pullRequest} already`,
59342
59354
  ` changed (merged ${s.mergedAt}).`,
59343
59355
  " This is NOT a blocker \u2014 the dispatch proceeds. But this work item may already",
@@ -59346,7 +59358,7 @@ function renderDispatchAdvisories(dispatch) {
59346
59358
  " of rebuilding it. If the two merely share a file, proceed."
59347
59359
  );
59348
59360
  }
59349
- return lines.length === 0 ? null : lines.join("\n");
59361
+ return lines2.length === 0 ? null : lines2.join("\n");
59350
59362
  }
59351
59363
  function deliveryLabel(state) {
59352
59364
  switch (state) {
@@ -59362,6 +59374,8 @@ function deliveryLabel(state) {
59362
59374
  return "NOT ESTABLISHED \u2014 this repository does not exist yet, so there is nothing to open a pull request against";
59363
59375
  case "excluded":
59364
59376
  return "excluded \u2014 the project is deliberately code-less here; it does not hold this work item";
59377
+ case "delivered_without_change_request":
59378
+ return "delivered without a pull request \u2014 its cards finished without one (a tag, a release, a setting); it does not hold this work item";
59365
59379
  default:
59366
59380
  return state;
59367
59381
  }
@@ -59372,8 +59386,9 @@ function repoSet(dispatch) {
59372
59386
  }
59373
59387
  function renderResumeNotice(dispatch) {
59374
59388
  const repos = repoSet(dispatch);
59375
- const delivered = repos.filter((r) => r.delivery === "delivered").map((r) => r.name);
59376
- const remaining = repos.filter((r) => r.delivery !== "delivered").map((r) => r.name);
59389
+ const done = (d) => d === "delivered" || d === "delivered_without_change_request";
59390
+ const delivered = repos.filter((r) => done(r.delivery)).map((r) => r.name);
59391
+ const remaining = repos.filter((r) => !done(r.delivery)).map((r) => r.name);
59377
59392
  if (delivered.length === 0 || remaining.length === 0) return null;
59378
59393
  return [
59379
59394
  `Resume: ${dispatch.key} is PARTIALLY DELIVERED \u2014 this is not a fresh work item.`,
@@ -59384,25 +59399,25 @@ function renderResumeNotice(dispatch) {
59384
59399
  }
59385
59400
  function renderRepositoriesBlock(targets, delivery) {
59386
59401
  if (targets.length <= 1) return [];
59387
- const lines = [`Repos: ${targets.length} \u2014 this item ships in every one of them:`];
59402
+ const lines2 = [`Repos: ${targets.length} \u2014 this item ships in every one of them:`];
59388
59403
  targets.forEach((t, i) => {
59389
59404
  const where = i === 0 ? "the working directory" : "a sibling checkout";
59390
- lines.push(` - ${t.targetRepo ?? "(unpinned)"} (${where})`);
59391
- lines.push(` ${t.repoPath ?? t.cwd} (${t.repoSource ?? "root"})`);
59405
+ lines2.push(` - ${t.targetRepo ?? "(unpinned)"} (${where})`);
59406
+ lines2.push(` ${t.repoPath ?? t.cwd} (${t.repoSource ?? "root"})`);
59392
59407
  const label2 = deliveryLabel(delivery?.[i] ?? null);
59393
- if (label2) lines.push(` ${label2}`);
59408
+ if (label2) lines2.push(` ${label2}`);
59394
59409
  if (t.reason === "bootstrap_root") {
59395
- lines.push(
59410
+ lines2.push(
59396
59411
  ` \u26A0 no checkout here yet. This is NOT a blocker \u2014 the dispatch`,
59397
59412
  ` proceeds. If it lives elsewhere: motir link add ${t.targetRepo ?? ""} <path>`
59398
59413
  );
59399
59414
  }
59400
59415
  });
59401
- return lines;
59416
+ return lines2;
59402
59417
  }
59403
59418
  function renderDispatchSummary(input) {
59404
59419
  const { key, title, dispatch, target, agent } = input;
59405
- const lines = [
59420
+ const lines2 = [
59406
59421
  `Dispatch: ${key}${title ? ` \u2014 ${title}` : ""}`,
59407
59422
  `Repo: ${dispatch.targetRepo ?? "not pinned (Motir cannot say)"}`,
59408
59423
  `Path: ${target.cwd}`,
@@ -59416,10 +59431,10 @@ function renderDispatchSummary(input) {
59416
59431
  ),
59417
59432
  `Workflow: ${workflowLabel(dispatch.workflowMode, dispatch.sessionBranch)}`
59418
59433
  ];
59419
- lines.push(
59434
+ lines2.push(
59420
59435
  agent ? `Agent: ${agent.command} (${agentSourceLabel(agent.source)})` : "Agent: none \u2014 printing the prompt (copy it into your agent)"
59421
59436
  );
59422
- return lines.join("\n");
59437
+ return lines2.join("\n");
59423
59438
  }
59424
59439
  function renderAgentSuccess(key, dispatch) {
59425
59440
  const repos = repoSet(dispatch);
@@ -59443,29 +59458,29 @@ function renderAgentSuccess(key, dispatch) {
59443
59458
  ].join("\n");
59444
59459
  }
59445
59460
  if (repos.length > 0) {
59446
- const lines = [
59461
+ const lines2 = [
59447
59462
  `${key}: agent finished \u2014 a pull request is expected in EACH of its ${repos.length} repositories:`
59448
59463
  ];
59449
59464
  for (const repo of repos) {
59450
59465
  const label2 = deliveryLabel(repo.delivery);
59451
- lines.push(` - ${repo.name}${label2 ? ` \u2014 ${label2}` : ""}`);
59466
+ lines2.push(` - ${repo.name}${label2 ? ` \u2014 ${label2}` : ""}`);
59452
59467
  }
59453
59468
  const blocked = repos.filter((r) => r.delivery === "unestablished");
59454
59469
  if (blocked.length > 0) {
59455
- lines.push(
59470
+ lines2.push(
59456
59471
  "",
59457
59472
  `\u26A0 ${blocked.map((r) => r.name).join(", ")} cannot be delivered yet: the repository does`,
59458
59473
  " not exist. Establish it on the project before this work item can complete."
59459
59474
  );
59460
59475
  }
59461
- lines.push(
59476
+ lines2.push(
59462
59477
  "",
59463
59478
  "CI decides when it becomes reviewable: the item moves to In Review on its own",
59464
59479
  "when the checks go green.",
59465
59480
  `Next: review + merge every one of them. ${key} completes only when EVERY`,
59466
59481
  "repository's pull request has merged \u2014 a single merge leaves it held."
59467
59482
  );
59468
- return lines.join("\n");
59483
+ return lines2.join("\n");
59469
59484
  }
59470
59485
  return [
59471
59486
  `${key}: agent finished \u2014 its pull request is open (now Implemented).`,
@@ -59485,18 +59500,18 @@ function renderNothingPushed(key, dispatch) {
59485
59500
  ].join("\n");
59486
59501
  }
59487
59502
  function renderAgentFailure(key, exitCode, dispatch) {
59488
- const lines = [
59503
+ const lines2 = [
59489
59504
  `${key}: agent exited ${exitCode}. The item stays In Progress (nothing was reverted).`,
59490
59505
  `It is excluded from the next \`motir next\` in this session; re-run it with \`motir run ${key}\`.`
59491
59506
  ];
59492
59507
  const repos = dispatch ? repoSet(dispatch) : [];
59493
59508
  if (repos.length > 0) {
59494
- lines.push(
59509
+ lines2.push(
59495
59510
  `${key} ships in ${repos.length} repositories (${repos.map((r) => r.name).join(", ")}),`,
59496
59511
  "so partial work may be sitting in more than one checkout."
59497
59512
  );
59498
59513
  }
59499
- return lines.join("\n");
59514
+ return lines2.join("\n");
59500
59515
  }
59501
59516
  function renderClaimRefusal(claim) {
59502
59517
  const where = `It is at ${claim.status.key}.`;
@@ -60033,273 +60048,294 @@ async function watchAndFixCi(input) {
60033
60048
  function renderFixPrompt(input) {
60034
60049
  const bases = [...new Set(input.failing.map((d) => d.baseRef ?? d.defaultBranch))];
60035
60050
  const mergeTarget = bases.length === 1 ? `origin/${bases[0]}` : "origin/<that pull request's base>";
60036
- const lines = [];
60037
- lines.push(`# Make the build pass \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`);
60038
- lines.push("");
60039
- lines.push(
60051
+ const lines2 = [];
60052
+ lines2.push(`# Make the build pass \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`);
60053
+ lines2.push("");
60054
+ lines2.push(
60040
60055
  `CI is RED on work that is already committed and pushed. This is attempt ${input.attempt} of ${CI_FIX_ATTEMPTS}.`
60041
60056
  );
60042
- lines.push("");
60043
- lines.push("## The failing pull requests");
60044
- lines.push("");
60057
+ lines2.push("");
60058
+ lines2.push("## The failing pull requests");
60059
+ lines2.push("");
60045
60060
  for (const d of input.failing) {
60046
- lines.push(`- **${d.repo}#${d.number}** (base \`${d.baseRef ?? d.defaultBranch}\`) \u2014 ${d.url}`);
60061
+ lines2.push(`- **${d.repo}#${d.number}** (base \`${d.baseRef ?? d.defaultBranch}\`) \u2014 ${d.url}`);
60047
60062
  }
60048
- lines.push("");
60063
+ lines2.push("");
60049
60064
  if (input.checkouts && input.checkouts.length > 0) {
60050
- lines.push("## Where each branch is checked out");
60051
- lines.push("");
60065
+ lines2.push("## Where each branch is checked out");
60066
+ lines2.push("");
60052
60067
  for (const c of input.checkouts) {
60053
- lines.push(`- **${c.repo}** \u2014 branch \`${c.branch}\` at \`${c.path}\``);
60068
+ lines2.push(`- **${c.repo}** \u2014 branch \`${c.branch}\` at \`${c.path}\``);
60054
60069
  }
60055
- lines.push("");
60056
- lines.push("Work in those checkouts, on those branches. Each push updates its");
60057
- lines.push("existing pull request.");
60058
- lines.push("");
60070
+ lines2.push("");
60071
+ lines2.push("Work in those checkouts, on those branches. Each push updates its");
60072
+ lines2.push("existing pull request.");
60073
+ lines2.push("");
60059
60074
  }
60060
60075
  const ejected = input.failing.filter((d) => d.queueExit);
60061
60076
  if (ejected.length > 0) {
60062
- lines.push("## Why it left the merge queue");
60063
- lines.push("");
60064
- lines.push("The merge queue tested these pull requests together with the work queued");
60065
- lines.push("ahead of them and threw them out. Their OWN checks may well be green \u2014 the");
60066
- lines.push("failure happened in the queue\u2019s merge commit, not on this branch alone.");
60067
- lines.push("");
60077
+ lines2.push("## Why it left the merge queue");
60078
+ lines2.push("");
60079
+ lines2.push("The merge queue tested these pull requests together with the work queued");
60080
+ lines2.push("ahead of them and threw them out. Their OWN checks may well be green \u2014 the");
60081
+ lines2.push("failure happened in the queue\u2019s merge commit, not on this branch alone.");
60082
+ lines2.push("");
60068
60083
  for (const d of ejected) {
60069
60084
  const exit = d.queueExit;
60070
60085
  const base = `origin/${d.baseRef ?? d.defaultBranch}`;
60071
- lines.push(`- **${d.repo}#${d.number}** \u2014 ${queueReasonInWords(exit.rawReason)}`);
60086
+ lines2.push(`- **${d.repo}#${d.number}** \u2014 ${queueReasonInWords(exit.rawReason)}`);
60072
60087
  if (exit.failingCheckName) {
60073
- lines.push(
60088
+ lines2.push(
60074
60089
  ` - The queue\u2019s failing check: **${exit.failingCheckName}**` + (exit.failingCheckUrl ? ` \u2014 ${exit.failingCheckUrl}` : "")
60075
60090
  );
60076
60091
  }
60077
60092
  if (exit.rawReason === "MERGE_CONFLICT") {
60078
- lines.push(` - **Merge \`${base}\` into the branch, resolve the conflicts, run the`);
60079
- lines.push(" checks the conflict touched, and push.** Only new commits clear a");
60080
- lines.push(" conflict; *Queue again* will fail the same way until it is resolved.");
60093
+ lines2.push(` - **Merge \`${base}\` into the branch, resolve the conflicts, run the`);
60094
+ lines2.push(" checks the conflict touched, and push.** Only new commits clear a");
60095
+ lines2.push(" conflict; *Queue again* will fail the same way until it is resolved.");
60081
60096
  } else if (exit.rawReason === "CI_FAILURE" || exit.rawReason === "CI_TIMEOUT") {
60082
- lines.push(` - **Reproduce the named check against the branch merged with \`${base}\`,**`);
60083
- lines.push(" fix what it shows, and push.");
60097
+ lines2.push(` - **Reproduce the named check against the branch merged with \`${base}\`,**`);
60098
+ lines2.push(" fix what it shows, and push.");
60084
60099
  }
60085
- lines.push(" - **If nothing needs changing** (the queue\u2019s failure was flaky, or is");
60086
- lines.push(" already fixed on the base), STILL PUSH: the merge of the base from step 1,");
60087
- lines.push(" or an empty commit if that merge brought nothing. *Queue again* is not");
60088
- lines.push(" offered after a queue failure, so only a new head whose checks go green");
60089
- lines.push(" asks for approval again. Say in your summary that nothing needed changing.");
60100
+ lines2.push(" - **If nothing needs changing** (the queue\u2019s failure was flaky, or is");
60101
+ lines2.push(" already fixed on the base), STILL PUSH: the merge of the base from step 1,");
60102
+ lines2.push(" or an empty commit if that merge brought nothing. *Queue again* is not");
60103
+ lines2.push(" offered after a queue failure, so only a new head whose checks go green");
60104
+ lines2.push(" asks for approval again. Say in your summary that nothing needed changing.");
60090
60105
  }
60091
- lines.push("");
60092
- }
60093
- lines.push("## What to do");
60094
- lines.push("");
60095
- lines.push("1. **MERGE THE LATEST BASE BRANCH FIRST \u2014 before you read anything as a");
60096
- lines.push(` diagnosis.** \`git fetch origin && git merge ${mergeTarget}\` in the`);
60097
- lines.push(" checkout holding the branch. A MERGE, never a rebase.");
60098
- lines.push(" **CI judges your branch MERGED with the branch it targets**, so the");
60099
- lines.push(" failure you are about to read happened in a tree this checkout does");
60100
- lines.push(" not have: the file the log names may be one that landed on the base");
60101
- lines.push(" after this branch started, and the failure may be inherited rather");
60102
- lines.push(" than yours. Build the combination CI judged, then diagnose against");
60103
- lines.push(" it.");
60104
- lines.push(" - **Push the merge like any other iteration.** It re-triggers CI, so");
60105
- lines.push(" if the red was inherited and is already repaired on the base, the");
60106
- lines.push(" merge alone turns the build green and nothing else is owed \u2014");
60107
- lines.push(" change nothing and say so.");
60108
- lines.push(" - **A merge CONFLICT is your own work, and no check can report it.**");
60109
- lines.push(" Resolve it here, in this iteration.");
60110
- lines.push("2. **Read the ACTUAL failure first** \u2014 on the MERGED tree: the assertion,");
60111
- lines.push(" the locator, the stack, the job log. Not the summary line, and not a");
60112
- lines.push(" guess from the test name.");
60113
- lines.push("3. **Decide what kind of failure it is.**");
60114
- lines.push(" - **Caused by this branch** \u2192 fix it. Commit to the SAME branch and");
60115
- lines.push(" push; the existing pull request picks it up. Open no new pull");
60116
- lines.push(" request and link nothing \u2014 this is repair work on a pull request");
60117
- lines.push(" that already exists.");
60118
- lines.push(" **THE PUSH IS THE VERIFICATION.** This loop re-triggers CI on");
60119
- lines.push(" every push, so the next verdict IS your confirmation \u2014 you do");
60120
- lines.push(" not have to produce one. You may run AT MOST the single file");
60121
- lines.push(" the log named, and only because it is seconds. NEVER the suite,");
60122
- lines.push(" never a coverage run, and never a wider sweep to check that");
60123
- lines.push(" nothing else broke: that is the same measurement taken twice,");
60124
- lines.push(" the second time slower, on a shared machine, and NOT merged with");
60125
- lines.push(" the base as CI will merge it at verdict time \u2014 so it is the");
60126
- lines.push(" less trustworthy copy.");
60127
- lines.push(" - **INHERITED** \u2014 the base branch is red on its own, or the log names");
60128
- lines.push(" a file your diff never touched. Say so and change NOTHING here:");
60129
- lines.push(" step 1 was the whole fix if there was one, and repairing somebody");
60130
- lines.push(" else's red belongs in its own pull request, never folded into this");
60131
- lines.push(" one.");
60132
- lines.push(" - **ENVIRONMENTAL** \u2014 a connection reset, a runner or webServer death,");
60133
- lines.push(" a first-hit cold-compile timeout, a different test failing each run \u2014");
60134
- lines.push(" then **say so and change NOTHING**. Pushing a speculative patch to");
60135
- lines.push(" chase a flake makes the diff worse and hides the real signal.");
60136
- lines.push("4. **Keep How to test true.** How to test belongs to the work item, not");
60137
- lines.push(" to a commit. If a commit you push changes anything it describes \u2014 a");
60138
- lines.push(" precondition, a setup, migrate or seed command, a click-path step \u2014");
60139
- lines.push(` publish the whole corrected text with the ${HOW_TO_TEST_TOOL_NAME}`);
60140
- lines.push(` tool, on ${input.key} or on the item the pull request body's`);
60141
- lines.push(' "## How to test" section says it was written onto, and update that');
60142
- lines.push(" section of the body to match. The body shows you the current text. A");
60143
- lines.push(" fix that changes none of them \u2014 the usual case \u2014 publishes nothing.");
60144
- lines.push("5. Do not move the work item: leave its status, and record no");
60145
- lines.push(" transition. The build decides when it moves.");
60146
- lines.push("");
60147
- lines.push("Changing nothing is a legitimate outcome and is sometimes the right one.");
60148
- return lines.join("\n");
60106
+ lines2.push("");
60107
+ }
60108
+ lines2.push("## What to do");
60109
+ lines2.push("");
60110
+ lines2.push("1. **MERGE THE LATEST BASE BRANCH FIRST \u2014 before you read anything as a");
60111
+ lines2.push(` diagnosis.** \`git fetch origin && git merge ${mergeTarget}\` in the`);
60112
+ lines2.push(" checkout holding the branch. A MERGE, never a rebase.");
60113
+ lines2.push(" **CI judges your branch MERGED with the branch it targets**, so the");
60114
+ lines2.push(" failure you are about to read happened in a tree this checkout does");
60115
+ lines2.push(" not have: the file the log names may be one that landed on the base");
60116
+ lines2.push(" after this branch started, and the failure may be inherited rather");
60117
+ lines2.push(" than yours. Build the combination CI judged, then diagnose against");
60118
+ lines2.push(" it.");
60119
+ lines2.push(" - **Push the merge like any other iteration.** It re-triggers CI, so");
60120
+ lines2.push(" if the red was inherited and is already repaired on the base, the");
60121
+ lines2.push(" merge alone turns the build green and nothing else is owed \u2014");
60122
+ lines2.push(" change nothing and say so.");
60123
+ lines2.push(" - **A merge CONFLICT is your own work, and no check can report it.**");
60124
+ lines2.push(" Resolve it here, in this iteration.");
60125
+ lines2.push("2. **Read the ACTUAL failure first** \u2014 on the MERGED tree: the assertion,");
60126
+ lines2.push(" the locator, the stack, the job log. Not the summary line, and not a");
60127
+ lines2.push(" guess from the test name.");
60128
+ lines2.push("3. **Decide what kind of failure it is.**");
60129
+ lines2.push(" - **Caused by this branch** \u2192 fix it. Commit to the SAME branch and");
60130
+ lines2.push(" push; the existing pull request picks it up. Open no new pull");
60131
+ lines2.push(" request and link nothing \u2014 this is repair work on a pull request");
60132
+ lines2.push(" that already exists.");
60133
+ lines2.push(" **THE PUSH IS THE VERIFICATION.** This loop re-triggers CI on");
60134
+ lines2.push(" every push, so the next verdict IS your confirmation \u2014 you do");
60135
+ lines2.push(" not have to produce one. You may run AT MOST the single file");
60136
+ lines2.push(" the log named, and only because it is seconds. NEVER the suite,");
60137
+ lines2.push(" never a coverage run, and never a wider sweep to check that");
60138
+ lines2.push(" nothing else broke: that is the same measurement taken twice,");
60139
+ lines2.push(" the second time slower, on a shared machine, and NOT merged with");
60140
+ lines2.push(" the base as CI will merge it at verdict time \u2014 so it is the");
60141
+ lines2.push(" less trustworthy copy.");
60142
+ lines2.push(" - **INHERITED** \u2014 the base branch is red on its own, or the log names");
60143
+ lines2.push(" a file your diff never touched. Say so and change NOTHING here:");
60144
+ lines2.push(" step 1 was the whole fix if there was one, and repairing somebody");
60145
+ lines2.push(" else's red belongs in its own pull request, never folded into this");
60146
+ lines2.push(" one.");
60147
+ lines2.push(" - **ENVIRONMENTAL** \u2014 a connection reset, a runner or webServer death,");
60148
+ lines2.push(" a first-hit cold-compile timeout, a different test failing each run \u2014");
60149
+ lines2.push(" then **say so and change NOTHING**. Pushing a speculative patch to");
60150
+ lines2.push(" chase a flake makes the diff worse and hides the real signal.");
60151
+ lines2.push("4. **Keep How to test true.** How to test belongs to the work item, not");
60152
+ lines2.push(" to a commit. If a commit you push changes anything it describes \u2014 a");
60153
+ lines2.push(" precondition, a setup, migrate or seed command, a click-path step \u2014");
60154
+ lines2.push(` publish the whole corrected text with the ${HOW_TO_TEST_TOOL_NAME}`);
60155
+ lines2.push(` tool, on ${input.key} or on the item the pull request body's`);
60156
+ lines2.push(' "## How to test" section says it was written onto, and update that');
60157
+ lines2.push(" section of the body to match. The body shows you the current text. A");
60158
+ lines2.push(" fix that changes none of them \u2014 the usual case \u2014 publishes nothing.");
60159
+ lines2.push("5. Do not move the work item: leave its status, and record no");
60160
+ lines2.push(" transition. The build decides when it moves.");
60161
+ lines2.push("");
60162
+ lines2.push("Changing nothing is a legitimate outcome and is sometimes the right one.");
60163
+ return lines2.join("\n");
60149
60164
  }
60150
60165
  function renderRefusalQuote(refusal) {
60151
60166
  const who = refusal.decidedByLabel ?? "The reviewer";
60152
60167
  const reason = refusal.reasonMd?.trim();
60153
- const lines = [`${who} sent it back on ${refusal.decidedAt}, choosing **Re-run**:`, ""];
60168
+ const lines2 = [`${who} sent it back on ${refusal.decidedAt}, choosing **Re-run**:`, ""];
60154
60169
  if (reason) {
60155
- for (const line of reason.split("\n")) lines.push(`> ${line}`);
60170
+ for (const line of reason.split("\n")) lines2.push(`> ${line}`);
60156
60171
  } else {
60157
- lines.push("> (no reason was recorded)");
60172
+ lines2.push("> (no reason was recorded)");
60158
60173
  }
60159
- return lines;
60174
+ return lines2;
60160
60175
  }
60161
60176
  function renderCheckoutList(checkouts) {
60162
- const lines = ["## Where each branch is checked out", ""];
60177
+ const lines2 = ["## Where each branch is checked out", ""];
60163
60178
  for (const c of checkouts)
60164
- lines.push(`- **${c.repo}** \u2014 branch \`${c.branch}\` at \`${c.path}\``);
60165
- lines.push("");
60166
- lines.push("Work in those checkouts, on those branches. Each push updates its");
60167
- lines.push("existing pull request.");
60168
- return lines;
60179
+ lines2.push(`- **${c.repo}** \u2014 branch \`${c.branch}\` at \`${c.path}\``);
60180
+ lines2.push("");
60181
+ lines2.push("Work in those checkouts, on those branches. Each push updates its");
60182
+ lines2.push("existing pull request.");
60183
+ return lines2;
60169
60184
  }
60170
60185
  function renderAcceptanceRerunPrompt(input) {
60171
- const lines = [];
60172
- lines.push(
60186
+ const lines2 = [];
60187
+ lines2.push(
60173
60188
  `# Answer the acceptance review \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`
60174
60189
  );
60175
- lines.push("");
60176
- lines.push("This story was run as a whole and its pull requests are open. A person watched");
60177
- lines.push("its acceptance video and asked for a FIX to what was built.");
60178
- lines.push("");
60179
- lines.push("## What they said");
60180
- lines.push("");
60181
- lines.push(...renderRefusalQuote(input.refusal));
60182
- lines.push("");
60183
- lines.push("## The pull requests");
60184
- lines.push("");
60185
- for (const pr of input.pullRequests) lines.push(`- **${pr.repo}#${pr.number}** \u2014 ${pr.url}`);
60186
- lines.push("");
60187
- lines.push(...renderCheckoutList(input.checkouts));
60188
- lines.push("");
60189
- lines.push("## The scope");
60190
- lines.push("");
60191
- lines.push("A fix to the delivered work on these branches: what the reviewer saw and named.");
60192
- lines.push("**Anything structural is a Re-plan, not a Re-run** \u2014 a new screen, a different");
60193
- lines.push("flow, work another card owns, a change to what the story is for. If the reason");
60194
- lines.push("asks for that, make NO commit, stop, and say so in one paragraph: the reviewer");
60195
- lines.push("re-plans the story in Motir instead.");
60196
- lines.push("");
60197
- lines.push("## What to do");
60198
- lines.push("");
60199
- lines.push("1. **Merge each pull request\u2019s base branch first** (a merge, never a rebase),");
60200
- lines.push(" so you change the tree CI will judge.");
60201
- lines.push("2. **Find where the reviewer\u2019s complaint lives** \u2014 render or run the surface");
60202
- lines.push(" they watched rather than reasoning from the code, and change the smallest thing");
60203
- lines.push(" that answers it.");
60204
- lines.push("3. **Keep the tests true.** Update or add the tests that cover what you changed.");
60205
- lines.push(" Run ONLY the test files you touched, plus lint and typecheck \u2014 never the full");
60206
- lines.push(" suite: the push re-runs CI, and that is the verdict.");
60207
- lines.push("4. **Commit and push to the same branches.** Open no pull request, link nothing,");
60208
- lines.push(" and move no status \u2014 the board stays where it is.");
60209
- lines.push("5. **Do not record or publish the acceptance video.** Once CI is green on this");
60210
- lines.push(" push, you will be asked to re-record it in a separate step.");
60211
- lines.push("6. If the pull request body\u2019s `## How to test` no longer matches what a person");
60212
- lines.push(` does, publish it again with \`${HOW_TO_TEST_TOOL_NAME}\` on ${input.key}.`);
60213
- return lines.join("\n");
60214
- }
60190
+ lines2.push("");
60191
+ lines2.push("This story was run as a whole and its pull requests are open. A person watched");
60192
+ lines2.push("its acceptance video and asked for a FIX to what was built.");
60193
+ lines2.push("");
60194
+ lines2.push("## What they said");
60195
+ lines2.push("");
60196
+ lines2.push(...renderRefusalQuote(input.refusal));
60197
+ lines2.push("");
60198
+ lines2.push("## The pull requests");
60199
+ lines2.push("");
60200
+ for (const pr of input.pullRequests) lines2.push(`- **${pr.repo}#${pr.number}** \u2014 ${pr.url}`);
60201
+ lines2.push("");
60202
+ lines2.push(...renderCheckoutList(input.checkouts));
60203
+ lines2.push("");
60204
+ lines2.push("## The scope");
60205
+ lines2.push("");
60206
+ lines2.push("A fix to the delivered work on these branches: what the reviewer saw and named.");
60207
+ lines2.push("**Anything structural is a Re-plan, not a Re-run** \u2014 a new screen, a different");
60208
+ lines2.push("flow, work another card owns, a change to what the story is for. If the reason");
60209
+ lines2.push("asks for that, make NO commit, stop, and say so in one paragraph: the reviewer");
60210
+ lines2.push("re-plans the story in Motir instead.");
60211
+ lines2.push("");
60212
+ lines2.push("## What to do");
60213
+ lines2.push("");
60214
+ lines2.push("1. **Merge each pull request\u2019s base branch first** (a merge, never a rebase),");
60215
+ lines2.push(" so you change the tree CI will judge.");
60216
+ lines2.push("2. **Find where the reviewer\u2019s complaint lives** \u2014 render or run the surface");
60217
+ lines2.push(" they watched rather than reasoning from the code, and change the smallest thing");
60218
+ lines2.push(" that answers it.");
60219
+ lines2.push("3. **Keep the tests true.** Update or add the tests that cover what you changed.");
60220
+ lines2.push(" Run ONLY the test files you touched, plus lint and typecheck \u2014 never the full");
60221
+ lines2.push(" suite: the push re-runs CI, and that is the verdict.");
60222
+ lines2.push("4. **Commit and push to the same branches.** Open no pull request, link nothing,");
60223
+ lines2.push(" and move no status \u2014 the board stays where it is.");
60224
+ lines2.push("5. **Do not record or publish the acceptance video.** Once CI is green on this");
60225
+ lines2.push(" push, you will be asked to re-record it in a separate step.");
60226
+ lines2.push("6. If the pull request body\u2019s `## How to test` no longer matches what a person");
60227
+ lines2.push(` does, publish it again with \`${HOW_TO_TEST_TOOL_NAME}\` on ${input.key}.`);
60228
+ return lines2.join("\n");
60229
+ }
60230
+ var ACCEPTANCE_LANE_PUBLISHES_CHECK = "grep -rlE 'uses:\\s*\\./\\.github/actions/upload-acceptance-video' .github/workflows/";
60215
60231
  function renderAcceptanceRecordPrompt(input) {
60216
- const lines = [];
60217
- lines.push(
60232
+ const lines2 = [];
60233
+ lines2.push(
60218
60234
  `# Re-record the acceptance video \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`
60219
60235
  );
60220
- lines.push("");
60221
- lines.push("CI is green on the fix you pushed for this review:");
60222
- lines.push("");
60223
- lines.push(...renderRefusalQuote(input.refusal));
60224
- lines.push("");
60225
- lines.push(...renderCheckoutList(input.checkouts));
60226
- lines.push("");
60227
- lines.push("## What to do");
60228
- lines.push("");
60229
- lines.push("1. **Run the story's acceptance spec** (`tests/e2e/acceptance*.spec.ts`, the one");
60230
- lines.push(` that declares \`acceptanceStory('${input.key}')\`) on the acceptance lane, with`);
60231
- lines.push(" recording on, so the clip shows the fixed work.");
60232
- lines.push("2. **Publish it \u2014 two calls.** `create_acceptance_upload` with the card key mints a");
60233
- lines.push(
60236
+ lines2.push("");
60237
+ lines2.push("CI is green on the fix you pushed for this review:");
60238
+ lines2.push("");
60239
+ lines2.push(...renderRefusalQuote(input.refusal));
60240
+ lines2.push("");
60241
+ lines2.push(...renderCheckoutList(input.checkouts));
60242
+ lines2.push("");
60243
+ lines2.push("## What to do");
60244
+ lines2.push("");
60245
+ lines2.push("1. **Find out who publishes the receipt.** In the checkout that holds the");
60246
+ lines2.push(" acceptance spec, run:");
60247
+ lines2.push("");
60248
+ lines2.push(` ${ACCEPTANCE_LANE_PUBLISHES_CHECK}`);
60249
+ lines2.push("");
60250
+ lines2.push(" **If it prints a file**, that repository\u2019s acceptance lane publishes the");
60251
+ lines2.push(" receipt itself, from the green pull-request run on the commit you pushed. Make");
60252
+ lines2.push(
60253
+ " NO MCP publish. Run the story\u2019s acceptance spec only to confirm it is green,"
60254
+ );
60255
+ lines2.push(
60256
+ " then report: \u201Cthe repository\u2019s acceptance lane published the receipt on"
60257
+ );
60258
+ lines2.push(" CI\u2019s green run; no MCP publish made\u201D, naming that run (its");
60259
+ lines2.push(
60260
+ " `Publish the acceptance receipt` step logs the receipt id). Steps 2\u20134 do not"
60261
+ );
60262
+ lines2.push(" apply.");
60263
+ lines2.push("");
60264
+ lines2.push(" **If it prints nothing**, no lane publishes there, and steps 2\u20134 are yours.");
60265
+ lines2.push("2. **Run the story's acceptance spec** (`tests/e2e/acceptance*.spec.ts`, the one");
60266
+ lines2.push(` that declares \`acceptanceStory('${input.key}')\`) on the acceptance lane, with`);
60267
+ lines2.push(" recording on, so the clip shows the fixed work.");
60268
+ lines2.push("3. **Publish it \u2014 two calls.** `create_acceptance_upload` with the card key mints a");
60269
+ lines2.push(
60234
60270
  " presigned PUT; PUT the clip\u2019s bytes to it (`Content-Type: video/webm`); then"
60235
60271
  );
60236
- lines.push(" `publish_acceptance_result` with the pathname it returned, the chapters, the");
60237
- lines.push(" commit sha you pushed, and the card key.");
60238
- lines.push(
60239
- "3. **Report the receipt id the publish returned.** Without it nothing was published,"
60272
+ lines2.push(" `publish_acceptance_result` with the pathname it returned, the chapters, the");
60273
+ lines2.push(" commit sha you pushed, and the card key.");
60274
+ lines2.push(
60275
+ "4. **Report the receipt id the publish returned.** Without it nothing was published,"
60240
60276
  );
60241
- lines.push(" and the reviewer has nothing new to watch.");
60242
- lines.push("");
60243
- lines.push("Change no code in this step and move no status. If the spec goes red, publish");
60244
- lines.push("nothing and say what failed: a red run records nothing.");
60245
- return lines.join("\n");
60277
+ lines2.push(" and the reviewer has nothing new to watch.");
60278
+ lines2.push("");
60279
+ lines2.push("Change no code in this step and move no status. If the spec goes red, publish");
60280
+ lines2.push("nothing and say what failed: a red run records nothing.");
60281
+ return lines2.join("\n");
60246
60282
  }
60247
60283
  function renderReviewFixPrompt(input) {
60248
60284
  const { refusal } = input;
60249
60285
  const byAgent = refusal.gate === "agent_review";
60250
60286
  const who = refusal.reviewerName ?? (byAgent ? "The review agent" : "The reviewer");
60251
- const lines = [];
60252
- lines.push(`# Answer the code review \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`);
60253
- lines.push("");
60254
- lines.push("This card's pull requests are open and its run has ended. A review of them");
60255
- lines.push(
60287
+ const lines2 = [];
60288
+ lines2.push(`# Answer the code review \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`);
60289
+ lines2.push("");
60290
+ lines2.push("This card's pull requests are open and its run has ended. A review of them");
60291
+ lines2.push(
60256
60292
  byAgent ? "by the REVIEW AGENT, against the card, sent the card back with findings." : "by a PERSON, on the approve-and-merge gate, sent the card back \u2014 Request changes."
60257
60293
  );
60258
- lines.push("");
60259
- lines.push("## The findings");
60260
- lines.push("");
60261
- lines.push(`${who} sent it back on ${refusal.decidedAt}:`);
60262
- lines.push("");
60294
+ lines2.push("");
60295
+ lines2.push("## The findings");
60296
+ lines2.push("");
60297
+ lines2.push(`${who} sent it back on ${refusal.decidedAt}:`);
60298
+ lines2.push("");
60263
60299
  const findings = refusal.findingsMd?.trim();
60264
60300
  if (findings) {
60265
- for (const line of findings.split("\n")) lines.push(`> ${line}`);
60301
+ for (const line of findings.split("\n")) lines2.push(`> ${line}`);
60266
60302
  } else {
60267
- lines.push("> (no findings were recorded)");
60268
- }
60269
- lines.push("");
60270
- lines.push("## The pull requests");
60271
- lines.push("");
60272
- for (const pr of input.pullRequests) lines.push(`- **${pr.repo}#${pr.number}** \u2014 ${pr.url}`);
60273
- lines.push("");
60274
- lines.push(...renderCheckoutList(input.checkouts));
60275
- lines.push("");
60276
- lines.push("## The scope");
60277
- lines.push("");
60278
- lines.push("A fix to the delivered work on these branches: what the review named, measured");
60279
- lines.push("against the card. **A finding that asks for a change to what the card is FOR** \u2014");
60280
- lines.push("work another card owns, a new surface, a different design \u2014 is not yours to");
60281
- lines.push("build: leave it, and say so in one paragraph naming the finding.");
60282
- lines.push("");
60283
- lines.push("## What to do");
60284
- lines.push("");
60285
- lines.push("1. **Merge each pull request\u2019s base branch first** (a merge, never a rebase),");
60286
- lines.push(" so you change the tree CI will judge.");
60287
- lines.push("2. **Address EVERY finding**, one by one \u2014 read the code it names, confirm it, and");
60288
- lines.push(" change the smallest thing that answers it. A finding you judge wrong is answered");
60289
- lines.push(" in words, with the evidence, never by silently skipping it.");
60290
- lines.push("3. **Keep the tests true.** Update or add the tests that cover what you changed.");
60291
- lines.push(" Run ONLY the test files you touched, plus lint and typecheck \u2014 never the full");
60292
- lines.push(" suite: the push re-runs CI, and that is the verdict.");
60293
- lines.push("4. **Commit and push to the same branches.** Open no pull request, link nothing,");
60294
- lines.push(" decide no approval and move no status \u2014 the push withdraws the review, and the");
60295
- lines.push(" next green version is reviewed again.");
60296
- lines.push("5. If the pull request body\u2019s `## How to test` no longer matches what a person");
60297
- lines.push(` does, publish it again with \`${HOW_TO_TEST_TOOL_NAME}\` on ${input.key}.`);
60298
- return lines.join("\n");
60303
+ lines2.push("> (no findings were recorded)");
60304
+ }
60305
+ lines2.push("");
60306
+ lines2.push("## The pull requests");
60307
+ lines2.push("");
60308
+ for (const pr of input.pullRequests) lines2.push(`- **${pr.repo}#${pr.number}** \u2014 ${pr.url}`);
60309
+ lines2.push("");
60310
+ lines2.push(...renderCheckoutList(input.checkouts));
60311
+ lines2.push("");
60312
+ lines2.push("## The scope");
60313
+ lines2.push("");
60314
+ lines2.push("A fix to the delivered work on these branches: what the review named, measured");
60315
+ lines2.push("against the card. **A finding that asks for a change to what the card is FOR** \u2014");
60316
+ lines2.push("work another card owns, a new surface, a different design \u2014 is not yours to");
60317
+ lines2.push("build: leave it, and say so in one paragraph naming the finding.");
60318
+ lines2.push("");
60319
+ lines2.push("## What to do");
60320
+ lines2.push("");
60321
+ lines2.push("1. **Merge each pull request\u2019s base branch first** (a merge, never a rebase),");
60322
+ lines2.push(" so you change the tree CI will judge.");
60323
+ lines2.push("2. **Address EVERY finding**, one by one \u2014 read the code it names, confirm it, and");
60324
+ lines2.push(" change the smallest thing that answers it. A finding you judge wrong is answered");
60325
+ lines2.push(" in words, with the evidence, never by silently skipping it.");
60326
+ lines2.push("3. **Keep the tests true.** Update or add the tests that cover what you changed.");
60327
+ lines2.push(" Run ONLY the test files you touched, plus lint and typecheck \u2014 never the full");
60328
+ lines2.push(" suite: the push re-runs CI, and that is the verdict.");
60329
+ lines2.push("4. **Commit and push to the same branches.** Open no pull request, link nothing,");
60330
+ lines2.push(" decide no approval and move no status \u2014 the push withdraws the review, and the");
60331
+ lines2.push(" next green version is reviewed again.");
60332
+ lines2.push("5. If the pull request body\u2019s `## How to test` no longer matches what a person");
60333
+ lines2.push(` does, publish it again with \`${HOW_TO_TEST_TOOL_NAME}\` on ${input.key}.`);
60334
+ return lines2.join("\n");
60299
60335
  }
60300
60336
  var POLL_INTERVAL_MS = 2e4;
60301
- var realWait = () => new Promise((resolve6) => {
60302
- setTimeout(resolve6, POLL_INTERVAL_MS);
60337
+ var realWait = () => new Promise((resolve8) => {
60338
+ setTimeout(resolve8, POLL_INTERVAL_MS);
60303
60339
  });
60304
60340
  async function runCiWatchPhase(input) {
60305
60341
  const runAgentFn = input.runAgentFn ?? runAgent;
@@ -60495,7 +60531,7 @@ function fitTitle(title) {
60495
60531
  function renderSessionPrBody(runId, branch, records, commits = [], howToTest) {
60496
60532
  const carried = records.filter(landedWork);
60497
60533
  const failed = records.filter((r) => r.outcome === "failed");
60498
- const lines = [
60534
+ const lines2 = [
60499
60535
  `Unattended \`motir auto\` run \`${runId}\`, integrated on \`${branch}\`.`,
60500
60536
  "",
60501
60537
  `## Work items carried (${carried.length})`,
@@ -60503,25 +60539,25 @@ function renderSessionPrBody(runId, branch, records, commits = [], howToTest) {
60503
60539
  ...carried.map((r) => `- ${r.key} \u2014 ${r.title ?? "(untitled)"}`)
60504
60540
  ];
60505
60541
  if (commits.length > 0) {
60506
- lines.push("", `## What the commits say (${commits.length})`, "");
60542
+ lines2.push("", `## What the commits say (${commits.length})`, "");
60507
60543
  for (const [index, commit] of commits.entries()) {
60508
- if (index > 0) lines.push("");
60509
- lines.push(`### ${commit.subject}`);
60544
+ if (index > 0) lines2.push("");
60545
+ lines2.push(`### ${commit.subject}`);
60510
60546
  const fallback = carried[index]?.title;
60511
60547
  const beneath = commit.body || (fallback ? `_${fallback}_` : "");
60512
- if (beneath) lines.push("", beneath);
60548
+ if (beneath) lines2.push("", beneath);
60513
60549
  }
60514
60550
  }
60515
- if (howToTest) lines.push("", ...renderHowToTestSection(howToTest, branch));
60551
+ if (howToTest) lines2.push("", ...renderHowToTestSection(howToTest, branch));
60516
60552
  if (failed.length > 0) {
60517
- lines.push(
60553
+ lines2.push(
60518
60554
  "",
60519
60555
  `## Attempted and failed (${failed.length}) \u2014 still In Progress, not in this branch`,
60520
60556
  "",
60521
60557
  ...failed.map((r) => `- ${r.key} \u2014 ${r.title ?? "(untitled)"}`)
60522
60558
  );
60523
60559
  }
60524
- lines.push(
60560
+ lines2.push(
60525
60561
  "",
60526
60562
  "## Close-out",
60527
60563
  "",
@@ -60535,7 +60571,7 @@ function renderSessionPrBody(runId, branch, records, commits = [], howToTest) {
60535
60571
  "branch. If the pull request is rejected instead, the items honestly stay In",
60536
60572
  "Review \u2014 flip them back or re-dispatch them."
60537
60573
  );
60538
- return lines.join("\n");
60574
+ return lines2.join("\n");
60539
60575
  }
60540
60576
  function renderHowToTestSection(howToTest, branch) {
60541
60577
  const { targetKey, record } = howToTest;
@@ -60665,15 +60701,15 @@ function renderLaneBlocks(lanes) {
60665
60701
  const blocks = [];
60666
60702
  const declined = lanes.filter((l) => l.lane === "declined_out_of_lane");
60667
60703
  if (declined.length > 0) {
60668
- const lines = [
60704
+ const lines2 = [
60669
60705
  `Re-plans NOT auto-approved \u2014 wider than the card's own lane, so a person decides (${declined.length}). Not a failure:`
60670
60706
  ];
60671
60707
  for (const record of declined) {
60672
- lines.push(` ${record.key}${record.planId ? ` \u2014 plan ${record.planId}` : ""}`);
60673
- for (const out2 of record.outOfLane ?? []) lines.push(` ${out2}`);
60708
+ lines2.push(` ${record.key}${record.planId ? ` \u2014 plan ${record.planId}` : ""}`);
60709
+ for (const out2 of record.outOfLane ?? []) lines2.push(` ${out2}`);
60674
60710
  }
60675
- lines.push(" Review each plan in Motir, then re-run the card if the corrected one survives.");
60676
- blocks.push(lines.join("\n"));
60711
+ lines2.push(" Review each plan in Motir, then re-run the card if the corrected one survives.");
60712
+ blocks.push(lines2.join("\n"));
60677
60713
  }
60678
60714
  const elsewhere = lanes.filter((l) => l.lane === "anchored_elsewhere");
60679
60715
  if (elsewhere.length > 0) {
@@ -60699,18 +60735,18 @@ function renderLaneBlocks(lanes) {
60699
60735
  }
60700
60736
  const refreshed = lanes.filter((l) => l.refresh && !isEmptyRefresh(l.refresh));
60701
60737
  if (refreshed.length > 0) {
60702
- const lines = [`Queue refreshed after an approved re-plan (${refreshed.length}):`];
60738
+ const lines2 = [`Queue refreshed after an approved re-plan (${refreshed.length}):`];
60703
60739
  for (const record of refreshed) {
60704
60740
  const refresh = record.refresh;
60705
- lines.push(` ${record.key} \u2014 re-read ${refresh.parentKey ?? "no parent"}'s children`);
60741
+ lines2.push(` ${record.key} \u2014 re-read ${refresh.parentKey ?? "no parent"}'s children`);
60706
60742
  if (refresh.removed.length > 0) {
60707
- lines.push(` removed by the plan, NOT run: ${refresh.removed.join(", ")}`);
60743
+ lines2.push(` removed by the plan, NOT run: ${refresh.removed.join(", ")}`);
60708
60744
  }
60709
60745
  if (refresh.added.length > 0) {
60710
- lines.push(` added by the plan, run when ready: ${refresh.added.join(", ")}`);
60746
+ lines2.push(` added by the plan, run when ready: ${refresh.added.join(", ")}`);
60711
60747
  }
60712
60748
  }
60713
- blocks.push(lines.join("\n"));
60749
+ blocks.push(lines2.join("\n"));
60714
60750
  }
60715
60751
  return blocks;
60716
60752
  }
@@ -60834,12 +60870,12 @@ function renderScopeRefusal(claim) {
60834
60870
  ].join("\n");
60835
60871
  }
60836
60872
  case "not_finishable": {
60837
- const lines = claim.blockers.map(
60873
+ const lines2 = claim.blockers.map(
60838
60874
  (b) => ` ${b.item} is blocked by ${b.blockedBy} (${b.blockerStatus}), which is OUTSIDE the scope`
60839
60875
  );
60840
60876
  return [
60841
60877
  `${scope}: NOT claimed \u2014 it cannot be finished from inside itself.`,
60842
- ...lines.length > 0 ? lines : [" (the validator named no specific pair)"],
60878
+ ...lines2.length > 0 ? lines2 : [" (the validator named no specific pair)"],
60843
60879
  "A run that took this scope could not finish it, so it did not take it. Finish the",
60844
60880
  "work above, or pull it into the scope. Nothing was locked."
60845
60881
  ].join("\n");
@@ -60978,7 +61014,7 @@ function sameKey(a, b) {
60978
61014
  return a.toUpperCase() === b.toUpperCase();
60979
61015
  }
60980
61016
  function renderLaneDecline(key, verdict) {
60981
- const lines = [
61017
+ const lines2 = [
60982
61018
  `${key}: its re-plan was NOT auto-approved \u2014 the plan reaches beyond this card's own lane.`,
60983
61019
  "This is not a failure. The plan is submitted and intact; it goes to a person, which is",
60984
61020
  "what a correction wider than one card and its siblings is supposed to do.",
@@ -60988,7 +61024,7 @@ function renderLaneDecline(key, verdict) {
60988
61024
  "",
60989
61025
  `Review the plan in Motir, then re-run ${key} if the corrected card survives.`
60990
61026
  ];
60991
- return lines.join("\n");
61027
+ return lines2.join("\n");
60992
61028
  }
60993
61029
  function renderElsewhereAnchored(key, serverMessage) {
60994
61030
  return [
@@ -61525,16 +61561,16 @@ async function refreshParent(client, lane, opts) {
61525
61561
  return { parentKey: lane.parentKey, removed, added };
61526
61562
  }
61527
61563
  function renderRefresh(refresh) {
61528
- const lines = [];
61564
+ const lines2 = [];
61529
61565
  if (refresh.removed.length > 0) {
61530
- lines.push(
61566
+ lines2.push(
61531
61567
  ` the plan removed ${refresh.removed.join(", ")} \u2014 held out of the rest of this run.`
61532
61568
  );
61533
61569
  }
61534
61570
  if (refresh.added.length > 0) {
61535
- lines.push(` the plan added ${refresh.added.join(", ")} \u2014 picked up when they are ready.`);
61571
+ lines2.push(` the plan added ${refresh.added.join(", ")} \u2014 picked up when they are ready.`);
61536
61572
  }
61537
- return lines;
61573
+ return lines2;
61538
61574
  }
61539
61575
  async function triggerExpansion(client, serverUrl, item) {
61540
61576
  const base = { key: item.key, title: item.title };
@@ -61866,8 +61902,8 @@ async function watchRunCi(summary, session, agent, opts, deps) {
61866
61902
  void opts;
61867
61903
  return allGreen;
61868
61904
  }
61869
- function closeOutRepos(summary, run, open = null, howToTest) {
61870
- const outstanding = open && open.openChildren.length > 0 ? open : null;
61905
+ function closeOutRepos(summary, run, open2 = null, howToTest) {
61906
+ const outstanding = open2 && open2.openChildren.length > 0 ? open2 : null;
61871
61907
  if (outstanding) {
61872
61908
  summary.outstanding = {
61873
61909
  containerKey: outstanding.containerKey,
@@ -62028,23 +62064,23 @@ async function drainScope(input) {
62028
62064
  break drain;
62029
62065
  }
62030
62066
  const item = byKey.get(key);
62031
- const open = [
62067
+ const open2 = [
62032
62068
  .../* @__PURE__ */ new Set([
62033
62069
  ...unsatisfiedBlockers(key, edges, satisfied, inScope),
62034
62070
  ...(edges[key] ?? []).filter((dep) => heldGates.has(dep))
62035
62071
  ])
62036
62072
  ];
62037
62073
  const prior = blockedSkips.get(key);
62038
- if (open.length > 0) {
62039
- if (prior && sameKeys(prior.blockedBy, open)) continue;
62074
+ if (open2.length > 0) {
62075
+ if (prior && sameKeys(prior.blockedBy, open2)) continue;
62040
62076
  if (prior) {
62041
- prior.blockedBy = open;
62077
+ prior.blockedBy = open2;
62042
62078
  } else {
62043
62079
  const record2 = {
62044
62080
  key: item.key,
62045
62081
  title: item.title,
62046
62082
  reason: "blocked_in_scope",
62047
- blockedBy: open
62083
+ blockedBy: open2
62048
62084
  };
62049
62085
  skipped.push(record2);
62050
62086
  blockedSkips.set(key, record2);
@@ -62053,12 +62089,12 @@ async function drainScope(input) {
62053
62089
  workItemKey: item.key,
62054
62090
  disposition: "skipped",
62055
62091
  skipReason: "blocked_in_scope",
62056
- data: { blockedBy: open }
62092
+ data: { blockedBy: open2 }
62057
62093
  });
62058
62094
  }
62059
- const gates = open.filter((dep) => heldGates.has(dep));
62095
+ const gates = open2.filter((dep) => heldGates.has(dep));
62060
62096
  info(
62061
- gates.length === open.length ? `${item.key}: held \u2014 waiting on ${gateLabel(gates)} to be approved.` : `${item.key}: skipped \u2014 waiting on ${open.join(", ")}, which did not land.`
62097
+ gates.length === open2.length ? `${item.key}: held \u2014 waiting on ${gateLabel(gates)} to be approved.` : `${item.key}: skipped \u2014 waiting on ${open2.join(", ")}, which did not land.`
62062
62098
  );
62063
62099
  continue;
62064
62100
  }
@@ -62477,13 +62513,13 @@ async function readRecord(client, key) {
62477
62513
  }
62478
62514
  }
62479
62515
  function checkoutsSection(repos) {
62480
- const lines = ["", "\u2550\u2550\u2550\u2550 CHECKOUTS \u2550\u2550\u2550\u2550", ""];
62516
+ const lines2 = ["", "\u2550\u2550\u2550\u2550 CHECKOUTS \u2550\u2550\u2550\u2550", ""];
62481
62517
  for (const repo of repos) {
62482
- lines.push(
62518
+ lines2.push(
62483
62519
  `- ${repo.repoName ?? "(the project repository)"}: ${repo.cwd} \u2014 branch ${repo.branch}`
62484
62520
  );
62485
62521
  }
62486
- return lines.join("\n") + "\n";
62522
+ return lines2.join("\n") + "\n";
62487
62523
  }
62488
62524
  async function runCloseOutHowToTest(input) {
62489
62525
  const { client, dispatchRunId, targetKey, summary } = input;
@@ -62680,8 +62716,8 @@ async function deliver(input) {
62680
62716
  primary: target,
62681
62717
  sessionBranch: dispatch.sessionBranch,
62682
62718
  reporter,
62683
- onMaterialization: (lines) => {
62684
- for (const line of lines) info(line);
62719
+ onMaterialization: (lines2) => {
62720
+ for (const line of lines2) info(line);
62685
62721
  },
62686
62722
  beforeSpawn: () => {
62687
62723
  info("");
@@ -62985,7 +63021,7 @@ async function runCommand(key, opts, deps = {}) {
62985
63021
  materialize: adopted !== null,
62986
63022
  ...adopted ? adoptedCheckoutPreparer() : {}
62987
63023
  });
62988
- const open = await readOpenChildren(client, decision.target);
63024
+ const open2 = await readOpenChildren(client, decision.target);
62989
63025
  const howToTest = decision.target.kind === "work_item" ? await runCloseOutHowToTest({
62990
63026
  client,
62991
63027
  dispatchRunId: reporter.runId,
@@ -62994,7 +63030,7 @@ async function runCommand(key, opts, deps = {}) {
62994
63030
  agent,
62995
63031
  runAgentFn: deps.runAgentFn ?? runAgent
62996
63032
  }) : void 0;
62997
- closeOutRepos(summary, run, open, howToTest);
63033
+ closeOutRepos(summary, run, open2, howToTest);
62998
63034
  await closeOutContainer(client, summary);
62999
63035
  for (const pr of summary.prs) {
63000
63036
  reporter.event({
@@ -63143,7 +63179,7 @@ async function runClaimedScope(input) {
63143
63179
  runAgentFn: deps.runAgentFn ?? runAgent,
63144
63180
  reporter
63145
63181
  });
63146
- const open = await readOpenChildren(client, decision.target);
63182
+ const open2 = await readOpenChildren(client, decision.target);
63147
63183
  const howToTest = decision.target.kind === "work_item" ? await runCloseOutHowToTest({
63148
63184
  client,
63149
63185
  dispatchRunId: reporter.runId,
@@ -63152,7 +63188,7 @@ async function runClaimedScope(input) {
63152
63188
  agent,
63153
63189
  runAgentFn: deps.runAgentFn ?? runAgent
63154
63190
  }) : void 0;
63155
- closeOutRepos(summary, run, open, howToTest);
63191
+ closeOutRepos(summary, run, open2, howToTest);
63156
63192
  await closeOutContainer(client, summary);
63157
63193
  for (const pr of summary.prs) {
63158
63194
  reporter.event({
@@ -63248,11 +63284,11 @@ function prepareContinueCheckouts(input) {
63248
63284
  exists: input.exists
63249
63285
  });
63250
63286
  const materialized = materializeDispatchCheckouts(input.rootDir, targets, { run: input.run });
63251
- const lines = renderMaterialization(materialized);
63287
+ const lines2 = renderMaterialization(materialized);
63252
63288
  if (materialized.failures.length > 0) {
63253
63289
  return {
63254
63290
  ok: false,
63255
- message: [`${input.key}: a repository could not be cloned.`, ...lines].join("\n")
63291
+ message: [`${input.key}: a repository could not be cloned.`, ...lines2].join("\n")
63256
63292
  };
63257
63293
  }
63258
63294
  const same = (a, b) => a !== null && b !== null && a.toLowerCase() === b.toLowerCase();
@@ -63280,7 +63316,7 @@ function prepareContinueCheckouts(input) {
63280
63316
  if (!prepared2.ok) return prepared2;
63281
63317
  checkouts.push({ repository: repo.name, branch, path: prepared2.path });
63282
63318
  }
63283
- return { ok: true, checkouts, materialized: lines };
63319
+ return { ok: true, checkouts, materialized: lines2 };
63284
63320
  }
63285
63321
  async function continueCommand(key, opts, deps = {}) {
63286
63322
  const trimmed = key.trim();
@@ -63618,15 +63654,15 @@ function prepareCheckouts(input) {
63618
63654
  function renderRepairGaveUp(input) {
63619
63655
  const { key, watch } = input;
63620
63656
  const head = watch.kind === "gave_up" ? `${key}: the repair gave up after ${pluralize(watch.attempts, "attempt")}.` : `${key}: the repair stopped after ${pluralize(watch.attempts, "attempt")} \u2014 ${watch.detail}.`;
63621
- const lines = [head];
63657
+ const lines2 = [head];
63622
63658
  for (const pr of input.pullRequests) {
63623
63659
  const checks = pr.queueExit ? `in the merge queue \u2014 ${pr.queueExit.failingCheckName ?? queueReasonInWords(pr.queueExit.rawReason)}` : pr.failingChecks.length > 0 ? pr.failingChecks.join(", ") : "unknown checks";
63624
- lines.push(` ${pr.repo}#${pr.number} \u2014 failing: ${checks} (${pr.url})`);
63660
+ lines2.push(` ${pr.repo}#${pr.number} \u2014 failing: ${checks} (${pr.url})`);
63625
63661
  }
63626
- lines.push(
63662
+ lines2.push(
63627
63663
  `The card stays where it is. Look at the failure, then run \`motir fix ${key}\` again.`
63628
63664
  );
63629
- return lines.join("\n");
63665
+ return lines2.join("\n");
63630
63666
  }
63631
63667
  async function fixCommand(key, opts, deps = {}) {
63632
63668
  const trimmed = key.trim();
@@ -63691,8 +63727,8 @@ function prepareHostedRepairCheckouts(input) {
63691
63727
  { exists: input.exists }
63692
63728
  );
63693
63729
  const materialized = materializeDispatchCheckouts(input.rootDir, targets, { run: input.run });
63694
- const lines = renderMaterialization(materialized);
63695
- for (const line of lines) info(line);
63730
+ const lines2 = renderMaterialization(materialized);
63731
+ for (const line of lines2) info(line);
63696
63732
  if (materialized.failures.length > 0) {
63697
63733
  return {
63698
63734
  ok: false,
@@ -64103,11 +64139,11 @@ function prepareReviewCheckouts(input) {
64103
64139
  { exists: input.exists }
64104
64140
  );
64105
64141
  const materialized = materializeDispatchCheckouts(input.rootDir, targets, { run: input.run });
64106
- const lines = renderMaterialization(materialized);
64142
+ const lines2 = renderMaterialization(materialized);
64107
64143
  if (materialized.failures.length > 0) {
64108
64144
  return {
64109
64145
  ok: false,
64110
- message: [`${input.key}: a repository under review could not be cloned.`, ...lines].join(
64146
+ message: [`${input.key}: a repository under review could not be cloned.`, ...lines2].join(
64111
64147
  "\n"
64112
64148
  )
64113
64149
  };
@@ -64155,7 +64191,7 @@ function prepareReviewCheckouts(input) {
64155
64191
  used.add(repoPath);
64156
64192
  checkouts.push({ repository: pr.repository, number: pr.number, headSha: pr.headSha, path });
64157
64193
  }
64158
- return { ok: true, checkouts, materialized: lines };
64194
+ return { ok: true, checkouts, materialized: lines2 };
64159
64195
  }
64160
64196
  function checkoutStandards(checkouts, exists = existsSync12) {
64161
64197
  const found = [];
@@ -64167,7 +64203,7 @@ function checkoutStandards(checkouts, exists = existsSync12) {
64167
64203
  return found;
64168
64204
  }
64169
64205
  function reviewRunSection(input) {
64170
- const lines = [
64206
+ const lines2 = [
64171
64207
  "",
64172
64208
  "## HOSTED REVIEW RUN \u2014 where the code is, and how your verdict leaves",
64173
64209
  "",
@@ -64180,7 +64216,7 @@ function reviewRunSection(input) {
64180
64216
  ""
64181
64217
  ];
64182
64218
  if (input.standards.length > 0) {
64183
- lines.push(
64219
+ lines2.push(
64184
64220
  "These repositories carry their own written standard. Read it as a standard for",
64185
64221
  "the CHANGED code \u2014 quote the rule a finding relies on; where the card requires",
64186
64222
  "what it forbids, the card wins:",
@@ -64189,7 +64225,7 @@ function reviewRunSection(input) {
64189
64225
  ""
64190
64226
  );
64191
64227
  }
64192
- lines.push(
64228
+ lines2.push(
64193
64229
  "Pushing is disabled in this run and `gh` is unavailable: you cannot commit, push or",
64194
64230
  "post anything, and must not try.",
64195
64231
  "",
@@ -64210,7 +64246,7 @@ function reviewRunSection(input) {
64210
64246
  "A missing, unparseable or incomplete file, or any other key, is NO verdict: the",
64211
64247
  "review is recorded as one that could not run. Write the file LAST, once you are sure."
64212
64248
  );
64213
- return `${lines.join("\n")}
64249
+ return `${lines2.join("\n")}
64214
64250
  `;
64215
64251
  }
64216
64252
 
@@ -64453,19 +64489,19 @@ function renderSkipGroups(skipped, titleWidth) {
64453
64489
  for (const reason of SKIP_ORDER) {
64454
64490
  const group = skipped.filter((s) => s.reason === reason);
64455
64491
  if (group.length === 0) continue;
64456
- const lines = [
64492
+ const lines2 = [
64457
64493
  `Not in the snapshot \u2014 ${SKIP_LABEL2[reason]} (${group.length}):`,
64458
64494
  ...group.map((s) => ` ${s.key} \u2014 ${truncate(s.title ?? "", titleWidth)}`)
64459
64495
  ];
64460
64496
  if (reason === "integrated_dep") {
64461
- lines.push(" Run these with `motir auto`, which carries that lineage on a session branch.");
64497
+ lines2.push(" Run these with `motir auto`, which carries that lineage on a session branch.");
64462
64498
  }
64463
64499
  if (reason === "replan_submitted") {
64464
- lines.push(
64500
+ lines2.push(
64465
64501
  " Each is in Planning with a plan awaiting approval; nothing was recorded for it."
64466
64502
  );
64467
64503
  }
64468
- blocks.push(lines.join("\n"));
64504
+ blocks.push(lines2.join("\n"));
64469
64505
  }
64470
64506
  return blocks;
64471
64507
  }
@@ -64889,8 +64925,8 @@ async function dispatchOne2(input) {
64889
64925
  // NULL, deliberately, exactly as before: batch has no session branch to
64890
64926
  // offer and must not start believing in one the server happened to mention.
64891
64927
  sessionBranch: null,
64892
- onMaterialization: (lines) => {
64893
- for (const line of lines) info(` ${line.trimStart()}`);
64928
+ onMaterialization: (lines2) => {
64929
+ for (const line of lines2) info(` ${line.trimStart()}`);
64894
64930
  },
64895
64931
  // One block per dispatched item, in the drain's own order.
64896
64932
  beforeSpawn: () => echoPromptIfAsked(input.opts, entry.key, dispatch),
@@ -65044,23 +65080,23 @@ function renderAwaiting(turns) {
65044
65080
  }
65045
65081
  function renderThread(session) {
65046
65082
  const resumed = session.turnCount > 0;
65047
- const lines = [
65083
+ const lines2 = [
65048
65084
  `${resumed ? "Resumed" : "Opened"} the planning conversation (${scopeLabel2(
65049
65085
  session.targetKeys
65050
65086
  )}) \u2014 ${session.turnCount} turn${session.turnCount === 1 ? "" : "s"}.`,
65051
65087
  session.lastSubmittedAt ? `Last submitted ${session.lastSubmittedAt}${session.lastJobId ? ` as job ${session.lastJobId}` : ""}.` : "Never submitted \u2014 nothing has been sent to the planner yet."
65052
65088
  ];
65053
65089
  if (session.turns.length > 0) {
65054
- lines.push("");
65090
+ lines2.push("");
65055
65091
  session.turns.forEach((turn, i) => {
65056
- lines.push(renderTurn(turn, i + 1));
65092
+ lines2.push(renderTurn(turn, i + 1));
65057
65093
  const disposition = dispositionMarkerFor(session.turns, i);
65058
- if (disposition) lines.push(renderDisposition(disposition));
65094
+ if (disposition) lines2.push(renderDisposition(disposition));
65059
65095
  });
65060
65096
  }
65061
65097
  const awaiting = renderAwaiting(session.turns);
65062
- if (awaiting) lines.push("", awaiting);
65063
- return lines.join("\n");
65098
+ if (awaiting) lines2.push("", awaiting);
65099
+ return lines2.join("\n");
65064
65100
  }
65065
65101
  function watchVerdict(outcome) {
65066
65102
  if (outcome.status !== "generating") return { kind: "ready" };
@@ -65129,37 +65165,37 @@ function renderProposalTree(items) {
65129
65165
  roots.push(item);
65130
65166
  }
65131
65167
  }
65132
- const lines = [];
65168
+ const lines2 = [];
65133
65169
  const seen = /* @__PURE__ */ new Set();
65134
65170
  const walk = (item, depth) => {
65135
65171
  if (seen.has(item.id)) return;
65136
65172
  seen.add(item.id);
65137
- lines.push(`${" ".repeat(depth + 1)}${describeProposal(item)}`);
65173
+ lines2.push(`${" ".repeat(depth + 1)}${describeProposal(item)}`);
65138
65174
  for (const child of childrenOf.get(item.id) ?? []) walk(child, depth + 1);
65139
65175
  };
65140
65176
  for (const root of roots) walk(root, 0);
65141
65177
  for (const item of items) walk(item, 0);
65142
- return lines;
65178
+ return lines2;
65143
65179
  }
65144
65180
  function renderPlan(plan) {
65145
- const lines = [
65181
+ const lines2 = [
65146
65182
  `Plan ${plan.id} \u2014 ${plan.status}, ${plan.itemCount} proposal${plan.itemCount === 1 ? "" : "s"}.`
65147
65183
  ];
65148
- if (plan.title) lines.push(`Title: ${plan.title}`);
65149
- if (plan.summary) lines.push(`Summary: ${plan.summary}`);
65150
- lines.push("");
65184
+ if (plan.title) lines2.push(`Title: ${plan.title}`);
65185
+ if (plan.summary) lines2.push(`Summary: ${plan.summary}`);
65186
+ lines2.push("");
65151
65187
  if (plan.items.length === 0) {
65152
- lines.push(
65188
+ lines2.push(
65153
65189
  plan.status === "generating" ? "No proposals have arrived yet \u2014 the planner is still generating this plan." : "This plan bundles no proposals."
65154
65190
  );
65155
65191
  } else {
65156
- lines.push(
65192
+ lines2.push(
65157
65193
  "Proposals (indented under their proposed parent):",
65158
65194
  ...renderProposalTree(plan.items)
65159
65195
  );
65160
65196
  }
65161
- lines.push("", PROPOSALS_NOT_WORK_ITEMS);
65162
- return lines.join("\n");
65197
+ lines2.push("", PROPOSALS_NOT_WORK_ITEMS);
65198
+ return lines2.join("\n");
65163
65199
  }
65164
65200
  function nextStepHint(reviewUrl, anchored) {
65165
65201
  const resume = anchored.length > 0 ? `motir plan ${anchored.join(" ")}` : "motir plan";
@@ -65172,8 +65208,8 @@ function nextStepHint(reviewUrl, anchored) {
65172
65208
 
65173
65209
  // src/commands/plan.ts
65174
65210
  function delay2(ms) {
65175
- return new Promise((resolve6) => {
65176
- setTimeout(resolve6, ms);
65211
+ return new Promise((resolve8) => {
65212
+ setTimeout(resolve8, ms);
65177
65213
  });
65178
65214
  }
65179
65215
  async function planCommand(args = [], opts = {}, deps = {}) {
@@ -65305,7 +65341,7 @@ async function watchAndShow(client, planId, reviewUrl, targetKeys, deps) {
65305
65341
 
65306
65342
  // src/commands/agentTerminal.ts
65307
65343
  import { chmodSync as chmodSync5, mkdirSync as mkdirSync7, rmSync as rmSync7, writeFileSync as writeFileSync8 } from "fs";
65308
- import { join as join21 } from "path";
65344
+ import { join as join24 } from "path";
65309
65345
 
65310
65346
  // src/agentTerminal/control.ts
65311
65347
  import { chmodSync as chmodSync4, mkdirSync as mkdirSync6, rmSync as rmSync5 } from "fs";
@@ -65352,7 +65388,7 @@ function parseControlRequest(text) {
65352
65388
  }
65353
65389
  }
65354
65390
  function readLine(socket) {
65355
- return new Promise((resolve6) => {
65391
+ return new Promise((resolve8) => {
65356
65392
  const chunks = [];
65357
65393
  let size = 0;
65358
65394
  let done = false;
@@ -65363,7 +65399,7 @@ function readLine(socket) {
65363
65399
  socket.off("end", onEnd);
65364
65400
  socket.off("error", onEnd);
65365
65401
  socket.off("close", onEnd);
65366
- resolve6(line);
65402
+ resolve8(line);
65367
65403
  };
65368
65404
  const onData = (chunk) => {
65369
65405
  chunks.push(chunk);
@@ -65397,7 +65433,7 @@ function listenControl(path, handler) {
65397
65433
  `);
65398
65434
  });
65399
65435
  });
65400
- return new Promise((resolve6, reject) => {
65436
+ return new Promise((resolve8, reject) => {
65401
65437
  server.once("error", reject);
65402
65438
  server.listen(path, () => {
65403
65439
  server.off("error", reject);
@@ -65405,7 +65441,7 @@ function listenControl(path, handler) {
65405
65441
  chmodSync4(path, 384);
65406
65442
  } catch {
65407
65443
  }
65408
- resolve6({
65444
+ resolve8({
65409
65445
  server,
65410
65446
  close: () => new Promise((done) => {
65411
65447
  server.close(() => {
@@ -65424,7 +65460,7 @@ var ControlUnavailableError = class extends Error {
65424
65460
  }
65425
65461
  };
65426
65462
  function controlRequest(path, request, timeoutMs) {
65427
- return new Promise((resolve6, reject) => {
65463
+ return new Promise((resolve8, reject) => {
65428
65464
  const socket = connect(path);
65429
65465
  const timer = setTimeout(() => {
65430
65466
  socket.destroy();
@@ -65446,7 +65482,7 @@ function controlRequest(path, request, timeoutMs) {
65446
65482
  return;
65447
65483
  }
65448
65484
  try {
65449
- resolve6(JSON.parse(line));
65485
+ resolve8(JSON.parse(line));
65450
65486
  } catch {
65451
65487
  reject(new ControlUnavailableError());
65452
65488
  }
@@ -65497,10 +65533,2375 @@ function loadNodePty(dir) {
65497
65533
  }
65498
65534
 
65499
65535
  // src/agentTerminal/server.ts
65500
- import { randomUUID } from "crypto";
65536
+ import { randomUUID as randomUUID2 } from "crypto";
65501
65537
  import { existsSync as existsSync15, rmSync as rmSync6, statSync as statSync3 } from "fs";
65502
65538
  import { createServer as createServer2 } from "http";
65539
+ import { join as join23 } from "path";
65540
+
65541
+ // src/agentTerminal/chat/adapters/claude.ts
65542
+ import { execFile as execFile2 } from "child_process";
65543
+ import { randomUUID } from "crypto";
65544
+ import { open, readdir, stat } from "fs/promises";
65545
+ import { join as join19 } from "path";
65546
+
65547
+ // src/agentTerminal/chat/protocol.ts
65548
+ var MAX_PROMPT_BYTES = 64 * 1024;
65549
+ var MAX_TOOL_OUTPUT_BYTES = 64 * 1024;
65550
+ var SESSION_ID = /^[A-Za-z0-9_][A-Za-z0-9_.:-]{0,127}$/;
65551
+ function isSessionId(value) {
65552
+ return SESSION_ID.test(value) && !value.includes("..");
65553
+ }
65554
+ function parseChatClientFrame(text) {
65555
+ let value;
65556
+ try {
65557
+ value = JSON.parse(text);
65558
+ } catch {
65559
+ return { ok: false, code: "bad_frame" };
65560
+ }
65561
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
65562
+ return { ok: false, code: "bad_frame" };
65563
+ }
65564
+ const frame = value;
65565
+ switch (frame["t"]) {
65566
+ case "list":
65567
+ return { ok: true, frame: { t: "list" } };
65568
+ case "stop":
65569
+ return { ok: true, frame: { t: "stop" } };
65570
+ case "ping":
65571
+ return { ok: true, frame: { t: "ping", active: frame["active"] === true } };
65572
+ case "open": {
65573
+ const session = frame["session"];
65574
+ if (session === void 0 || session === null) return { ok: true, frame: { t: "open" } };
65575
+ if (typeof session !== "string" || session.length === 0) {
65576
+ return { ok: false, code: "bad_frame" };
65577
+ }
65578
+ return { ok: true, frame: { t: "open", session } };
65579
+ }
65580
+ case "prompt": {
65581
+ const promptText = frame["text"];
65582
+ if (typeof promptText !== "string" || promptText.length === 0) {
65583
+ return { ok: false, code: "bad_frame" };
65584
+ }
65585
+ if (Buffer.byteLength(promptText, "utf8") > MAX_PROMPT_BYTES) {
65586
+ return { ok: false, code: "too_large" };
65587
+ }
65588
+ return { ok: true, frame: { t: "prompt", text: promptText } };
65589
+ }
65590
+ default:
65591
+ return { ok: false, code: "bad_frame" };
65592
+ }
65593
+ }
65594
+ function encodeChatServerFrame(frame) {
65595
+ return JSON.stringify(frame);
65596
+ }
65597
+ function boundTail(text, maxBytes = MAX_TOOL_OUTPUT_BYTES) {
65598
+ const bytes = Buffer.from(text, "utf8");
65599
+ if (bytes.length <= maxBytes) return { text, truncated: false };
65600
+ let start = bytes.length - maxBytes;
65601
+ while (start < bytes.length && (bytes[start] & 192) === 128) start++;
65602
+ return { text: bytes.subarray(start).toString("utf8"), truncated: true };
65603
+ }
65604
+ function boundEvent(event) {
65605
+ if (event.k === "tool_result" && event.output !== void 0) {
65606
+ const bounded = boundTail(event.output);
65607
+ if (!bounded.truncated) return event;
65608
+ return { ...event, output: bounded.text, truncated: true };
65609
+ }
65610
+ if (event.k === "tool_call" && event.diff !== void 0) {
65611
+ const bounded = boundTail(event.diff);
65612
+ if (!bounded.truncated) return event;
65613
+ return { ...event, diff: bounded.text };
65614
+ }
65615
+ return event;
65616
+ }
65617
+
65618
+ // src/agentTerminal/chat/adapters/claude.ts
65619
+ var BINARY = "claude";
65620
+ var UNSUPPORTED = { supported: false, code: "unsupported" };
65621
+ var MAX_TITLE_CHARS = 120;
65622
+ var PROBE_TIMEOUT_MS = 15e3;
65623
+ var TITLE_SCAN_BYTES = 64 * 1024;
65624
+ var HISTORY_SCAN_BYTES = 8 * 1024 * 1024;
65625
+ function classifyClaudeAuth(status) {
65626
+ const provider = typeof status.apiProvider === "string" ? status.apiProvider : null;
65627
+ if (provider !== null && provider !== "" && provider !== "firstParty") return "cloud_provider";
65628
+ const source = typeof status.apiKeySource === "string" ? status.apiKeySource : null;
65629
+ if (source !== null && source !== "" && source !== "none") return "api_key";
65630
+ return "subscription";
65631
+ }
65632
+ var runClaudeAuthStatus = (ctx) => new Promise((resolve8, reject) => {
65633
+ const env = { ...ctx.env };
65634
+ delete env["MOTIR_TERMINAL_KEY"];
65635
+ execFile2(
65636
+ BINARY,
65637
+ ["auth", "status", "--json"],
65638
+ {
65639
+ cwd: ctx.cwd,
65640
+ env,
65641
+ timeout: PROBE_TIMEOUT_MS,
65642
+ maxBuffer: 64 * 1024,
65643
+ windowsHide: true
65644
+ },
65645
+ (error, stdout) => {
65646
+ const out2 = typeof stdout === "string" ? stdout : String(stdout ?? "");
65647
+ if (out2.trim().length > 0) return resolve8(out2);
65648
+ reject(error ?? new Error("no answer"));
65649
+ }
65650
+ );
65651
+ });
65652
+ function parseAuthStatus(text) {
65653
+ const start = text.indexOf("{");
65654
+ const end = text.lastIndexOf("}");
65655
+ if (start === -1 || end < start) return null;
65656
+ try {
65657
+ const value = JSON.parse(text.slice(start, end + 1));
65658
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
65659
+ return value;
65660
+ } catch {
65661
+ return null;
65662
+ }
65663
+ }
65664
+ function isObject(value) {
65665
+ return typeof value === "object" && value !== null && !Array.isArray(value);
65666
+ }
65667
+ function str(value) {
65668
+ return typeof value === "string" ? value : void 0;
65669
+ }
65670
+ function splitLines(text) {
65671
+ if (text === "") return [];
65672
+ const lines2 = text.split("\n");
65673
+ if (lines2[lines2.length - 1] === "") lines2.pop();
65674
+ return lines2;
65675
+ }
65676
+ function hunk(oldText, newText) {
65677
+ const removed = splitLines(oldText);
65678
+ const added = splitLines(newText);
65679
+ const lines2 = [`@@ -${removed.length} +${added.length} @@`];
65680
+ for (const line of removed) lines2.push(`-${line}`);
65681
+ for (const line of added) lines2.push(`+${line}`);
65682
+ return lines2.join("\n");
65683
+ }
65684
+ function diffOf(name, input, path) {
65685
+ const header = `--- a/${path.replace(/^\/+/, "")}
65686
+ +++ b/${path.replace(/^\/+/, "")}`;
65687
+ if (name === "Edit") {
65688
+ const oldString = str(input["old_string"]);
65689
+ const newString = str(input["new_string"]);
65690
+ if (oldString === void 0 || newString === void 0) return void 0;
65691
+ return `${header}
65692
+ ${hunk(oldString, newString)}
65693
+ `;
65694
+ }
65695
+ if (name === "MultiEdit") {
65696
+ const edits = Array.isArray(input["edits"]) ? input["edits"] : [];
65697
+ const hunks = [];
65698
+ for (const edit of edits) {
65699
+ if (!isObject(edit)) continue;
65700
+ const oldString = str(edit["old_string"]);
65701
+ const newString = str(edit["new_string"]);
65702
+ if (oldString === void 0 || newString === void 0) continue;
65703
+ hunks.push(hunk(oldString, newString));
65704
+ }
65705
+ return hunks.length > 0 ? `${header}
65706
+ ${hunks.join("\n")}
65707
+ ` : void 0;
65708
+ }
65709
+ if (name === "Write") {
65710
+ const content = str(input["content"]);
65711
+ if (content === void 0) return void 0;
65712
+ return `${header}
65713
+ ${hunk("", content)}
65714
+ `;
65715
+ }
65716
+ return void 0;
65717
+ }
65718
+ var EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit"]);
65719
+ function toolCallEvent(block) {
65720
+ const id = str(block["id"]);
65721
+ const name = str(block["name"]);
65722
+ if (!id || !name) return null;
65723
+ const input = isObject(block["input"]) ? block["input"] : {};
65724
+ if (name === "Read") {
65725
+ const path = str(input["file_path"]);
65726
+ return {
65727
+ k: "tool_call",
65728
+ id,
65729
+ kind: "read",
65730
+ name,
65731
+ title: path ?? name,
65732
+ ...path ? { path } : {}
65733
+ };
65734
+ }
65735
+ if (EDIT_TOOLS.has(name)) {
65736
+ const path = str(input["file_path"]);
65737
+ const diff = path ? diffOf(name, input, path) : void 0;
65738
+ return {
65739
+ k: "tool_call",
65740
+ id,
65741
+ kind: "edit",
65742
+ name,
65743
+ title: path ?? name,
65744
+ ...path ? { path } : {},
65745
+ ...diff !== void 0 ? { diff: boundTail(diff).text } : {}
65746
+ };
65747
+ }
65748
+ if (name === "Bash") {
65749
+ const command = str(input["command"]);
65750
+ const description = str(input["description"]);
65751
+ return {
65752
+ k: "tool_call",
65753
+ id,
65754
+ kind: "command",
65755
+ name,
65756
+ title: description || command || name,
65757
+ ...command !== void 0 ? { command } : {}
65758
+ };
65759
+ }
65760
+ const kind = "other";
65761
+ return { k: "tool_call", id, kind, name, title: name };
65762
+ }
65763
+ function resultText(content) {
65764
+ if (typeof content === "string") return content;
65765
+ if (!Array.isArray(content)) return "";
65766
+ const parts = [];
65767
+ for (const part of content) {
65768
+ if (isObject(part) && part["type"] === "text" && typeof part["text"] === "string") {
65769
+ parts.push(part["text"]);
65770
+ }
65771
+ }
65772
+ return parts.join("\n");
65773
+ }
65774
+ function toolResultEvent(block) {
65775
+ const id = str(block["tool_use_id"]);
65776
+ if (!id) return null;
65777
+ const ok = block["is_error"] !== true;
65778
+ const bounded = boundTail(resultText(block["content"]));
65779
+ const exit = ok ? null : /^Exit code (\d+)/.exec(bounded.text);
65780
+ return {
65781
+ k: "tool_result",
65782
+ id,
65783
+ ok,
65784
+ output: bounded.text,
65785
+ ...exit ? { exitCode: Number(exit[1]) } : {},
65786
+ truncated: bounded.truncated
65787
+ };
65788
+ }
65789
+ var DROPPED_SYSTEM = /* @__PURE__ */ new Set(["hook_started", "hook_progress", "hook_response", "status"]);
65790
+ var DROPPED_TYPES = /* @__PURE__ */ new Set(["rate_limit_event", "keep_alive"]);
65791
+ function createClaudeMapper(options = {}) {
65792
+ let session = null;
65793
+ let ended = false;
65794
+ let kill = null;
65795
+ const streamed = /* @__PURE__ */ new Set();
65796
+ let streamingMessage = null;
65797
+ const calls = /* @__PURE__ */ new Set();
65798
+ const results = /* @__PURE__ */ new Set();
65799
+ const onStreamEvent = (value) => {
65800
+ const event = isObject(value["event"]) ? value["event"] : null;
65801
+ if (!event) return [];
65802
+ if (event["type"] === "message_start") {
65803
+ const message = isObject(event["message"]) ? event["message"] : null;
65804
+ streamingMessage = str(message?.["id"]) ?? null;
65805
+ return [];
65806
+ }
65807
+ if (event["type"] !== "content_block_delta") return [];
65808
+ const delta = isObject(event["delta"]) ? event["delta"] : null;
65809
+ if (!delta || delta["type"] !== "text_delta") return [];
65810
+ const text = str(delta["text"]);
65811
+ if (!text || streamingMessage === null) return [];
65812
+ streamed.add(streamingMessage);
65813
+ const index = typeof event["index"] === "number" ? event["index"] : 0;
65814
+ return [{ k: "text", id: `${streamingMessage}:${index}`, delta: text }];
65815
+ };
65816
+ const onAssistant = (value) => {
65817
+ const message = isObject(value["message"]) ? value["message"] : null;
65818
+ if (!message || !Array.isArray(message["content"])) return [];
65819
+ const messageId = str(message["id"]) ?? str(value["uuid"]) ?? "message";
65820
+ const events = [];
65821
+ message["content"].forEach((block, index) => {
65822
+ if (!isObject(block)) return;
65823
+ if (block["type"] === "text") {
65824
+ if (streamed.has(messageId)) return;
65825
+ const text = str(block["text"]);
65826
+ if (text) events.push({ k: "text", id: `${messageId}:t${index}`, delta: text });
65827
+ return;
65828
+ }
65829
+ if (block["type"] === "tool_use") {
65830
+ const call = toolCallEvent(block);
65831
+ if (call && call.k === "tool_call" && !calls.has(call.id)) {
65832
+ calls.add(call.id);
65833
+ events.push(call);
65834
+ }
65835
+ }
65836
+ });
65837
+ return events;
65838
+ };
65839
+ const onUser = (value) => {
65840
+ const message = isObject(value["message"]) ? value["message"] : null;
65841
+ if (!message || !Array.isArray(message["content"])) return [];
65842
+ const events = [];
65843
+ for (const block of message["content"]) {
65844
+ if (!isObject(block) || block["type"] !== "tool_result") continue;
65845
+ const result = toolResultEvent(block);
65846
+ if (result && result.k === "tool_result" && !results.has(result.id)) {
65847
+ results.add(result.id);
65848
+ events.push(result);
65849
+ }
65850
+ }
65851
+ return events;
65852
+ };
65853
+ const onResult = (value) => {
65854
+ if (value["terminal_reason"] === "aborted_streaming") return [];
65855
+ ended = true;
65856
+ if (value["subtype"] === "success" && value["is_error"] !== true) return [];
65857
+ const code = str(value["subtype"]) ?? "error";
65858
+ const message = str(value["result"]);
65859
+ return [{ k: "error", code, ...message ? { message } : {} }];
65860
+ };
65861
+ const onSystem = (value) => {
65862
+ const subtype = str(value["subtype"]) ?? "";
65863
+ if (subtype === "init") {
65864
+ if (value["apiKeySource"] === "none" && !options.trustNoKey) kill = "subscription_signin";
65865
+ return [];
65866
+ }
65867
+ if (subtype === "api_retry") return [{ k: "error", code: "api_retry" }];
65868
+ if (DROPPED_SYSTEM.has(subtype)) return [];
65869
+ return [{ k: "other", name: subtype ? `system/${subtype}` : "system" }];
65870
+ };
65871
+ return {
65872
+ onLine(line) {
65873
+ let value;
65874
+ try {
65875
+ value = JSON.parse(line);
65876
+ } catch {
65877
+ return [];
65878
+ }
65879
+ if (!isObject(value)) return [];
65880
+ const type = str(value["type"]);
65881
+ if (!type) return [];
65882
+ const id = str(value["session_id"]);
65883
+ if (id && session === null && isSessionId(id)) session = id;
65884
+ if (typeof value["parent_tool_use_id"] === "string") return [];
65885
+ switch (type) {
65886
+ case "system":
65887
+ return onSystem(value);
65888
+ case "stream_event":
65889
+ return onStreamEvent(value);
65890
+ case "assistant":
65891
+ return onAssistant(value);
65892
+ case "user":
65893
+ return onUser(value);
65894
+ case "result":
65895
+ return onResult(value);
65896
+ default:
65897
+ return DROPPED_TYPES.has(type) ? [] : [{ k: "other", name: type }];
65898
+ }
65899
+ },
65900
+ sessionId: () => session,
65901
+ sawEnd: () => ended,
65902
+ killCode: () => kill
65903
+ };
65904
+ }
65905
+ function claudeProjectDir(ctx) {
65906
+ const configDir2 = ctx.env["CLAUDE_CONFIG_DIR"] || join19(ctx.home, ".claude");
65907
+ return join19(configDir2, "projects", ctx.cwd.replace(/[^A-Za-z0-9]/g, "-"));
65908
+ }
65909
+ async function readBounded(path, max, from) {
65910
+ const handle = await open(path, "r");
65911
+ try {
65912
+ const { size } = await handle.stat();
65913
+ const length = Math.min(size, max);
65914
+ const position = from === "end" ? size - length : 0;
65915
+ const buffer = Buffer.alloc(length);
65916
+ await handle.read(buffer, 0, length, position);
65917
+ let text = buffer.toString("utf8");
65918
+ const cut = size > max;
65919
+ if (cut && from === "end") text = text.slice(text.indexOf("\n") + 1);
65920
+ if (cut && from === "start") text = text.slice(0, text.lastIndexOf("\n") + 1);
65921
+ return { text, cut };
65922
+ } finally {
65923
+ await handle.close();
65924
+ }
65925
+ }
65926
+ function parseJsonLines(text) {
65927
+ const out2 = [];
65928
+ for (const line of text.split("\n")) {
65929
+ if (line.trim() === "") continue;
65930
+ try {
65931
+ const value = JSON.parse(line);
65932
+ if (isObject(value)) out2.push(value);
65933
+ } catch {
65934
+ }
65935
+ }
65936
+ return out2;
65937
+ }
65938
+ function promptOf(entry) {
65939
+ if (entry["type"] !== "user" || entry["isMeta"] === true || entry["isSidechain"] === true) {
65940
+ return null;
65941
+ }
65942
+ const message = isObject(entry["message"]) ? entry["message"] : null;
65943
+ if (!message) return null;
65944
+ const content = message["content"];
65945
+ let text = null;
65946
+ if (typeof content === "string") text = content;
65947
+ else if (Array.isArray(content)) {
65948
+ if (content.some((part) => isObject(part) && part["type"] === "tool_result")) return null;
65949
+ text = resultText(content);
65950
+ }
65951
+ if (!text || !text.trim()) return null;
65952
+ const trimmed = text.trimStart();
65953
+ if (/^<(command-|local-command-)/.test(trimmed) || trimmed.startsWith("Caveat:")) return null;
65954
+ return text;
65955
+ }
65956
+ function cutTitle(text) {
65957
+ const oneLine = text.replace(/\s+/g, " ").trim();
65958
+ const chars = Array.from(oneLine);
65959
+ return chars.length <= MAX_TITLE_CHARS ? oneLine : chars.slice(0, MAX_TITLE_CHARS).join("");
65960
+ }
65961
+ function titleOf(entries) {
65962
+ let summary = null;
65963
+ let firstPrompt = null;
65964
+ for (const entry of entries) {
65965
+ if (entry["type"] === "summary" && typeof entry["summary"] === "string") {
65966
+ summary = entry["summary"];
65967
+ }
65968
+ if (firstPrompt === null) firstPrompt = promptOf(entry);
65969
+ }
65970
+ const title = summary ?? firstPrompt;
65971
+ return title === null ? null : cutTitle(title);
65972
+ }
65973
+ async function listClaudeSessions(ctx, limit) {
65974
+ const dir = claudeProjectDir(ctx);
65975
+ let names;
65976
+ try {
65977
+ names = await readdir(dir);
65978
+ } catch {
65979
+ return [];
65980
+ }
65981
+ const files = [];
65982
+ for (const name of names) {
65983
+ if (!name.endsWith(".jsonl")) continue;
65984
+ const id = name.slice(0, -".jsonl".length);
65985
+ if (!isSessionId(id)) continue;
65986
+ const path = join19(dir, name);
65987
+ try {
65988
+ const info2 = await stat(path);
65989
+ if (info2.isFile()) files.push({ id, path, mtimeMs: info2.mtimeMs });
65990
+ } catch {
65991
+ }
65992
+ }
65993
+ files.sort((a, b) => b.mtimeMs - a.mtimeMs || a.id.localeCompare(b.id));
65994
+ const out2 = [];
65995
+ for (const file of files) {
65996
+ if (out2.length >= limit) break;
65997
+ let title = null;
65998
+ try {
65999
+ title = titleOf(
66000
+ parseJsonLines((await readBounded(file.path, TITLE_SCAN_BYTES, "start")).text)
66001
+ );
66002
+ } catch {
66003
+ continue;
66004
+ }
66005
+ if (title === null) continue;
66006
+ out2.push({ id: file.id, title, updatedAt: new Date(file.mtimeMs).toISOString() });
66007
+ }
66008
+ return out2;
66009
+ }
66010
+ function historyEvents(entries) {
66011
+ const events = [];
66012
+ const calls = /* @__PURE__ */ new Set();
66013
+ for (const entry of entries) {
66014
+ if (entry["isSidechain"] === true) continue;
66015
+ const prompt = promptOf(entry);
66016
+ if (prompt !== null) {
66017
+ events.push({ k: "user", text: prompt });
66018
+ continue;
66019
+ }
66020
+ const message = isObject(entry["message"]) ? entry["message"] : null;
66021
+ if (!message || !Array.isArray(message["content"])) continue;
66022
+ if (entry["type"] === "assistant") {
66023
+ const messageId = str(message["id"]) ?? str(entry["uuid"]) ?? "message";
66024
+ message["content"].forEach((block, index) => {
66025
+ if (!isObject(block)) return;
66026
+ if (block["type"] === "text" && str(block["text"])) {
66027
+ events.push({ k: "text", id: `${messageId}:h${index}`, delta: block["text"] });
66028
+ } else if (block["type"] === "tool_use") {
66029
+ const call = toolCallEvent(block);
66030
+ if (call && call.k === "tool_call" && !calls.has(call.id)) {
66031
+ calls.add(call.id);
66032
+ events.push(call);
66033
+ }
66034
+ }
66035
+ });
66036
+ } else if (entry["type"] === "user") {
66037
+ for (const block of message["content"]) {
66038
+ if (isObject(block) && block["type"] === "tool_result") {
66039
+ const result = toolResultEvent(block);
66040
+ if (result) events.push(result);
66041
+ }
66042
+ }
66043
+ }
66044
+ }
66045
+ return events;
66046
+ }
66047
+ function withinBudget(events, budgetBytes) {
66048
+ const sizes = events.map((event) => Buffer.byteLength(JSON.stringify(event), "utf8"));
66049
+ let total = sizes.reduce((sum, size) => sum + size, 0);
66050
+ let start = 0;
66051
+ while (total > budgetBytes && start < events.length) {
66052
+ let next = start + 1;
66053
+ while (next < events.length && events[next].k !== "user") next++;
66054
+ for (let i = start; i < next; i++) total -= sizes[i];
66055
+ start = next;
66056
+ }
66057
+ return { events: events.slice(start), dropped: start > 0 };
66058
+ }
66059
+ async function readClaudeHistory(ctx, sessionId, budgetBytes) {
66060
+ if (!isSessionId(sessionId)) return { unavailable: true };
66061
+ let read;
66062
+ try {
66063
+ read = await readBounded(
66064
+ join19(claudeProjectDir(ctx), `${sessionId}.jsonl`),
66065
+ HISTORY_SCAN_BYTES,
66066
+ "end"
66067
+ );
66068
+ } catch {
66069
+ return { unavailable: true };
66070
+ }
66071
+ let events = historyEvents(parseJsonLines(read.text));
66072
+ if (read.cut) {
66073
+ const first = events.findIndex((event) => event.k === "user");
66074
+ events = first === -1 ? [] : events.slice(first);
66075
+ }
66076
+ const bounded = withinBudget(events, budgetBytes);
66077
+ return { events: bounded.events, truncated: read.cut || bounded.dropped };
66078
+ }
66079
+ function createClaudeChatAdapter(options = {}) {
66080
+ const probe = options.probe ?? runClaudeAuthStatus;
66081
+ const newSessionId = options.newSessionId ?? randomUUID;
66082
+ let lastKind = null;
66083
+ return {
66084
+ profile: "claude",
66085
+ async support(ctx) {
66086
+ lastKind = null;
66087
+ let answer;
66088
+ try {
66089
+ answer = await probe(ctx);
66090
+ } catch {
66091
+ return UNSUPPORTED;
66092
+ }
66093
+ const status = parseAuthStatus(answer);
66094
+ if (!status) return UNSUPPORTED;
66095
+ lastKind = classifyClaudeAuth(status);
66096
+ if (lastKind === "subscription") return { supported: false, code: "subscription_signin" };
66097
+ return { supported: true, signedIn: true };
66098
+ },
66099
+ turnCommand({ prompt, sessionId }) {
66100
+ return {
66101
+ file: BINARY,
66102
+ args: [
66103
+ "-p",
66104
+ "--output-format",
66105
+ "stream-json",
66106
+ "--verbose",
66107
+ "--include-partial-messages",
66108
+ ...sessionId === null ? ["--session-id", newSessionId()] : ["--resume", sessionId],
66109
+ "--dangerously-skip-permissions"
66110
+ ],
66111
+ stdin: prompt
66112
+ };
66113
+ },
66114
+ createMapper: () => createClaudeMapper({ trustNoKey: lastKind === "cloud_provider" }),
66115
+ listSessions: listClaudeSessions,
66116
+ readHistory: readClaudeHistory
66117
+ };
66118
+ }
66119
+ var claudeChatAdapter = createClaudeChatAdapter();
66120
+
66121
+ // src/agentTerminal/chat/adapters/codex.ts
66122
+ import { createReadStream } from "fs";
66123
+ import { readdir as readdir2, stat as stat2 } from "fs/promises";
65503
66124
  import { join as join20 } from "path";
66125
+ import { createInterface as createInterface2 } from "readline";
66126
+ var CODEX_PROFILE = "codex";
66127
+ var CODEX_SANDBOX_OVERRIDE = 'sandbox_mode="workspace-write"';
66128
+ var TURN_FLAGS = ["--json", "-c", CODEX_SANDBOX_OVERRIDE];
66129
+ var LIST_SCAN_LINES = 400;
66130
+ var ROLLOUT_FILE = /^rollout-.+\.jsonl$/;
66131
+ function parseObject(line) {
66132
+ let value;
66133
+ try {
66134
+ value = JSON.parse(line);
66135
+ } catch {
66136
+ return null;
66137
+ }
66138
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
66139
+ }
66140
+ var str2 = (value) => typeof value === "string" ? value : void 0;
66141
+ var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
66142
+ var obj = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
66143
+ function commandText(value) {
66144
+ if (typeof value === "string") return value;
66145
+ if (Array.isArray(value) && value.every((part) => typeof part === "string")) {
66146
+ return value.join(" ");
66147
+ }
66148
+ return void 0;
66149
+ }
66150
+ function toolOutput(id, ok, output, exitCode) {
66151
+ const bounded = output === void 0 ? void 0 : boundTail(output);
66152
+ return {
66153
+ k: "tool_result",
66154
+ id,
66155
+ ok,
66156
+ ...bounded !== void 0 ? { output: bounded.text } : {},
66157
+ ...exitCode !== void 0 ? { exitCode } : {},
66158
+ truncated: bounded?.truncated ?? false
66159
+ };
66160
+ }
66161
+ function mcpResultText(result) {
66162
+ const content = obj(result)?.["content"];
66163
+ if (!Array.isArray(content)) return void 0;
66164
+ const texts = content.map((block) => obj(block)?.["type"] === "text" ? str2(obj(block)?.["text"]) : void 0).filter((text) => text !== void 0);
66165
+ return texts.length > 0 ? texts.join("\n") : void 0;
66166
+ }
66167
+ function createCodexMapper() {
66168
+ let thread = null;
66169
+ let ended = false;
66170
+ const called = /* @__PURE__ */ new Set();
66171
+ const commandCall = (item, id) => {
66172
+ const command = str2(item["command"]) ?? "";
66173
+ return {
66174
+ k: "tool_call",
66175
+ id,
66176
+ kind: "command",
66177
+ name: "command_execution",
66178
+ title: command,
66179
+ command
66180
+ };
66181
+ };
66182
+ const mcpCall = (item, id) => {
66183
+ const server = str2(item["server"]);
66184
+ const tool = str2(item["tool"]) ?? "mcp_tool_call";
66185
+ const name = server ? `${server}.${tool}` : tool;
66186
+ return { k: "tool_call", id, kind: "other", name, title: name };
66187
+ };
66188
+ const searchCall = (item, id) => ({
66189
+ k: "tool_call",
66190
+ id,
66191
+ kind: "other",
66192
+ name: "web_search",
66193
+ title: str2(item["query"]) ?? "web_search"
66194
+ });
66195
+ const onStarted = (item, id) => {
66196
+ let call = null;
66197
+ switch (item["type"]) {
66198
+ case "command_execution":
66199
+ call = commandCall(item, id);
66200
+ break;
66201
+ case "mcp_tool_call":
66202
+ call = mcpCall(item, id);
66203
+ break;
66204
+ case "web_search":
66205
+ call = searchCall(item, id);
66206
+ break;
66207
+ default:
66208
+ return [];
66209
+ }
66210
+ if (called.has(id)) return [];
66211
+ called.add(id);
66212
+ return [call];
66213
+ };
66214
+ const withCall = (id, call, rest) => called.has(id) ? rest : [call, ...rest];
66215
+ const onCompleted = (item, id) => {
66216
+ const status = str2(item["status"]);
66217
+ switch (item["type"]) {
66218
+ case "agent_message": {
66219
+ const text = str2(item["text"]);
66220
+ return text ? [{ k: "text", id, delta: text }] : [];
66221
+ }
66222
+ case "reasoning":
66223
+ return [];
66224
+ case "command_execution": {
66225
+ const exitCode = num(item["exit_code"]);
66226
+ const ok = status !== "failed" && status !== "declined" && (exitCode ?? 0) === 0;
66227
+ return withCall(id, commandCall(item, id), [
66228
+ toolOutput(id, ok, str2(item["aggregated_output"]) ?? "", exitCode)
66229
+ ]);
66230
+ }
66231
+ case "file_change": {
66232
+ const changes = Array.isArray(item["changes"]) ? item["changes"] : [];
66233
+ const ok = status !== "failed";
66234
+ const events = [];
66235
+ changes.forEach((change, index) => {
66236
+ const path = str2(obj(change)?.["path"]);
66237
+ if (!path) return;
66238
+ const kind = str2(obj(change)?.["kind"]) ?? "update";
66239
+ const callId = changes.length === 1 ? id : `${id}:${index}`;
66240
+ events.push(
66241
+ {
66242
+ k: "tool_call",
66243
+ id: callId,
66244
+ kind: "edit",
66245
+ name: "file_change",
66246
+ title: `${kind} ${path}`,
66247
+ path
66248
+ },
66249
+ toolOutput(callId, ok, void 0, void 0)
66250
+ );
66251
+ });
66252
+ return events;
66253
+ }
66254
+ case "mcp_tool_call": {
66255
+ const error = str2(obj(item["error"])?.["message"]);
66256
+ const ok = status !== "failed" && error === void 0;
66257
+ return withCall(id, mcpCall(item, id), [
66258
+ toolOutput(id, ok, error ?? mcpResultText(item["result"]), void 0)
66259
+ ]);
66260
+ }
66261
+ case "web_search":
66262
+ return withCall(id, searchCall(item, id), [
66263
+ toolOutput(id, status !== "failed", void 0, void 0)
66264
+ ]);
66265
+ case "error": {
66266
+ const message = str2(item["message"]);
66267
+ return [{ k: "error", code: "codex_error", ...message ? { message } : {} }];
66268
+ }
66269
+ default: {
66270
+ const type = str2(item["type"]);
66271
+ return type ? [{ k: "other", name: type }] : [];
66272
+ }
66273
+ }
66274
+ };
66275
+ return {
66276
+ onLine(line) {
66277
+ const event = parseObject(line);
66278
+ if (!event) return [];
66279
+ const type = str2(event["type"]);
66280
+ if (!type) return [];
66281
+ switch (type) {
66282
+ case "thread.started": {
66283
+ const id = str2(event["thread_id"]);
66284
+ if (id) thread = id;
66285
+ return [];
66286
+ }
66287
+ case "turn.started":
66288
+ return [];
66289
+ case "turn.completed":
66290
+ ended = true;
66291
+ return [];
66292
+ case "turn.failed": {
66293
+ const message = str2(obj(event["error"])?.["message"]);
66294
+ return [{ k: "error", code: "turn_failed", ...message ? { message } : {} }];
66295
+ }
66296
+ case "error": {
66297
+ const message = str2(event["message"]);
66298
+ return [{ k: "error", code: "codex_error", ...message ? { message } : {} }];
66299
+ }
66300
+ case "item.started":
66301
+ case "item.updated":
66302
+ case "item.completed": {
66303
+ const item = obj(event["item"]);
66304
+ const id = str2(item?.["id"]);
66305
+ if (!item || !id) return [{ k: "other", name: type }];
66306
+ if (type === "item.completed") return onCompleted(item, id);
66307
+ if (type === "item.started") return onStarted(item, id);
66308
+ return [];
66309
+ }
66310
+ default:
66311
+ return [{ k: "other", name: type }];
66312
+ }
66313
+ },
66314
+ sessionId: () => thread,
66315
+ sawEnd: () => ended,
66316
+ killCode: () => null
66317
+ };
66318
+ }
66319
+ function codexHome(ctx) {
66320
+ const configured = ctx.env["CODEX_HOME"]?.trim();
66321
+ return configured ? configured : join20(ctx.home, ".codex");
66322
+ }
66323
+ async function rolloutFiles(root) {
66324
+ const found = [];
66325
+ const walk = async (dir, depth) => {
66326
+ let entries;
66327
+ try {
66328
+ entries = await readdir2(dir, { withFileTypes: true });
66329
+ } catch {
66330
+ return;
66331
+ }
66332
+ for (const entry of entries) {
66333
+ const path = join20(dir, entry.name);
66334
+ if (entry.isDirectory()) {
66335
+ if (depth < 4) await walk(path, depth + 1);
66336
+ } else if (entry.isFile() && ROLLOUT_FILE.test(entry.name)) {
66337
+ try {
66338
+ found.push({ path, mtimeMs: (await stat2(path)).mtimeMs });
66339
+ } catch {
66340
+ }
66341
+ }
66342
+ }
66343
+ };
66344
+ await walk(join20(root, "sessions"), 0);
66345
+ return found;
66346
+ }
66347
+ async function readLines(path, visit) {
66348
+ const stream = createReadStream(path, { encoding: "utf8" });
66349
+ const lines2 = createInterface2({ input: stream, crlfDelay: Infinity });
66350
+ try {
66351
+ for await (const text of lines2) {
66352
+ const line = parseObject(text);
66353
+ if (line && visit(line) === true) break;
66354
+ }
66355
+ } finally {
66356
+ lines2.close();
66357
+ stream.destroy();
66358
+ }
66359
+ }
66360
+ async function readHead(path) {
66361
+ const head = { id: null, cwd: null, firstPrompt: null };
66362
+ let seen = 0;
66363
+ await readLines(path, (line) => {
66364
+ seen += 1;
66365
+ const payload = obj(line["payload"]);
66366
+ if (line["type"] === "session_meta" && payload && head.id === null) {
66367
+ head.id = str2(payload["id"]) ?? null;
66368
+ head.cwd = str2(payload["cwd"]) ?? null;
66369
+ if (head.cwd === null) return true;
66370
+ } else if (line["type"] === "event_msg" && payload?.["type"] === "user_message") {
66371
+ head.firstPrompt = str2(payload["message"]) ?? null;
66372
+ if (head.firstPrompt !== null) return true;
66373
+ }
66374
+ return seen >= LIST_SCAN_LINES;
66375
+ });
66376
+ return head;
66377
+ }
66378
+ async function listCodexSessions(ctx, limit) {
66379
+ const files = await rolloutFiles(codexHome(ctx));
66380
+ files.sort((a, b) => b.mtimeMs - a.mtimeMs);
66381
+ const items = [];
66382
+ const seen = /* @__PURE__ */ new Set();
66383
+ for (const file of files) {
66384
+ if (items.length >= limit) break;
66385
+ let head;
66386
+ try {
66387
+ head = await readHead(file.path);
66388
+ } catch {
66389
+ continue;
66390
+ }
66391
+ if (head.id === null || head.cwd !== ctx.cwd || seen.has(head.id)) continue;
66392
+ seen.add(head.id);
66393
+ items.push({
66394
+ id: head.id,
66395
+ title: (head.firstPrompt ?? "").trim() || head.id,
66396
+ updatedAt: new Date(file.mtimeMs).toISOString()
66397
+ });
66398
+ }
66399
+ return items;
66400
+ }
66401
+ async function findRollout(ctx, sessionId) {
66402
+ const files = (await rolloutFiles(codexHome(ctx))).filter((file) => file.path.endsWith(`-${sessionId}.jsonl`)).sort((a, b) => b.mtimeMs - a.mtimeMs);
66403
+ for (const file of files) {
66404
+ const head = await readHead(file.path).catch(() => null);
66405
+ if (head?.id === sessionId && head.cwd === ctx.cwd) return file.path;
66406
+ }
66407
+ return null;
66408
+ }
66409
+ async function readCodexHistory(ctx, sessionId, budgetBytes) {
66410
+ const path = await findRollout(ctx, sessionId);
66411
+ if (path === null) return { unavailable: true };
66412
+ const turns = [];
66413
+ let turn = null;
66414
+ let ended = true;
66415
+ let texts = 0;
66416
+ const close = (reason) => {
66417
+ if (turn && !ended) turn.push({ k: "turn_end", reason });
66418
+ ended = true;
66419
+ };
66420
+ await readLines(path, (line) => {
66421
+ const payload = obj(line["payload"]);
66422
+ if (!payload) return;
66423
+ const kind = str2(payload["type"]);
66424
+ if (line["type"] === "event_msg") {
66425
+ if (kind === "user_message") {
66426
+ close("completed");
66427
+ turn = [{ k: "user", text: str2(payload["message"]) ?? "" }];
66428
+ turns.push(turn);
66429
+ ended = false;
66430
+ } else if (kind === "agent_message" && turn && !ended) {
66431
+ const text = str2(payload["message"]);
66432
+ if (text) turn.push({ k: "text", id: `history-${++texts}`, delta: text });
66433
+ } else if (kind === "turn_aborted") {
66434
+ close("stopped");
66435
+ }
66436
+ return;
66437
+ }
66438
+ if (line["type"] !== "response_item" || !turn || ended) return;
66439
+ const callId = str2(payload["call_id"]);
66440
+ if (!callId) return;
66441
+ if (kind === "function_call") {
66442
+ const args = parseObject(str2(payload["arguments"]) ?? "");
66443
+ const command = commandText(args?.["command"] ?? args?.["cmd"]);
66444
+ const name = str2(payload["name"]) ?? "function_call";
66445
+ turn.push(
66446
+ command !== void 0 ? { k: "tool_call", id: callId, kind: "command", name, title: command, command } : { k: "tool_call", id: callId, kind: "other", name, title: name }
66447
+ );
66448
+ } else if (kind === "custom_tool_call") {
66449
+ const name = str2(payload["name"]) ?? "custom_tool_call";
66450
+ const input = str2(payload["input"]);
66451
+ const target = input?.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/m)?.[1];
66452
+ turn.push(
66453
+ name === "apply_patch" && input !== void 0 ? {
66454
+ k: "tool_call",
66455
+ id: callId,
66456
+ kind: "edit",
66457
+ name,
66458
+ title: target ?? name,
66459
+ ...target ? { path: target } : {},
66460
+ diff: boundTail(input).text
66461
+ } : { k: "tool_call", id: callId, kind: "other", name, title: name }
66462
+ );
66463
+ } else if (kind === "function_call_output" || kind === "custom_tool_call_output") {
66464
+ const raw = payload["output"];
66465
+ const text = str2(raw) ?? str2(obj(raw)?.["content"]);
66466
+ const wrapped = text !== void 0 ? parseObject(text) : null;
66467
+ const output = str2(wrapped?.["output"]) ?? text;
66468
+ const exitLine = text?.match(/^Exit code: (-?\d+)$/m)?.[1];
66469
+ const exitCode = num(obj(wrapped?.["metadata"])?.["exit_code"]) ?? (exitLine !== void 0 ? Number(exitLine) : void 0);
66470
+ turn.push(toolOutput(callId, (exitCode ?? 0) === 0, output, exitCode));
66471
+ }
66472
+ });
66473
+ close("completed");
66474
+ const kept = [];
66475
+ let bytes = 0;
66476
+ for (let index = turns.length - 1; index >= 0; index--) {
66477
+ const events = turns[index];
66478
+ const size = Buffer.byteLength(JSON.stringify(events), "utf8");
66479
+ if (bytes + size > budgetBytes) break;
66480
+ bytes += size;
66481
+ kept.unshift(events);
66482
+ }
66483
+ return { events: kept.flat(), truncated: kept.length < turns.length };
66484
+ }
66485
+ function codexTurnCommand(input) {
66486
+ const args = input.sessionId === null ? ["exec", ...TURN_FLAGS, "-"] : ["exec", "resume", ...TURN_FLAGS, input.sessionId, "-"];
66487
+ return { file: "codex", args, stdin: input.prompt };
66488
+ }
66489
+ var codexChatAdapter = {
66490
+ profile: CODEX_PROFILE,
66491
+ // Q2: Codex chats on either sign-in. The runner's sign-in gate still applies.
66492
+ support: async () => ({ supported: true }),
66493
+ turnCommand: codexTurnCommand,
66494
+ createMapper: createCodexMapper,
66495
+ listSessions: listCodexSessions,
66496
+ readHistory: readCodexHistory
66497
+ };
66498
+
66499
+ // src/agentTerminal/chat/adapters/opencode.ts
66500
+ import { spawn as spawnChild } from "child_process";
66501
+ var OPENCODE_BINARY = "opencode";
66502
+ var OPENCODE_TURN_FLAGS = ["run", "--format", "json", "--auto"];
66503
+ var MAX_ERROR_MESSAGE_CHARS = 2e3;
66504
+ var HELPER_TIMEOUT_MS = 2e4;
66505
+ var HELPER_MAX_BYTES = 64 * 1024 * 1024;
66506
+ var execOpencode = (args, ctx) => new Promise((resolve8) => {
66507
+ let settled = false;
66508
+ const done = (value) => {
66509
+ if (settled) return;
66510
+ settled = true;
66511
+ clearTimeout(timer);
66512
+ resolve8(value);
66513
+ };
66514
+ let child;
66515
+ try {
66516
+ child = spawnChild(OPENCODE_BINARY, args, {
66517
+ cwd: ctx.cwd,
66518
+ // The app's tsconfig widens `ProcessEnv` with required keys (NODE_ENV).
66519
+ env: { ...ctx.env },
66520
+ stdio: ["ignore", "pipe", "pipe"]
66521
+ });
66522
+ } catch {
66523
+ resolve8(null);
66524
+ return;
66525
+ }
66526
+ const timer = setTimeout(() => {
66527
+ child.kill("SIGKILL");
66528
+ done(null);
66529
+ }, HELPER_TIMEOUT_MS);
66530
+ timer.unref();
66531
+ const chunks = [];
66532
+ let bytes = 0;
66533
+ child.stderr?.resume();
66534
+ child.stdout?.on("data", (chunk) => {
66535
+ bytes += chunk.length;
66536
+ if (bytes > HELPER_MAX_BYTES) {
66537
+ child.kill("SIGKILL");
66538
+ done(null);
66539
+ return;
66540
+ }
66541
+ chunks.push(chunk);
66542
+ });
66543
+ child.on("error", () => done(null));
66544
+ child.on("close", (code) => done(code === 0 ? Buffer.concat(chunks).toString("utf8") : null));
66545
+ });
66546
+ function isObject2(value) {
66547
+ return typeof value === "object" && value !== null && !Array.isArray(value);
66548
+ }
66549
+ function str3(value) {
66550
+ return typeof value === "string" ? value : void 0;
66551
+ }
66552
+ function num2(value) {
66553
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
66554
+ }
66555
+ function parseHelperJson(stdout, open2) {
66556
+ const start = stdout.indexOf(open2);
66557
+ if (start === -1) return void 0;
66558
+ try {
66559
+ return JSON.parse(stdout.slice(start));
66560
+ } catch {
66561
+ return void 0;
66562
+ }
66563
+ }
66564
+ function opencodeToolKind(tool) {
66565
+ switch (tool) {
66566
+ case "read":
66567
+ return "read";
66568
+ case "edit":
66569
+ case "write":
66570
+ case "patch":
66571
+ case "apply_patch":
66572
+ case "multiedit":
66573
+ return "edit";
66574
+ case "bash":
66575
+ return "command";
66576
+ default:
66577
+ return "other";
66578
+ }
66579
+ }
66580
+ function mapToolPart(part, seen, fallbackId) {
66581
+ const name = str3(part["tool"]) ?? "tool";
66582
+ const id = str3(part["callID"]) ?? str3(part["id"]) ?? fallbackId();
66583
+ const state = isObject2(part["state"]) ? part["state"] : {};
66584
+ const status = str3(state["status"]);
66585
+ const input = isObject2(state["input"]) ? state["input"] : {};
66586
+ const metadata = isObject2(state["metadata"]) ? state["metadata"] : {};
66587
+ const kind = opencodeToolKind(name);
66588
+ const events = [];
66589
+ if (!seen.calls.has(id)) {
66590
+ seen.calls.add(id);
66591
+ const path = str3(input["filePath"]) ?? str3(metadata["filepath"]) ?? str3(input["path"]);
66592
+ const command = kind === "command" ? str3(input["command"]) : void 0;
66593
+ const title = str3(state["title"]) || command || path || name;
66594
+ const call = {
66595
+ k: "tool_call",
66596
+ id,
66597
+ kind,
66598
+ name,
66599
+ title
66600
+ };
66601
+ if (path !== void 0 && kind !== "command") call.path = path;
66602
+ if (command !== void 0) call.command = command;
66603
+ const diff = kind === "edit" ? str3(metadata["diff"]) : void 0;
66604
+ if (diff) call.diff = boundTail(diff).text;
66605
+ events.push(call);
66606
+ }
66607
+ if ((status === "completed" || status === "error") && !seen.results.has(id)) {
66608
+ seen.results.add(id);
66609
+ if (status === "error") {
66610
+ const bounded = boundTail(str3(state["error"]) ?? "");
66611
+ events.push({
66612
+ k: "tool_result",
66613
+ id,
66614
+ ok: false,
66615
+ output: bounded.text,
66616
+ truncated: bounded.truncated
66617
+ });
66618
+ } else {
66619
+ const exitCode = kind === "command" ? num2(metadata["exit"]) : void 0;
66620
+ const output = str3(state["output"]) ?? (kind === "command" ? str3(metadata["output"]) : void 0);
66621
+ const result = {
66622
+ k: "tool_result",
66623
+ id,
66624
+ // A command that exited non-zero is drawn failed; anything else completed ran.
66625
+ ok: exitCode === void 0 || exitCode === 0,
66626
+ truncated: false
66627
+ };
66628
+ if (output !== void 0) {
66629
+ const bounded = boundTail(output);
66630
+ result.output = bounded.text;
66631
+ result.truncated = bounded.truncated;
66632
+ }
66633
+ if (exitCode !== void 0) result.exitCode = exitCode;
66634
+ events.push(result);
66635
+ }
66636
+ }
66637
+ return events;
66638
+ }
66639
+ function mapError(error) {
66640
+ const value = isObject2(error) ? error : {};
66641
+ const data = isObject2(value["data"]) ? value["data"] : {};
66642
+ const code = str3(value["name"]) || "error";
66643
+ const message = str3(data["message"]) ?? str3(value["message"]);
66644
+ const event = { k: "error", code };
66645
+ if (message) event.message = Array.from(message).slice(0, MAX_ERROR_MESSAGE_CHARS).join("");
66646
+ return event;
66647
+ }
66648
+ function createOpencodeMapper() {
66649
+ let session = null;
66650
+ let ended = false;
66651
+ let counter = 0;
66652
+ const seen = { calls: /* @__PURE__ */ new Set(), results: /* @__PURE__ */ new Set() };
66653
+ const texts = /* @__PURE__ */ new Set();
66654
+ const fallbackId = () => `opencode-${++counter}`;
66655
+ return {
66656
+ onLine(line) {
66657
+ let value;
66658
+ try {
66659
+ value = JSON.parse(line);
66660
+ } catch {
66661
+ return [];
66662
+ }
66663
+ if (!isObject2(value)) return [];
66664
+ const type = str3(value["type"]);
66665
+ if (!type) return [];
66666
+ const sessionID = str3(value["sessionID"]);
66667
+ if (session === null && sessionID !== void 0 && isSessionId(sessionID))
66668
+ session = sessionID;
66669
+ const part = isObject2(value["part"]) ? value["part"] : {};
66670
+ switch (type) {
66671
+ case "step_start":
66672
+ case "reasoning":
66673
+ return [];
66674
+ case "step_finish":
66675
+ if (str3(part["reason"]) === "stop") ended = true;
66676
+ return [];
66677
+ case "text": {
66678
+ const text = str3(part["text"]);
66679
+ if (!text) return [];
66680
+ const id = str3(part["id"]) ?? fallbackId();
66681
+ if (texts.has(id)) return [];
66682
+ texts.add(id);
66683
+ return [{ k: "text", id, delta: text }];
66684
+ }
66685
+ case "tool_use":
66686
+ return mapToolPart(part, seen, fallbackId).map(boundEvent);
66687
+ case "error":
66688
+ return [mapError(value["error"])];
66689
+ default:
66690
+ return [{ k: "other", name: type }];
66691
+ }
66692
+ },
66693
+ sessionId: () => session,
66694
+ sawEnd: () => ended,
66695
+ killCode: () => null
66696
+ };
66697
+ }
66698
+ function parseOpencodeSessionList(stdout, cwd, limit) {
66699
+ const value = parseHelperJson(stdout, "[");
66700
+ if (!Array.isArray(value)) return [];
66701
+ const rows = [];
66702
+ for (const entry of value) {
66703
+ if (!isObject2(entry)) continue;
66704
+ const id = str3(entry["id"]);
66705
+ const updated = num2(entry["updated"]) ?? num2(entry["created"]);
66706
+ if (!id || !isSessionId(id) || updated === void 0) continue;
66707
+ const directory = str3(entry["directory"]);
66708
+ if (directory !== void 0 && directory !== cwd) continue;
66709
+ rows.push({ id, title: str3(entry["title"]) || id, updated });
66710
+ }
66711
+ rows.sort((a, b) => b.updated - a.updated);
66712
+ return rows.slice(0, Math.max(0, limit)).map((row) => ({
66713
+ id: row.id,
66714
+ title: row.title,
66715
+ updatedAt: new Date(row.updated).toISOString()
66716
+ }));
66717
+ }
66718
+ function turnEndOf(info2) {
66719
+ const error = isObject2(info2["error"]) ? info2["error"] : null;
66720
+ if (!error) return "completed";
66721
+ return str3(error["name"]) === "MessageAbortedError" ? "stopped" : "failed";
66722
+ }
66723
+ function parseOpencodeExport(stdout, budgetBytes) {
66724
+ const value = parseHelperJson(stdout, "{");
66725
+ if (!isObject2(value) || !Array.isArray(value["messages"])) return null;
66726
+ const turns = [];
66727
+ let current = null;
66728
+ let end = "completed";
66729
+ let counter = 0;
66730
+ const seen = { calls: /* @__PURE__ */ new Set(), results: /* @__PURE__ */ new Set() };
66731
+ const fallbackId = () => `opencode-history-${++counter}`;
66732
+ const close = () => {
66733
+ if (current) {
66734
+ current.push({ k: "turn_end", reason: end });
66735
+ turns.push(current);
66736
+ }
66737
+ current = null;
66738
+ end = "completed";
66739
+ };
66740
+ for (const message of value["messages"]) {
66741
+ if (!isObject2(message)) continue;
66742
+ const info2 = isObject2(message["info"]) ? message["info"] : {};
66743
+ const parts = Array.isArray(message["parts"]) ? message["parts"] : [];
66744
+ const role = str3(info2["role"]);
66745
+ if (role === "user") {
66746
+ close();
66747
+ const text = parts.filter(isObject2).filter((part) => str3(part["type"]) === "text" && part["synthetic"] !== true).map((part) => str3(part["text"]) ?? "").join("\n");
66748
+ current = [{ k: "user", text }];
66749
+ } else if (role === "assistant") {
66750
+ current ??= [];
66751
+ end = turnEndOf(info2);
66752
+ for (const part of parts) {
66753
+ if (!isObject2(part)) continue;
66754
+ const type = str3(part["type"]);
66755
+ if (type === "text") {
66756
+ const text = str3(part["text"]);
66757
+ if (text) current.push({ k: "text", id: str3(part["id"]) ?? fallbackId(), delta: text });
66758
+ } else if (type === "tool") {
66759
+ current.push(...mapToolPart(part, seen, fallbackId).map(boundEvent));
66760
+ }
66761
+ }
66762
+ }
66763
+ }
66764
+ close();
66765
+ const kept = [];
66766
+ let bytes = 0;
66767
+ for (let index = turns.length - 1; index >= 0; index--) {
66768
+ const turn = turns[index];
66769
+ const size = turn.reduce(
66770
+ (sum, event) => sum + Buffer.byteLength(JSON.stringify(event), "utf8"),
66771
+ 0
66772
+ );
66773
+ if (bytes + size > budgetBytes) break;
66774
+ bytes += size;
66775
+ kept.unshift(turn);
66776
+ }
66777
+ return { events: kept.flat(), truncated: kept.length < turns.length };
66778
+ }
66779
+ function createOpencodeAdapter(exec = execOpencode) {
66780
+ return {
66781
+ profile: "opencode",
66782
+ // Q1: OpenCode streams, on any sign-in (Q2 names no term against it). The
66783
+ // terminal's sign-in check still gates each turn, in the runner.
66784
+ async support() {
66785
+ return { supported: true };
66786
+ },
66787
+ turnCommand({ prompt, sessionId }) {
66788
+ return {
66789
+ file: OPENCODE_BINARY,
66790
+ args: [
66791
+ ...OPENCODE_TURN_FLAGS,
66792
+ ...sessionId !== null ? ["--session", sessionId] : [],
66793
+ "--",
66794
+ prompt
66795
+ ],
66796
+ stdin: null
66797
+ };
66798
+ },
66799
+ createMapper: createOpencodeMapper,
66800
+ async listSessions(ctx, limit) {
66801
+ const stdout = await exec(["session", "list", "--format", "json"], ctx);
66802
+ return stdout === null ? [] : parseOpencodeSessionList(stdout, ctx.cwd, limit);
66803
+ },
66804
+ async readHistory(ctx, sessionId, budgetBytes) {
66805
+ if (!isSessionId(sessionId)) return { unavailable: true };
66806
+ const stdout = await exec(["export", sessionId], ctx);
66807
+ const history = stdout === null ? null : parseOpencodeExport(stdout, budgetBytes);
66808
+ return history ?? { unavailable: true };
66809
+ }
66810
+ };
66811
+ }
66812
+ var opencodeChatAdapter = createOpencodeAdapter();
66813
+
66814
+ // src/agentTerminal/chat/adapters/kimi.ts
66815
+ import { readFile } from "fs/promises";
66816
+ import { join as join21, resolve as resolve6 } from "path";
66817
+ var KIMI_BINARY = "kimi";
66818
+ var MAX_TITLE_CHARS2 = 120;
66819
+ var COMMAND_TOOLS = /* @__PURE__ */ new Set(["Bash", "Shell", "bash", "shell", "run_shell_command"]);
66820
+ var READ_TOOLS = /* @__PURE__ */ new Set(["ReadFile", "Read", "read_file", "ReadMediaFile"]);
66821
+ var EDIT_TOOLS2 = /* @__PURE__ */ new Set([
66822
+ "WriteFile",
66823
+ "StrReplaceFile",
66824
+ "Write",
66825
+ "Edit",
66826
+ "MultiEdit",
66827
+ "write_file",
66828
+ "str_replace_file"
66829
+ ]);
66830
+ function isObject3(value) {
66831
+ return typeof value === "object" && value !== null && !Array.isArray(value);
66832
+ }
66833
+ function str4(value) {
66834
+ return typeof value === "string" ? value : void 0;
66835
+ }
66836
+ function toolKind(name) {
66837
+ if (COMMAND_TOOLS.has(name)) return "command";
66838
+ if (READ_TOOLS.has(name)) return "read";
66839
+ if (EDIT_TOOLS2.has(name)) return "edit";
66840
+ return "other";
66841
+ }
66842
+ function parseArguments(value) {
66843
+ if (isObject3(value)) return value;
66844
+ if (typeof value !== "string") return {};
66845
+ try {
66846
+ const parsed = JSON.parse(value);
66847
+ return isObject3(parsed) ? parsed : {};
66848
+ } catch {
66849
+ return {};
66850
+ }
66851
+ }
66852
+ function contentText(content) {
66853
+ if (typeof content === "string") return content;
66854
+ if (!Array.isArray(content)) return "";
66855
+ let text = "";
66856
+ for (const part of content) {
66857
+ if (typeof part === "string") text += part;
66858
+ else if (isObject3(part) && part["type"] === "text") text += str4(part["text"]) ?? "";
66859
+ }
66860
+ return text;
66861
+ }
66862
+ function lines(text) {
66863
+ if (text.length === 0) return [];
66864
+ const out2 = text.split("\n");
66865
+ if (out2[out2.length - 1] === "") out2.pop();
66866
+ return out2;
66867
+ }
66868
+ function editDiff(args, path) {
66869
+ const file = path ?? "file";
66870
+ const hunks = [];
66871
+ const replacement = (edit) => {
66872
+ const oldText = str4(edit["old"]) ?? str4(edit["old_string"]) ?? str4(edit["old_str"]);
66873
+ const newText = str4(edit["new"]) ?? str4(edit["new_string"]) ?? str4(edit["new_str"]);
66874
+ if (oldText === void 0 || newText === void 0) return;
66875
+ hunks.push(
66876
+ ["@@ @@", ...lines(oldText).map((l) => `-${l}`), ...lines(newText).map((l) => `+${l}`)].join(
66877
+ "\n"
66878
+ )
66879
+ );
66880
+ };
66881
+ const edits = args["edit"] ?? args["edits"];
66882
+ if (Array.isArray(edits)) {
66883
+ for (const edit of edits) if (isObject3(edit)) replacement(edit);
66884
+ } else if (isObject3(edits)) {
66885
+ replacement(edits);
66886
+ } else {
66887
+ replacement(args);
66888
+ }
66889
+ const content = str4(args["content"]);
66890
+ if (hunks.length === 0 && content !== void 0) {
66891
+ hunks.push(["@@ @@", ...lines(content).map((l) => `+${l}`)].join("\n"));
66892
+ }
66893
+ if (hunks.length === 0) return void 0;
66894
+ return boundTail(`--- a/${file}
66895
+ +++ b/${file}
66896
+ ${hunks.join("\n")}
66897
+ `).text;
66898
+ }
66899
+ var SYSTEM_NOTE = /<system>([\s\S]*?)<\/system>/g;
66900
+ function toolOutcome(content) {
66901
+ const notes = [];
66902
+ const output = content.replace(SYSTEM_NOTE, (_match, note) => {
66903
+ notes.push(note);
66904
+ return "";
66905
+ });
66906
+ const failed = notes.some((note) => /^\s*ERROR\b/i.test(note));
66907
+ const exit = notes.map((note) => /exit code:?\s*(-?\d+)/i.exec(note)).find((m) => m != null);
66908
+ const shown = output.trim().length > 0 ? output : failed ? notes.map((n) => n.trim()).join("\n") : "";
66909
+ return { ok: !failed, output: shown, ...exit ? { exitCode: Number(exit[1]) } : {} };
66910
+ }
66911
+ function createKimiMapper() {
66912
+ let session = null;
66913
+ let ended = false;
66914
+ let messages = 0;
66915
+ const toolCall = (value) => {
66916
+ if (!isObject3(value)) return null;
66917
+ const id = str4(value["id"]);
66918
+ const fn = isObject3(value["function"]) ? value["function"] : null;
66919
+ const name = fn ? str4(fn["name"]) : void 0;
66920
+ if (!id || !name || !fn) return null;
66921
+ const args = parseArguments(fn["arguments"]);
66922
+ const kind = toolKind(name);
66923
+ const path = str4(args["path"]) ?? str4(args["file_path"]);
66924
+ if (kind === "command") {
66925
+ const command = str4(args["command"]) ?? "";
66926
+ return { k: "tool_call", id, kind, name, title: command || name, command };
66927
+ }
66928
+ if (kind === "read") {
66929
+ return { k: "tool_call", id, kind, name, title: path ?? name, ...path ? { path } : {} };
66930
+ }
66931
+ if (kind === "edit") {
66932
+ const diff = editDiff(args, path);
66933
+ return {
66934
+ k: "tool_call",
66935
+ id,
66936
+ kind,
66937
+ name,
66938
+ title: path ?? name,
66939
+ ...path ? { path } : {},
66940
+ ...diff !== void 0 ? { diff } : {}
66941
+ };
66942
+ }
66943
+ return { k: "tool_call", id, kind, name, title: name, ...path ? { path } : {} };
66944
+ };
66945
+ const onLine = (line) => {
66946
+ let parsed;
66947
+ try {
66948
+ parsed = JSON.parse(line);
66949
+ } catch {
66950
+ return [];
66951
+ }
66952
+ if (!isObject3(parsed)) return [];
66953
+ const role = str4(parsed["role"]);
66954
+ const type = str4(parsed["type"]);
66955
+ switch (role) {
66956
+ case "assistant": {
66957
+ const events = [];
66958
+ const text = contentText(parsed["content"]);
66959
+ if (text.length > 0) events.push({ k: "text", id: `kimi-msg-${++messages}`, delta: text });
66960
+ const toolCalls = parsed["tool_calls"];
66961
+ if (Array.isArray(toolCalls)) {
66962
+ for (const call of toolCalls) {
66963
+ const event = toolCall(call);
66964
+ if (event) events.push(event);
66965
+ }
66966
+ }
66967
+ return events;
66968
+ }
66969
+ case "tool": {
66970
+ const id = str4(parsed["tool_call_id"]);
66971
+ if (!id) return type ? [{ k: "other", name: type }] : [];
66972
+ const outcome = toolOutcome(contentText(parsed["content"]));
66973
+ const explicitError = parsed["is_error"] === true;
66974
+ const bounded = boundTail(outcome.output);
66975
+ return [
66976
+ {
66977
+ k: "tool_result",
66978
+ id,
66979
+ ok: outcome.ok && !explicitError,
66980
+ ...bounded.text.length > 0 ? { output: bounded.text } : {},
66981
+ ...outcome.exitCode !== void 0 ? { exitCode: outcome.exitCode } : {},
66982
+ truncated: bounded.truncated
66983
+ }
66984
+ ];
66985
+ }
66986
+ case "meta": {
66987
+ if (type === "session.resume_hint") {
66988
+ const id = str4(parsed["session_id"]);
66989
+ if (id) session = id;
66990
+ ended = true;
66991
+ return [];
66992
+ }
66993
+ if (type === "system.version") return [];
66994
+ return type ? [{ k: "other", name: type }] : [];
66995
+ }
66996
+ case "user":
66997
+ case "system":
66998
+ return [];
66999
+ default:
67000
+ return type ? [{ k: "other", name: type }] : [];
67001
+ }
67002
+ };
67003
+ return {
67004
+ onLine,
67005
+ sessionId: () => session,
67006
+ sawEnd: () => ended,
67007
+ killCode: () => null
67008
+ };
67009
+ }
67010
+ function kimiHome(ctx) {
67011
+ const configured = ctx.env["KIMI_CODE_HOME"]?.trim();
67012
+ return configured ? configured : join21(ctx.home, ".kimi-code");
67013
+ }
67014
+ function sessionIndexPaths(ctx) {
67015
+ const home = kimiHome(ctx);
67016
+ return [join21(home, "sessions", "session_index.jsonl"), join21(home, "session_index.jsonl")];
67017
+ }
67018
+ function firstString(row, names) {
67019
+ for (const name of names) {
67020
+ const value = str4(row[name]);
67021
+ if (value !== void 0 && value.length > 0) return value;
67022
+ }
67023
+ return void 0;
67024
+ }
67025
+ function parseTime(value) {
67026
+ if (typeof value === "number" && Number.isFinite(value)) {
67027
+ return value < 1e12 ? value * 1e3 : value;
67028
+ }
67029
+ if (typeof value === "string" && value.length > 0) {
67030
+ const numeric = Number(value);
67031
+ if (Number.isFinite(numeric)) return parseTime(numeric);
67032
+ const ms = Date.parse(value);
67033
+ return Number.isNaN(ms) ? null : ms;
67034
+ }
67035
+ return null;
67036
+ }
67037
+ function cutTitle2(title) {
67038
+ const chars = Array.from(title.replace(/\s+/g, " ").trim());
67039
+ return chars.length <= MAX_TITLE_CHARS2 ? chars.join("") : chars.slice(0, MAX_TITLE_CHARS2).join("");
67040
+ }
67041
+ function parseSessionIndex(text, cwd, limit) {
67042
+ const scope = resolve6(cwd);
67043
+ const latest = /* @__PURE__ */ new Map();
67044
+ for (const line of text.split("\n")) {
67045
+ if (line.trim().length === 0) continue;
67046
+ let row;
67047
+ try {
67048
+ row = JSON.parse(line);
67049
+ } catch {
67050
+ continue;
67051
+ }
67052
+ if (!isObject3(row)) continue;
67053
+ const id = firstString(row, ["session_id", "id"]);
67054
+ const dir = firstString(row, ["work_dir", "workdir", "cwd", "working_directory", "path"]);
67055
+ if (!id || !dir || resolve6(dir) !== scope) continue;
67056
+ const at = parseTime(row["updated_at"]) ?? parseTime(row["updated"]) ?? parseTime(row["last_updated"]) ?? parseTime(row["created_at"]);
67057
+ if (at === null) continue;
67058
+ const title = firstString(row, ["title", "first_prompt", "summary"]) ?? id;
67059
+ const previous = latest.get(id);
67060
+ if (!previous || at >= previous.at) latest.set(id, { title, at });
67061
+ }
67062
+ return [...latest.entries()].sort((a, b) => b[1].at - a[1].at).slice(0, Math.max(0, limit)).map(([id, { title, at }]) => ({
67063
+ id,
67064
+ title: cutTitle2(title),
67065
+ updatedAt: new Date(at).toISOString()
67066
+ }));
67067
+ }
67068
+ var kimiAdapter = {
67069
+ profile: "kimi",
67070
+ // Q1: supported. kimi's sign-in cannot be told by a file stat, so the
67071
+ // terminal's check reads `unknown`, which Q6 lets through; there is no
67072
+ // sign-in rule of Q2's kind for kimi.
67073
+ async support() {
67074
+ return { supported: true };
67075
+ },
67076
+ // Q3 exactly: the prompt on argv, no permission flag (`-p` is already auto),
67077
+ // no environment addition (Q11).
67078
+ turnCommand({ prompt, sessionId }) {
67079
+ return {
67080
+ file: KIMI_BINARY,
67081
+ args: [
67082
+ "-p",
67083
+ prompt,
67084
+ "--output-format",
67085
+ "stream-json",
67086
+ ...sessionId !== null ? ["--session", sessionId] : []
67087
+ ],
67088
+ stdin: null
67089
+ };
67090
+ },
67091
+ createMapper: createKimiMapper,
67092
+ async listSessions(ctx, limit) {
67093
+ for (const path of sessionIndexPaths(ctx)) {
67094
+ let text;
67095
+ try {
67096
+ text = await readFile(path, "utf8");
67097
+ } catch {
67098
+ continue;
67099
+ }
67100
+ return parseSessionIndex(text, ctx.cwd, limit);
67101
+ }
67102
+ return [];
67103
+ },
67104
+ async readHistory() {
67105
+ return { unavailable: true };
67106
+ }
67107
+ };
67108
+
67109
+ // src/agentTerminal/chat/adapters/goose.ts
67110
+ import { execFile as execFile3 } from "child_process";
67111
+ import { resolve as resolve7 } from "path";
67112
+ var GOOSE_BINARY = "goose";
67113
+ var GOOSE_RUN_ARGS = [
67114
+ "run",
67115
+ "-q",
67116
+ "--output-format",
67117
+ "stream-json",
67118
+ "-i",
67119
+ "-"
67120
+ ];
67121
+ var GOOSE_ENV = { GOOSE_MODE: "auto" };
67122
+ var GOOSE_INTERRUPTED = "Headless run interrupted";
67123
+ var LIST_TIMEOUT_MS = 1e4;
67124
+ var LIST_MAX_BYTES = 8 * 1024 * 1024;
67125
+ var GOOSE_TITLE_CHARS = 120;
67126
+ var MAX_OTHER_NAME = 64;
67127
+ function asObject(value) {
67128
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
67129
+ }
67130
+ function asString(value) {
67131
+ return typeof value === "string" ? value : void 0;
67132
+ }
67133
+ function firstString2(object, names) {
67134
+ for (const name of names) {
67135
+ const value = asString(object[name]);
67136
+ if (value !== void 0) return value;
67137
+ }
67138
+ return void 0;
67139
+ }
67140
+ function otherName(name) {
67141
+ return Array.from(name).slice(0, MAX_OTHER_NAME).join("");
67142
+ }
67143
+ function bareToolName(name) {
67144
+ const cut = name.lastIndexOf("__");
67145
+ return cut === -1 ? name : name.slice(cut + 2);
67146
+ }
67147
+ var COMMAND_TOOLS2 = /* @__PURE__ */ new Set(["shell", "bash", "run_command", "command"]);
67148
+ var EDIT_TOOLS3 = /* @__PURE__ */ new Set([
67149
+ "edit",
67150
+ "write",
67151
+ "create",
67152
+ "str_replace",
67153
+ "write_file",
67154
+ "edit_file",
67155
+ "insert"
67156
+ ]);
67157
+ var READ_TOOLS2 = /* @__PURE__ */ new Set(["view", "read", "read_file", "cat"]);
67158
+ function textLines(text) {
67159
+ if (text === "") return [];
67160
+ const lines2 = text.split("\n");
67161
+ if (lines2[lines2.length - 1] === "") lines2.pop();
67162
+ return lines2;
67163
+ }
67164
+ function unifiedDiff(path, before, after) {
67165
+ const removed = before === null ? [] : textLines(before);
67166
+ const added = textLines(after);
67167
+ const header = [
67168
+ `--- ${before === null ? "/dev/null" : path}`,
67169
+ `+++ ${path}`,
67170
+ `@@ -${removed.length === 0 ? 0 : 1},${removed.length} +${added.length === 0 ? 0 : 1},${added.length} @@`
67171
+ ];
67172
+ return [
67173
+ ...header,
67174
+ ...removed.map((line) => `-${line}`),
67175
+ ...added.map((line) => `+${line}`),
67176
+ ""
67177
+ ].join("\n");
67178
+ }
67179
+ function mapToolCall(id, rawName, args) {
67180
+ const name = bareToolName(rawName);
67181
+ const lower = name.toLowerCase();
67182
+ const path = firstString2(args, ["path", "file_path", "file"]);
67183
+ if (COMMAND_TOOLS2.has(lower)) {
67184
+ const command = firstString2(args, ["command", "cmd"]) ?? "";
67185
+ return { k: "tool_call", id, kind: "command", name, title: command || name, command };
67186
+ }
67187
+ const operation = lower === "text_editor" ? asString(args["command"]) ?? "" : lower;
67188
+ let kind = "other";
67189
+ if (READ_TOOLS2.has(operation)) kind = "read";
67190
+ else if (EDIT_TOOLS3.has(operation) || operation === "undo_edit") kind = "edit";
67191
+ const event = {
67192
+ k: "tool_call",
67193
+ id,
67194
+ kind,
67195
+ name,
67196
+ title: path ?? name,
67197
+ ...path !== void 0 && kind !== "other" ? { path } : {}
67198
+ };
67199
+ if (kind === "edit" && path !== void 0) {
67200
+ const whole = firstString2(args, ["file_text", "content"]);
67201
+ const before = firstString2(args, ["old_str", "old_string", "before"]);
67202
+ const after = firstString2(args, ["new_str", "new_string", "after"]);
67203
+ let diff;
67204
+ if (operation === "write" || operation === "create" || operation === "write_file") {
67205
+ if (whole !== void 0) diff = unifiedDiff(path, null, whole);
67206
+ } else if (operation === "insert") {
67207
+ if (after !== void 0) diff = unifiedDiff(path, "", after);
67208
+ } else if (before !== void 0 && after !== void 0) {
67209
+ diff = unifiedDiff(path, before, after);
67210
+ } else if (whole !== void 0) {
67211
+ diff = unifiedDiff(path, null, whole);
67212
+ }
67213
+ if (diff !== void 0) event.diff = boundTail(diff).text;
67214
+ }
67215
+ return event;
67216
+ }
67217
+ function mapToolResult(id, toolResult) {
67218
+ if (!toolResult || toolResult["status"] !== "success") {
67219
+ const error = toolResult ? asString(toolResult["error"]) : void 0;
67220
+ const bounded2 = boundTail(error ?? "");
67221
+ return {
67222
+ k: "tool_result",
67223
+ id,
67224
+ ok: false,
67225
+ ...error !== void 0 ? { output: bounded2.text } : {},
67226
+ truncated: bounded2.truncated
67227
+ };
67228
+ }
67229
+ const value = asObject(toolResult["value"]) ?? {};
67230
+ const structured = asObject(value["structuredContent"]);
67231
+ let output;
67232
+ let exitCode;
67233
+ if (structured) {
67234
+ const stdout = asString(structured["stdout"]);
67235
+ const stderr = asString(structured["stderr"]);
67236
+ if (stdout !== void 0 || stderr !== void 0) {
67237
+ output = [stdout, stderr].filter((part) => part !== void 0 && part !== "").join("\n");
67238
+ }
67239
+ const code = structured["exit_code"] ?? structured["exitCode"];
67240
+ if (typeof code === "number" && Number.isInteger(code)) exitCode = code;
67241
+ }
67242
+ if (output === void 0 && Array.isArray(value["content"])) {
67243
+ const texts = value["content"].map((part) => asObject(part)).filter((part) => part !== null && part["type"] === "text").map((part) => asString(part["text"]) ?? "");
67244
+ if (texts.length > 0) output = texts.join("\n");
67245
+ }
67246
+ const ok = value["isError"] !== true && (exitCode === void 0 || exitCode === 0);
67247
+ const bounded = output === void 0 ? null : boundTail(output);
67248
+ return {
67249
+ k: "tool_result",
67250
+ id,
67251
+ ok,
67252
+ ...bounded ? { output: bounded.text } : {},
67253
+ ...exitCode !== void 0 ? { exitCode } : {},
67254
+ truncated: bounded?.truncated ?? false
67255
+ };
67256
+ }
67257
+ var DROPPED_CONTENT = /* @__PURE__ */ new Set(["thinking", "redactedThinking", "reasoning"]);
67258
+ var DROPPED_EVENTS = /* @__PURE__ */ new Set(["notification", "model_change"]);
67259
+ function createGooseMapper() {
67260
+ let session = null;
67261
+ let complete = false;
67262
+ let textCounter = 0;
67263
+ let syntheticTextId = null;
67264
+ const textId = (message) => {
67265
+ const id = asString(message["id"]);
67266
+ if (id) return id;
67267
+ syntheticTextId ??= `goose-text-${++textCounter}`;
67268
+ return syntheticTextId;
67269
+ };
67270
+ const mapMessage = (message) => {
67271
+ const role = message["role"];
67272
+ const content = Array.isArray(message["content"]) ? message["content"] : [];
67273
+ const events = [];
67274
+ for (const raw of content) {
67275
+ const part = asObject(raw);
67276
+ const type = part ? asString(part["type"]) : void 0;
67277
+ if (!part || !type || DROPPED_CONTENT.has(type)) continue;
67278
+ if (type === "text") {
67279
+ if (role !== "assistant") continue;
67280
+ const delta = asString(part["text"]) ?? "";
67281
+ if (delta.length > 0) events.push({ k: "text", id: textId(message), delta });
67282
+ continue;
67283
+ }
67284
+ if (type === "toolRequest") {
67285
+ syntheticTextId = null;
67286
+ const id = asString(part["id"]) ?? `goose-tool-${++textCounter}`;
67287
+ const call = asObject(part["toolCall"]);
67288
+ const value = call && call["status"] === "success" ? asObject(call["value"]) : null;
67289
+ const name = value ? asString(value["name"]) : void 0;
67290
+ if (value && name) {
67291
+ events.push(mapToolCall(id, name, asObject(value["arguments"]) ?? {}));
67292
+ } else {
67293
+ events.push({ k: "tool_call", id, kind: "other", name: "tool", title: "tool" });
67294
+ }
67295
+ continue;
67296
+ }
67297
+ if (type === "toolResponse") {
67298
+ syntheticTextId = null;
67299
+ const id = asString(part["id"]) ?? "";
67300
+ events.push(mapToolResult(id, asObject(part["toolResult"])));
67301
+ continue;
67302
+ }
67303
+ events.push({ k: "other", name: otherName(type) });
67304
+ }
67305
+ return events;
67306
+ };
67307
+ return {
67308
+ onLine(line) {
67309
+ let parsed;
67310
+ try {
67311
+ parsed = JSON.parse(line);
67312
+ } catch {
67313
+ return [];
67314
+ }
67315
+ const event = asObject(parsed);
67316
+ if (!event) return [];
67317
+ const id = firstString2(event, ["session_id", "sessionId"]);
67318
+ if (id !== void 0 && isSessionId(id)) session = id;
67319
+ const type = asString(event["type"]);
67320
+ if (type === void 0) return [];
67321
+ switch (type) {
67322
+ case "message": {
67323
+ const message = asObject(event["message"]);
67324
+ return message ? mapMessage(message) : [];
67325
+ }
67326
+ case "complete":
67327
+ complete = true;
67328
+ return [];
67329
+ case "error": {
67330
+ const error = asString(event["error"]) ?? "";
67331
+ if (error === GOOSE_INTERRUPTED) return [];
67332
+ const bounded = boundTail(error, 4 * 1024);
67333
+ return [{ k: "error", code: "goose_error", ...error ? { message: bounded.text } : {} }];
67334
+ }
67335
+ default:
67336
+ if (DROPPED_EVENTS.has(type)) return [];
67337
+ return [{ k: "other", name: otherName(type) }];
67338
+ }
67339
+ },
67340
+ sessionId: () => session,
67341
+ sawEnd: () => complete,
67342
+ killCode: () => null
67343
+ };
67344
+ }
67345
+ var runGooseSessionList = (ctx) => new Promise((resolveList, reject) => {
67346
+ execFile3(
67347
+ GOOSE_BINARY,
67348
+ ["session", "list", "--format", "json"],
67349
+ {
67350
+ cwd: ctx.cwd,
67351
+ env: { ...ctx.env },
67352
+ timeout: LIST_TIMEOUT_MS,
67353
+ maxBuffer: LIST_MAX_BYTES,
67354
+ encoding: "utf8"
67355
+ },
67356
+ (error, stdout) => {
67357
+ if (error) reject(new Error("goose session list failed"));
67358
+ else resolveList(stdout);
67359
+ }
67360
+ );
67361
+ });
67362
+ function gooseTime(value) {
67363
+ let ms;
67364
+ if (typeof value === "number" && Number.isFinite(value)) {
67365
+ ms = value < 1e12 ? value * 1e3 : value;
67366
+ } else if (typeof value === "string" && value.trim() !== "") {
67367
+ let text = value.trim().replace(/ UTC$/, "Z");
67368
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/.test(text)) text = text.replace(" ", "T");
67369
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(text)) text += "Z";
67370
+ ms = Date.parse(text);
67371
+ } else {
67372
+ return null;
67373
+ }
67374
+ return Number.isNaN(ms) ? null : new Date(ms).toISOString();
67375
+ }
67376
+ function samePath(a, b) {
67377
+ const strip = (path) => resolve7(path).replace(/\/+$/, "");
67378
+ return strip(a) === strip(b);
67379
+ }
67380
+ function cutTitle3(title) {
67381
+ const chars = Array.from(title);
67382
+ return chars.length <= GOOSE_TITLE_CHARS ? title : chars.slice(0, GOOSE_TITLE_CHARS).join("");
67383
+ }
67384
+ function parseGooseSessionList(stdout, cwd, limit) {
67385
+ let parsed;
67386
+ try {
67387
+ parsed = JSON.parse(stdout);
67388
+ } catch {
67389
+ return [];
67390
+ }
67391
+ const rows = Array.isArray(parsed) ? parsed : Array.isArray(asObject(parsed)?.["sessions"]) ? asObject(parsed)?.["sessions"] : [];
67392
+ const sessions = [];
67393
+ for (const raw of rows) {
67394
+ const row = asObject(raw);
67395
+ if (!row) continue;
67396
+ const id = asString(row["id"]);
67397
+ if (id === void 0 || !isSessionId(id)) continue;
67398
+ const metadata = asObject(row["metadata"]) ?? {};
67399
+ const dir = firstString2(row, ["working_dir", "workingDir"]) ?? asString(metadata["working_dir"]);
67400
+ if (dir === void 0 || !samePath(dir, cwd)) continue;
67401
+ const updatedAt = gooseTime(row["updated_at"] ?? row["updatedAt"] ?? row["modified"]) ?? gooseTime(row["created_at"] ?? row["createdAt"]);
67402
+ if (updatedAt === null) continue;
67403
+ const title = firstString2(row, ["name", "description"]) ?? asString(metadata["description"]) ?? "";
67404
+ sessions.push({
67405
+ id,
67406
+ title: cutTitle3(title.trim() || id),
67407
+ updatedAt,
67408
+ at: Date.parse(updatedAt)
67409
+ });
67410
+ }
67411
+ sessions.sort((a, b) => b.at - a.at);
67412
+ return sessions.slice(0, Math.max(0, limit)).map(({ id, title, updatedAt }) => ({
67413
+ id,
67414
+ title,
67415
+ updatedAt
67416
+ }));
67417
+ }
67418
+ function createGooseAdapter(options = {}) {
67419
+ const list = options.listSessions ?? runGooseSessionList;
67420
+ return {
67421
+ profile: "goose",
67422
+ // Q1: goose's stream is supported. Its sign-in cannot be told by a file
67423
+ // stat, so the terminal's check answers `unknown`, which Q6 lets through.
67424
+ support: async () => ({ supported: true }),
67425
+ turnCommand({ prompt, sessionId }) {
67426
+ return {
67427
+ file: GOOSE_BINARY,
67428
+ args: [...GOOSE_RUN_ARGS, ...sessionId ? ["--resume", "--session-id", sessionId] : []],
67429
+ stdin: prompt,
67430
+ env: { ...GOOSE_ENV }
67431
+ };
67432
+ },
67433
+ createMapper: createGooseMapper,
67434
+ async listSessions(ctx, limit) {
67435
+ return parseGooseSessionList(await list(ctx), ctx.cwd, limit);
67436
+ },
67437
+ // Q7: goose keeps its messages in `sessions.db`, whose schema it does not
67438
+ // document and whose CLI has no documented machine-readable export, so the
67439
+ // earlier turns are not drawn. The agent still has them on resume.
67440
+ readHistory: async () => ({ unavailable: true })
67441
+ };
67442
+ }
67443
+ var gooseChatAdapter = createGooseAdapter();
67444
+
67445
+ // src/agentTerminal/chat/adapter.ts
67446
+ var MAX_LISTED_SESSIONS = 50;
67447
+ var MAX_TITLE_CHARS3 = 120;
67448
+ var HISTORY_BUDGET_BYTES = 256 * 1024;
67449
+ var CHAT_ENV_ADDITIONS = /* @__PURE__ */ new Set(["GOOSE_MODE"]);
67450
+ var UNSUPPORTED2 = { supported: false, code: "unsupported" };
67451
+ var CHAT_ADAPTERS = [
67452
+ claudeChatAdapter,
67453
+ codexChatAdapter,
67454
+ opencodeChatAdapter,
67455
+ kimiAdapter,
67456
+ gooseChatAdapter
67457
+ ];
67458
+ function resolveChatAdapter(profile, adapters = CHAT_ADAPTERS) {
67459
+ if (!profile) return null;
67460
+ return adapters.find((adapter) => adapter.profile === profile) ?? null;
67461
+ }
67462
+
67463
+ // src/agentTerminal/chat/turns.ts
67464
+ import { spawn as spawnChild2 } from "child_process";
67465
+ import { StringDecoder } from "string_decoder";
67466
+ var STOP_GRACE_MS = 5e3;
67467
+ var TURN_RING_BYTES = 256 * 1024;
67468
+ var MAX_LINE_CHARS = 8 * 1024 * 1024;
67469
+ var BARE_BINARY = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;
67470
+ var spawnChatProcess = (options) => {
67471
+ const child = spawnChild2(options.file, options.args, {
67472
+ cwd: options.cwd,
67473
+ // The app's tsconfig widens `ProcessEnv` with required keys (NODE_ENV);
67474
+ // the CLI's does not. The record is a whole environment either way.
67475
+ env: options.env,
67476
+ detached: true,
67477
+ stdio: [options.stdin === null ? "ignore" : "pipe", "pipe", "pipe"]
67478
+ });
67479
+ child.stderr?.resume();
67480
+ if (options.stdin !== null && child.stdin) {
67481
+ child.stdin.on("error", () => {
67482
+ });
67483
+ child.stdin.end(options.stdin);
67484
+ }
67485
+ const listeners = [];
67486
+ let exited = null;
67487
+ const finish = (exit) => {
67488
+ if (exited) return;
67489
+ exited = exit;
67490
+ for (const listener of listeners) listener(exit);
67491
+ };
67492
+ child.on("error", () => {
67493
+ finish({ code: null, signal: null, failedToStart: child.pid === void 0 });
67494
+ });
67495
+ child.on("close", (code, signal) => finish({ code, signal }));
67496
+ return {
67497
+ pid: child.pid ?? -1,
67498
+ onStdout: (listener) => {
67499
+ child.stdout?.on("data", listener);
67500
+ },
67501
+ onExit: (listener) => {
67502
+ if (exited) listener(exited);
67503
+ else listeners.push(listener);
67504
+ },
67505
+ signal: (signal) => {
67506
+ if (child.pid === void 0 || exited) return;
67507
+ try {
67508
+ process.kill(-child.pid, signal);
67509
+ } catch {
67510
+ }
67511
+ }
67512
+ };
67513
+ };
67514
+ var EventRing = class {
67515
+ constructor(capacity = TURN_RING_BYTES) {
67516
+ this.capacity = capacity;
67517
+ }
67518
+ capacity;
67519
+ frames = [];
67520
+ bytes = 0;
67521
+ get size() {
67522
+ return this.bytes;
67523
+ }
67524
+ push(frame) {
67525
+ this.frames.push(frame);
67526
+ this.bytes += Buffer.byteLength(frame, "utf8");
67527
+ while (this.bytes > this.capacity && this.frames.length > 0) {
67528
+ this.bytes -= Buffer.byteLength(this.frames.shift(), "utf8");
67529
+ }
67530
+ }
67531
+ /** The held frames, oldest first. */
67532
+ snapshot() {
67533
+ return [...this.frames];
67534
+ }
67535
+ };
67536
+ function cutTitle4(title) {
67537
+ const chars = Array.from(title);
67538
+ return chars.length <= MAX_TITLE_CHARS3 ? title : chars.slice(0, MAX_TITLE_CHARS3).join("");
67539
+ }
67540
+ function createChatHub(options) {
67541
+ const { adapter, ctx, log } = options;
67542
+ const now = options.now ?? Date.now;
67543
+ const stopGraceMs = options.stopGraceMs ?? STOP_GRACE_MS;
67544
+ const ringBytes = options.ringBytes ?? TURN_RING_BYTES;
67545
+ const connections = /* @__PURE__ */ new Set();
67546
+ const views = /* @__PURE__ */ new Map();
67547
+ let current = null;
67548
+ let turnCounter = 0;
67549
+ let closing = false;
67550
+ const send = (connection, frame) => {
67551
+ if (connection.socket.isOpen) connection.socket.send(encodeChatServerFrame(frame));
67552
+ };
67553
+ const refuse = (connection, code, what) => {
67554
+ send(connection, { t: "error", code });
67555
+ log(`agent-chat: ${what} refused (${code})`);
67556
+ };
67557
+ const support = async () => {
67558
+ if (!adapter) return UNSUPPORTED2;
67559
+ try {
67560
+ return await adapter.support(ctx);
67561
+ } catch {
67562
+ return UNSUPPORTED2;
67563
+ }
67564
+ };
67565
+ const readSignIn = async () => {
67566
+ try {
67567
+ return await options.readSignIn();
67568
+ } catch {
67569
+ return { profile: options.profile, state: "unknown" };
67570
+ }
67571
+ };
67572
+ const forget = (view) => {
67573
+ if (view.holder || view.turn || view.sessionId === null) return;
67574
+ if (views.get(view.sessionId) === view) views.delete(view.sessionId);
67575
+ };
67576
+ const release = (connection) => {
67577
+ const view = connection.view;
67578
+ connection.view = null;
67579
+ if (!view || view.holder !== connection) return;
67580
+ view.holder = null;
67581
+ view.live = false;
67582
+ forget(view);
67583
+ };
67584
+ const emit = (turn, event) => {
67585
+ const frame = encodeChatServerFrame({ t: "event", turn: turn.id, e: boundEvent(event) });
67586
+ turn.ring.push(frame);
67587
+ const holder = turn.view.holder;
67588
+ if (holder && turn.view.live && holder.socket.isOpen) holder.socket.send(frame);
67589
+ };
67590
+ const revealSession = (turn) => {
67591
+ const view = turn.view;
67592
+ let id;
67593
+ try {
67594
+ id = turn.mapper?.sessionId() ?? null;
67595
+ } catch {
67596
+ id = null;
67597
+ }
67598
+ if (id === null || id === view.sessionId || !isSessionId(id)) return;
67599
+ if (view.sessionId !== null && views.get(view.sessionId) === view) views.delete(view.sessionId);
67600
+ view.sessionId = id;
67601
+ views.set(id, view);
67602
+ const holder = view.holder;
67603
+ if (holder && view.live) send(holder, { t: "session", id });
67604
+ };
67605
+ const endTurn = (turn, exit) => {
67606
+ if (turn.ended) return;
67607
+ turn.ended = true;
67608
+ if (turn.killTimer) clearTimeout(turn.killTimer);
67609
+ turn.killTimer = null;
67610
+ let sawEnd = false;
67611
+ try {
67612
+ sawEnd = turn.mapper?.sawEnd() ?? false;
67613
+ } catch {
67614
+ sawEnd = false;
67615
+ }
67616
+ let end;
67617
+ if (turn.stopRequested) {
67618
+ end = { k: "turn_end", reason: "stopped" };
67619
+ } else if (turn.failCode) {
67620
+ end = { k: "turn_end", reason: "failed", code: turn.failCode };
67621
+ } else if (exit.failedToStart) {
67622
+ end = { k: "turn_end", reason: "failed", code: "spawn_failed" };
67623
+ } else if (exit.code === 0 && sawEnd) {
67624
+ end = { k: "turn_end", reason: "completed" };
67625
+ } else if (exit.code === 0) {
67626
+ end = { k: "turn_end", reason: "failed", code: "no_end" };
67627
+ } else {
67628
+ end = { k: "turn_end", reason: "failed", code: "exit_nonzero" };
67629
+ }
67630
+ emit(turn, end);
67631
+ if (current === turn) current = null;
67632
+ const view = turn.view;
67633
+ if (view.turn === turn) view.turn = null;
67634
+ forget(view);
67635
+ const code = end.code ? ` ${end.code}` : "";
67636
+ log(`agent-chat: turn ${turn.id} ended (${end.reason}${code}, ${now() - turn.startedAt} ms)`);
67637
+ };
67638
+ const onLine = (turn, line) => {
67639
+ if (turn.ended || !turn.mapper || turn.failCode) return;
67640
+ let events;
67641
+ try {
67642
+ events = turn.mapper.onLine(line);
67643
+ } catch {
67644
+ events = [];
67645
+ }
67646
+ revealSession(turn);
67647
+ for (const event of events) {
67648
+ if (event.k !== "turn_end") emit(turn, event);
67649
+ }
67650
+ let killCode = null;
67651
+ try {
67652
+ killCode = turn.mapper.killCode();
67653
+ } catch {
67654
+ killCode = null;
67655
+ }
67656
+ if (killCode && !turn.failCode) {
67657
+ turn.failCode = killCode;
67658
+ turn.proc?.signal("SIGKILL");
67659
+ log(`agent-chat: turn ${turn.id} killed by its adapter (${killCode})`);
67660
+ }
67661
+ };
67662
+ const turnEnv = (additions) => {
67663
+ const env = { ...ctx.env };
67664
+ for (const [name, value] of Object.entries(additions ?? {})) {
67665
+ if (!CHAT_ENV_ADDITIONS.has(name)) throw new Error("env addition refused");
67666
+ env[name] = value;
67667
+ }
67668
+ delete env["MOTIR_TERMINAL_KEY"];
67669
+ return env;
67670
+ };
67671
+ const startTurn = (view, prompt) => {
67672
+ const turn = {
67673
+ id: ++turnCounter,
67674
+ view,
67675
+ proc: null,
67676
+ mapper: null,
67677
+ ring: new EventRing(ringBytes),
67678
+ startedAt: now(),
67679
+ stopRequested: false,
67680
+ failCode: null,
67681
+ killTimer: null,
67682
+ ended: false
67683
+ };
67684
+ current = turn;
67685
+ view.turn = turn;
67686
+ emit(turn, { k: "user", text: prompt });
67687
+ let proc;
67688
+ try {
67689
+ const command = adapter.turnCommand({
67690
+ prompt,
67691
+ sessionId: view.sessionId,
67692
+ ctx
67693
+ });
67694
+ if (!BARE_BINARY.test(command.file)) throw new Error("binary refused");
67695
+ const env = turnEnv(command.env);
67696
+ turn.mapper = adapter.createMapper();
67697
+ revealSession(turn);
67698
+ proc = options.spawn({
67699
+ file: command.file,
67700
+ args: [...command.args],
67701
+ cwd: ctx.cwd,
67702
+ env,
67703
+ stdin: command.stdin
67704
+ });
67705
+ } catch {
67706
+ log(`agent-chat: turn ${turn.id} failed to start`);
67707
+ endTurn(turn, { code: null, signal: null, failedToStart: true });
67708
+ return;
67709
+ }
67710
+ turn.proc = proc;
67711
+ const decoder = new StringDecoder("utf8");
67712
+ let partial = "";
67713
+ const feed = (text) => {
67714
+ partial += text;
67715
+ let newline = partial.indexOf("\n");
67716
+ while (newline !== -1) {
67717
+ const line = partial.slice(0, newline).replace(/\r$/, "");
67718
+ partial = partial.slice(newline + 1);
67719
+ if (line.length > 0) onLine(turn, line);
67720
+ newline = partial.indexOf("\n");
67721
+ }
67722
+ if (partial.length > MAX_LINE_CHARS) partial = "";
67723
+ };
67724
+ proc.onStdout((chunk) => feed(decoder.write(chunk)));
67725
+ proc.onExit((exit) => {
67726
+ feed(decoder.end());
67727
+ if (partial.length > 0) onLine(turn, partial);
67728
+ partial = "";
67729
+ endTurn(turn, exit);
67730
+ });
67731
+ log(`agent-chat: turn ${turn.id} started (pid ${proc.pid})`);
67732
+ if (closing) {
67733
+ turn.failCode = "shutdown";
67734
+ proc.signal("SIGKILL");
67735
+ }
67736
+ };
67737
+ const signInFor = async (verdict) => verdict.supported && verdict.signedIn ? "signed_in" : (await readSignIn()).state;
67738
+ const hello = async (connection) => {
67739
+ const verdict = await support();
67740
+ send(connection, {
67741
+ t: "hello",
67742
+ profile: options.profile,
67743
+ supported: verdict.supported,
67744
+ ...verdict.supported ? {} : { reason: verdict.code },
67745
+ signin: await signInFor(verdict)
67746
+ });
67747
+ };
67748
+ const onList = async (connection) => {
67749
+ if (!adapter) return refuse(connection, "unsupported", "list");
67750
+ let items;
67751
+ try {
67752
+ items = await adapter.listSessions(ctx, MAX_LISTED_SESSIONS);
67753
+ } catch {
67754
+ items = [];
67755
+ }
67756
+ send(connection, {
67757
+ t: "sessions",
67758
+ items: items.slice(0, MAX_LISTED_SESSIONS).map((item) => ({
67759
+ id: item.id,
67760
+ title: cutTitle4(item.title),
67761
+ updatedAt: item.updatedAt
67762
+ }))
67763
+ });
67764
+ };
67765
+ const onOpen = async (connection, session) => {
67766
+ if (!adapter) return refuse(connection, "unsupported", "open");
67767
+ if (session !== void 0 && !isSessionId(session)) {
67768
+ return refuse(connection, "unknown_session", "open");
67769
+ }
67770
+ release(connection);
67771
+ if (session === void 0) {
67772
+ const view2 = { sessionId: null, holder: connection, live: true, turn: null };
67773
+ connection.view = view2;
67774
+ send(connection, { t: "ready", session: null, resumed: false });
67775
+ log("agent-chat: chat opened");
67776
+ return;
67777
+ }
67778
+ let view = views.get(session);
67779
+ if (!view) {
67780
+ view = { sessionId: session, holder: null, live: false, turn: null };
67781
+ views.set(session, view);
67782
+ }
67783
+ const previous = view.holder;
67784
+ if (previous && previous !== connection) {
67785
+ previous.view = null;
67786
+ send(previous, { t: "error", code: "taken_over" });
67787
+ log("agent-chat: chat taken over");
67788
+ }
67789
+ view.holder = connection;
67790
+ view.live = false;
67791
+ connection.view = view;
67792
+ send(connection, { t: "ready", session, resumed: true });
67793
+ let history;
67794
+ try {
67795
+ history = await adapter.readHistory(ctx, session, HISTORY_BUDGET_BYTES);
67796
+ } catch {
67797
+ history = { unavailable: true };
67798
+ }
67799
+ if (view.holder !== connection) return;
67800
+ send(
67801
+ connection,
67802
+ "unavailable" in history ? { t: "history", events: [], truncated: true } : {
67803
+ t: "history",
67804
+ events: history.events.map(boundEvent),
67805
+ truncated: history.truncated
67806
+ }
67807
+ );
67808
+ if (view.turn && connection.socket.isOpen) {
67809
+ for (const frame of view.turn.ring.snapshot()) connection.socket.send(frame);
67810
+ }
67811
+ view.live = true;
67812
+ log(`agent-chat: chat resumed${view.turn ? ` (turn ${view.turn.id} running)` : ""}`);
67813
+ };
67814
+ const onPrompt = async (connection, prompt) => {
67815
+ const view = connection.view;
67816
+ if (!view) return refuse(connection, "bad_frame", "prompt");
67817
+ const verdict = await support();
67818
+ if (!verdict.supported) return refuse(connection, verdict.code, "prompt");
67819
+ if (await signInFor(verdict) === "signed_out") {
67820
+ return refuse(connection, "not_signed_in", "prompt");
67821
+ }
67822
+ if (current) return refuse(connection, "turn_running", "prompt");
67823
+ if (closing || connection.view !== view) return;
67824
+ startTurn(view, prompt);
67825
+ };
67826
+ const onStop = (connection) => {
67827
+ const turn = current;
67828
+ if (!turn || turn.ended || turn.view !== connection.view || !turn.proc) {
67829
+ return refuse(connection, "no_turn", "stop");
67830
+ }
67831
+ if (turn.stopRequested) return;
67832
+ turn.stopRequested = true;
67833
+ log(`agent-chat: turn ${turn.id} stopping`);
67834
+ turn.proc.signal("SIGINT");
67835
+ const proc = turn.proc;
67836
+ turn.killTimer = setTimeout(() => {
67837
+ turn.killTimer = null;
67838
+ if (turn.ended) return;
67839
+ proc.signal("SIGKILL");
67840
+ log(`agent-chat: turn ${turn.id} killed (stop grace elapsed)`);
67841
+ }, stopGraceMs);
67842
+ turn.killTimer.unref();
67843
+ };
67844
+ const enqueue = (connection, task) => {
67845
+ connection.queue = connection.queue.then(task).catch(() => log("agent-chat: frame handling failed"));
67846
+ };
67847
+ return {
67848
+ accept(socket) {
67849
+ const connection = { socket, view: null, queue: Promise.resolve() };
67850
+ connections.add(connection);
67851
+ log("agent-chat: connection opened");
67852
+ enqueue(connection, () => hello(connection));
67853
+ return {
67854
+ message(data, isBinary) {
67855
+ const parsed = isBinary ? { ok: false, code: "bad_frame" } : parseChatClientFrame(data.toString("utf8"));
67856
+ if (!parsed.ok) {
67857
+ enqueue(connection, () => refuse(connection, parsed.code, "frame"));
67858
+ return;
67859
+ }
67860
+ const frame = parsed.frame;
67861
+ switch (frame.t) {
67862
+ case "ping":
67863
+ enqueue(connection, () => send(connection, { t: "pong" }));
67864
+ return;
67865
+ case "list":
67866
+ enqueue(connection, () => onList(connection));
67867
+ return;
67868
+ case "open":
67869
+ enqueue(connection, () => onOpen(connection, frame.session));
67870
+ return;
67871
+ case "prompt":
67872
+ enqueue(connection, () => onPrompt(connection, frame.text));
67873
+ return;
67874
+ case "stop":
67875
+ enqueue(connection, () => onStop(connection));
67876
+ return;
67877
+ }
67878
+ },
67879
+ closed() {
67880
+ connections.delete(connection);
67881
+ release(connection);
67882
+ }
67883
+ };
67884
+ },
67885
+ pushSignIn(status) {
67886
+ for (const connection of connections) {
67887
+ send(connection, { t: "signin", profile: status.profile, state: status.state });
67888
+ }
67889
+ },
67890
+ runningTurn: () => current?.id ?? null,
67891
+ connectionCount: () => connections.size,
67892
+ close() {
67893
+ closing = true;
67894
+ const turn = current;
67895
+ if (turn && !turn.ended) {
67896
+ turn.failCode ??= "shutdown";
67897
+ if (turn.killTimer) clearTimeout(turn.killTimer);
67898
+ turn.killTimer = null;
67899
+ turn.proc?.signal("SIGKILL");
67900
+ }
67901
+ connections.clear();
67902
+ }
67903
+ };
67904
+ }
65504
67905
 
65505
67906
  // src/agentTerminal/outputRing.ts
65506
67907
  var REPLAY_BYTES = 256 * 1024;
@@ -65665,24 +68066,31 @@ var NonceMemory = class {
65665
68066
  // src/agentTerminal/signIn.ts
65666
68067
  import { stat as fsStat } from "fs/promises";
65667
68068
  import { homedir as homedir5 } from "os";
65668
- import { join as join19 } from "path";
68069
+ import { join as join22 } from "path";
65669
68070
  function credentialDirsFromEnv(env, home = env["HOME"] || homedir5()) {
65670
68071
  return {
65671
68072
  home,
65672
- xdgConfigHome: env["XDG_CONFIG_HOME"] || join19(home, ".config"),
65673
- xdgDataHome: env["XDG_DATA_HOME"] || join19(home, ".local", "share"),
68073
+ xdgConfigHome: env["XDG_CONFIG_HOME"] || join22(home, ".config"),
68074
+ xdgDataHome: env["XDG_DATA_HOME"] || join22(home, ".local", "share"),
65674
68075
  configHome: (name) => env[name] || void 0
65675
68076
  };
65676
68077
  }
65677
- async function checkSignIn(env, stat = fsStat) {
68078
+ function hasEnv(env, name) {
68079
+ const value = env[name];
68080
+ return typeof value === "string" && value.length > 0;
68081
+ }
68082
+ async function checkSignIn(env, stat3 = fsStat) {
65678
68083
  const id = env["MOTIR_SANDBOX_AGENT"]?.trim() || null;
65679
68084
  const profile = id ? AGENT_PROFILES.find((candidate) => candidate.id === id) : void 0;
65680
68085
  if (!profile) return { profile: id, state: "unknown" };
68086
+ if (profile.credentialEnv.some((name) => hasEnv(env, name))) {
68087
+ return { profile: id, state: "signed_in" };
68088
+ }
65681
68089
  const paths = profile.credentialPaths(credentialDirsFromEnv(env));
65682
68090
  if (paths.length === 0) return { profile: id, state: "unknown" };
65683
68091
  for (const path of paths) {
65684
68092
  try {
65685
- const info2 = await stat(path);
68093
+ const info2 = await stat3(path);
65686
68094
  if (info2.isFile() && info2.size > 0) return { profile: id, state: "signed_in" };
65687
68095
  } catch {
65688
68096
  }
@@ -65929,9 +68337,17 @@ function shellEnv(env) {
65929
68337
  out2["COLORTERM"] = "truecolor";
65930
68338
  return out2;
65931
68339
  }
68340
+ function chatEnv(env) {
68341
+ const out2 = {};
68342
+ for (const [name, value] of Object.entries(env)) {
68343
+ if (name === TERMINAL_KEY_ENV || value === void 0) continue;
68344
+ out2[name] = value;
68345
+ }
68346
+ return out2;
68347
+ }
65932
68348
  function shellCwd(env) {
65933
68349
  const home = env["HOME"] || "/";
65934
- const workspace = join20(home, "workspace");
68350
+ const workspace = join23(home, "workspace");
65935
68351
  try {
65936
68352
  if (existsSync15(workspace) && statSync3(workspace).isDirectory()) return workspace;
65937
68353
  } catch {
@@ -65957,6 +68373,23 @@ function createTerminalServer(options) {
65957
68373
  let signInTimer = null;
65958
68374
  let lastSignIn = null;
65959
68375
  let closing = false;
68376
+ const profile = options.env["MOTIR_SANDBOX_AGENT"]?.trim() || null;
68377
+ const chatContext = {
68378
+ home: options.env["HOME"] || "/",
68379
+ cwd: shellCwd(options.env),
68380
+ env: chatEnv(options.env)
68381
+ };
68382
+ const chat = createChatHub({
68383
+ profile,
68384
+ adapter: resolveChatAdapter(profile, options.chatAdapters),
68385
+ ctx: chatContext,
68386
+ spawn: options.spawnChat ?? spawnChatProcess,
68387
+ readSignIn: () => readSignIn(),
68388
+ log,
68389
+ now,
68390
+ ...options.chatStopGraceMs !== void 0 ? { stopGraceMs: options.chatStopGraceMs } : {}
68391
+ });
68392
+ const chatSockets = /* @__PURE__ */ new Set();
65960
68393
  const send = (attachment, frame) => {
65961
68394
  if (attachment.ws.isOpen) attachment.ws.send(encodeServerFrame(frame));
65962
68395
  };
@@ -65974,9 +68407,10 @@ function createTerminalServer(options) {
65974
68407
  if (closing || sameState(lastSignIn, status)) return;
65975
68408
  lastSignIn = status;
65976
68409
  pushSignIn(status, attachedConnections());
68410
+ chat.pushSignIn(status);
65977
68411
  };
65978
68412
  const syncSignInTimer = () => {
65979
- const wanted = attachedCount() > 0 && !closing;
68413
+ const wanted = (attachedCount() > 0 || chatSockets.size > 0) && !closing;
65980
68414
  if (wanted && signInTimer === null) {
65981
68415
  signInTimer = setInterval(() => void pollSignIn(), signInIntervalMs);
65982
68416
  signInTimer.unref();
@@ -66053,7 +68487,7 @@ function createTerminalServer(options) {
66053
68487
  };
66054
68488
  const registerSession = (pty, kind, runId, onExit) => {
66055
68489
  const session = {
66056
- id: randomUUID(),
68490
+ id: randomUUID2(),
66057
68491
  kind,
66058
68492
  ...runId === void 0 ? {} : { runId },
66059
68493
  pty,
@@ -66145,8 +68579,8 @@ function createTerminalServer(options) {
66145
68579
  return { ok: false, code: "spawn_failed" };
66146
68580
  }
66147
68581
  let markExited = () => void 0;
66148
- const exited = new Promise((resolve6) => {
66149
- markExited = resolve6;
68582
+ const exited = new Promise((resolve8) => {
68583
+ markExited = resolve8;
66150
68584
  });
66151
68585
  const record = {
66152
68586
  runId,
@@ -66195,7 +68629,7 @@ function createTerminalServer(options) {
66195
68629
  }
66196
68630
  await Promise.race([
66197
68631
  record.exited,
66198
- new Promise((resolve6) => setTimeout(resolve6, stopGraceMs + 5e3).unref())
68632
+ new Promise((resolve8) => setTimeout(resolve8, stopGraceMs + 5e3).unref())
66199
68633
  ]);
66200
68634
  }
66201
68635
  return { ok: true, result: "stopped" };
@@ -66260,9 +68694,8 @@ function createTerminalServer(options) {
66260
68694
  const onUpgrade = (req, socket, head) => {
66261
68695
  socket.on("error", () => socket.destroy());
66262
68696
  const path = (req.url ?? "/").split("?")[0];
66263
- if (path !== TERMINAL_PATH) {
68697
+ if (path !== TERMINAL_PATH && path !== CHAT_PATH) {
66264
68698
  refuseUpgrade(socket, 404, "Not Found");
66265
- if (path === CHAT_PATH) log("agent-terminal: upgrade refused (chat not served)");
66266
68699
  return;
66267
68700
  }
66268
68701
  if (!isWebSocketUpgrade(req)) {
@@ -66286,6 +68719,10 @@ function createTerminalServer(options) {
66286
68719
  return;
66287
68720
  }
66288
68721
  const ws = acceptWebSocket(req, socket, head);
68722
+ if (path === CHAT_PATH) {
68723
+ acceptChat(ws);
68724
+ return;
68725
+ }
66289
68726
  const attachment = { ws, session: null, tokenSession: verdict.payload.sessionId };
66290
68727
  connections.add(attachment);
66291
68728
  log("agent-terminal: connection opened");
@@ -66306,8 +68743,21 @@ function createTerminalServer(options) {
66306
68743
  log(`agent-terminal: connection closed (${code})`);
66307
68744
  });
66308
68745
  };
68746
+ const acceptChat = (ws) => {
68747
+ chatSockets.add(ws);
68748
+ syncSignInTimer();
68749
+ const handle = chat.accept(ws);
68750
+ ws.on("message", (data, isBinary) => handle.message(data, isBinary));
68751
+ ws.on("close", (code) => {
68752
+ chatSockets.delete(ws);
68753
+ handle.closed();
68754
+ syncSignInTimer();
68755
+ log(`agent-chat: connection closed (${code})`);
68756
+ });
68757
+ };
66309
68758
  const server = createServer2((req, res) => {
66310
- res.statusCode = req.url?.startsWith(TERMINAL_PATH) ? 426 : 404;
68759
+ const path = (req.url ?? "/").split("?")[0];
68760
+ res.statusCode = path === TERMINAL_PATH || path === CHAT_PATH ? 426 : 404;
66311
68761
  res.end();
66312
68762
  });
66313
68763
  server.on("upgrade", onUpgrade);
@@ -66315,14 +68765,14 @@ function createTerminalServer(options) {
66315
68765
  return {
66316
68766
  server,
66317
68767
  listen(port, host = "0.0.0.0") {
66318
- return new Promise((resolve6, reject) => {
68768
+ return new Promise((resolve8, reject) => {
66319
68769
  server.once("error", reject);
66320
68770
  server.listen(port, host, () => {
66321
68771
  server.off("error", reject);
66322
68772
  const address = server.address();
66323
68773
  const bound = typeof address === "object" && address ? address.port : port;
66324
68774
  log(`agent-terminal: listening on ${host}:${bound}`);
66325
- resolve6(bound);
68775
+ resolve8(bound);
66326
68776
  });
66327
68777
  });
66328
68778
  },
@@ -66347,9 +68797,13 @@ function createTerminalServer(options) {
66347
68797
  }
66348
68798
  for (const attachment of connections) attachment.ws.terminate();
66349
68799
  connections.clear();
66350
- await new Promise((resolve6) => server.close(() => resolve6()));
68800
+ chat.close();
68801
+ for (const ws of chatSockets) ws.terminate();
68802
+ chatSockets.clear();
68803
+ await new Promise((resolve8) => server.close(() => resolve8()));
66351
68804
  },
66352
- sessionCount: () => sessions.size
68805
+ sessionCount: () => sessions.size,
68806
+ chatTurn: () => chat.runningTurn()
66353
68807
  };
66354
68808
  }
66355
68809
 
@@ -66405,7 +68859,7 @@ function print(deps, value) {
66405
68859
  }
66406
68860
  function readStdinBounded(stream = process.stdin) {
66407
68861
  if (stream.isTTY) return Promise.resolve("");
66408
- return new Promise((resolve6, reject) => {
68862
+ return new Promise((resolve8, reject) => {
66409
68863
  const chunks = [];
66410
68864
  let size = 0;
66411
68865
  stream.on("data", (chunk) => {
@@ -66418,7 +68872,7 @@ function readStdinBounded(stream = process.stdin) {
66418
68872
  }
66419
68873
  chunks.push(buf);
66420
68874
  });
66421
- stream.on("end", () => resolve6(Buffer.concat(chunks).toString("utf8")));
68875
+ stream.on("end", () => resolve8(Buffer.concat(chunks).toString("utf8")));
66422
68876
  stream.on(
66423
68877
  "error",
66424
68878
  () => reject(new CliError("Could not read the run\u2019s credentials on stdin."))
@@ -66487,7 +68941,7 @@ async function agentTerminalRunCommand(key, opts, deps = {}) {
66487
68941
  }
66488
68942
  chmodSync5(dir, 448);
66489
68943
  const access = { apiUrl: credentials.apiUrl, runId, token: credentials.token };
66490
- const runFile = join21(dir, "run.json");
68944
+ const runFile = join24(dir, "run.json");
66491
68945
  writeFileSync8(runFile, `${JSON.stringify(access)}
66492
68946
  `, { mode: 384 });
66493
68947
  chmodSync5(runFile, 384);
@@ -67074,13 +69528,13 @@ function buildProgram() {
67074
69528
  // src/staleness.ts
67075
69529
  import { execFileSync } from "child_process";
67076
69530
  import { accessSync as accessSync3, constants as constants2, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "fs";
67077
- import { dirname as dirname8, join as join22 } from "path";
69531
+ import { dirname as dirname8, join as join25 } from "path";
67078
69532
  var CLI_PACKAGE = "@motir/cli";
67079
69533
  var REGISTRY_URL = `https://registry.npmjs.org/${CLI_PACKAGE}/latest`;
67080
69534
  var REGISTRY_TIMEOUT_MS = 1500;
67081
69535
  var CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
67082
69536
  function versionCachePath() {
67083
- return join22(stateDir(), "version-check.json");
69537
+ return join25(stateDir(), "version-check.json");
67084
69538
  }
67085
69539
  function compareVersions(a, b) {
67086
69540
  const parts = (value) => value.split(/[-+]/)[0].split(".").map((piece) => Number.parseInt(piece, 10)).map((piece) => Number.isFinite(piece) ? piece : 0);
@@ -67134,7 +69588,7 @@ function writeVersionCache(latest, now, path = versionCachePath()) {
67134
69588
  }
67135
69589
  }
67136
69590
  function globalPrefixDir(execPath = process.execPath) {
67137
- return join22(dirname8(dirname8(execPath)), "lib", "node_modules");
69591
+ return join25(dirname8(dirname8(execPath)), "lib", "node_modules");
67138
69592
  }
67139
69593
  function isWritable(path) {
67140
69594
  try {