@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.
- package/dist/index.js +2867 -413
- 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.
|
|
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.
|
|
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((
|
|
55662
|
+
return new Promise((resolve8) => {
|
|
55663
55663
|
rl.question(`${question}${suffix}: `, (answer) => {
|
|
55664
55664
|
rl.close();
|
|
55665
55665
|
const trimmed = answer.trim();
|
|
55666
|
-
|
|
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((
|
|
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
|
-
|
|
55677
|
+
resolve8(answer);
|
|
55678
55678
|
});
|
|
55679
55679
|
rl.on("close", () => {
|
|
55680
|
-
if (!answered)
|
|
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((
|
|
55689
|
+
return new Promise((resolve8) => {
|
|
55690
55690
|
rl.question("", (answer) => {
|
|
55691
55691
|
rl.close();
|
|
55692
55692
|
process.stderr.write("\n");
|
|
55693
|
-
|
|
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((
|
|
55852
|
+
return new Promise((resolve8) => {
|
|
55853
55853
|
try {
|
|
55854
55854
|
const child = spawn(cmd, [...args, url], { stdio: "ignore", detached: true });
|
|
55855
|
-
child.on("error", () =>
|
|
55855
|
+
child.on("error", () => resolve8(false));
|
|
55856
55856
|
child.unref();
|
|
55857
|
-
setTimeout(() =>
|
|
55857
|
+
setTimeout(() => resolve8(true), 0);
|
|
55858
55858
|
} catch {
|
|
55859
|
-
|
|
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
|
|
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
|
|
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
|
|
56782
|
+
const lines2 = [`${sprint.name} [${sprint.state}]`];
|
|
56783
56783
|
const window = formatSprintWindow(sprint);
|
|
56784
|
-
if (window)
|
|
56785
|
-
if (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
|
-
|
|
56789
|
+
lines2.push(`committed: ${issues} issues \xB7 ${points} points`);
|
|
56790
56790
|
}
|
|
56791
|
-
return
|
|
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
|
|
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
|
-
|
|
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)
|
|
56865
|
-
if (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
|
-
|
|
56867
|
+
lines2.push("Sprint: (no sprints)");
|
|
56868
56868
|
} else {
|
|
56869
|
-
|
|
56869
|
+
lines2.push("Sprint: (none active)");
|
|
56870
56870
|
}
|
|
56871
|
-
return
|
|
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
|
|
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)
|
|
56960
|
+
if (legend) lines2.push(legend);
|
|
56961
56961
|
const cycle = cycleMembers(waves);
|
|
56962
56962
|
if (cycle.length > 0) {
|
|
56963
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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, `${
|
|
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
|
|
58640
|
+
const lines2 = ["motir doctor \u2014 BYOK preflight", ""];
|
|
58641
58641
|
for (const check of report.checks) {
|
|
58642
|
-
|
|
58642
|
+
lines2.push(
|
|
58643
58643
|
`${check.status.toUpperCase().padEnd(4)} ${check.label.padEnd(labelWidth)} ${check.detail}`
|
|
58644
58644
|
);
|
|
58645
|
-
if (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
|
-
|
|
58651
|
+
lines2.push(
|
|
58652
58652
|
"",
|
|
58653
58653
|
report.ok ? `All hard checks passed \u2014 ${parts.join(", ")}.` : `${parts.join(", ")}.`
|
|
58654
58654
|
);
|
|
58655
|
-
return
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
59048
|
-
|
|
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
|
|
59254
|
-
for (const repo of result.cloned)
|
|
59265
|
+
const lines2 = [];
|
|
59266
|
+
for (const repo of result.cloned) lines2.push(`Cloned: ${repo}`);
|
|
59255
59267
|
for (const failure2 of result.failures) {
|
|
59256
|
-
|
|
59257
|
-
if (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
|
-
|
|
59261
|
-
|
|
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
|
|
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
|
|
59322
|
+
const lines2 = [];
|
|
59311
59323
|
if (references.length > 0) {
|
|
59312
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
59376
|
-
const
|
|
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
|
|
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
|
-
|
|
59391
|
-
|
|
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)
|
|
59408
|
+
if (label2) lines2.push(` ${label2}`);
|
|
59394
59409
|
if (t.reason === "bootstrap_root") {
|
|
59395
|
-
|
|
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
|
|
59416
|
+
return lines2;
|
|
59402
59417
|
}
|
|
59403
59418
|
function renderDispatchSummary(input) {
|
|
59404
59419
|
const { key, title, dispatch, target, agent } = input;
|
|
59405
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
60037
|
-
|
|
60038
|
-
|
|
60039
|
-
|
|
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
|
-
|
|
60043
|
-
|
|
60044
|
-
|
|
60057
|
+
lines2.push("");
|
|
60058
|
+
lines2.push("## The failing pull requests");
|
|
60059
|
+
lines2.push("");
|
|
60045
60060
|
for (const d of input.failing) {
|
|
60046
|
-
|
|
60061
|
+
lines2.push(`- **${d.repo}#${d.number}** (base \`${d.baseRef ?? d.defaultBranch}\`) \u2014 ${d.url}`);
|
|
60047
60062
|
}
|
|
60048
|
-
|
|
60063
|
+
lines2.push("");
|
|
60049
60064
|
if (input.checkouts && input.checkouts.length > 0) {
|
|
60050
|
-
|
|
60051
|
-
|
|
60065
|
+
lines2.push("## Where each branch is checked out");
|
|
60066
|
+
lines2.push("");
|
|
60052
60067
|
for (const c of input.checkouts) {
|
|
60053
|
-
|
|
60068
|
+
lines2.push(`- **${c.repo}** \u2014 branch \`${c.branch}\` at \`${c.path}\``);
|
|
60054
60069
|
}
|
|
60055
|
-
|
|
60056
|
-
|
|
60057
|
-
|
|
60058
|
-
|
|
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
|
-
|
|
60063
|
-
|
|
60064
|
-
|
|
60065
|
-
|
|
60066
|
-
|
|
60067
|
-
|
|
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
|
-
|
|
60086
|
+
lines2.push(`- **${d.repo}#${d.number}** \u2014 ${queueReasonInWords(exit.rawReason)}`);
|
|
60072
60087
|
if (exit.failingCheckName) {
|
|
60073
|
-
|
|
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
|
-
|
|
60079
|
-
|
|
60080
|
-
|
|
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
|
-
|
|
60083
|
-
|
|
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
|
-
|
|
60086
|
-
|
|
60087
|
-
|
|
60088
|
-
|
|
60089
|
-
|
|
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
|
-
|
|
60092
|
-
}
|
|
60093
|
-
|
|
60094
|
-
|
|
60095
|
-
|
|
60096
|
-
|
|
60097
|
-
|
|
60098
|
-
|
|
60099
|
-
|
|
60100
|
-
|
|
60101
|
-
|
|
60102
|
-
|
|
60103
|
-
|
|
60104
|
-
|
|
60105
|
-
|
|
60106
|
-
|
|
60107
|
-
|
|
60108
|
-
|
|
60109
|
-
|
|
60110
|
-
|
|
60111
|
-
|
|
60112
|
-
|
|
60113
|
-
|
|
60114
|
-
|
|
60115
|
-
|
|
60116
|
-
|
|
60117
|
-
|
|
60118
|
-
|
|
60119
|
-
|
|
60120
|
-
|
|
60121
|
-
|
|
60122
|
-
|
|
60123
|
-
|
|
60124
|
-
|
|
60125
|
-
|
|
60126
|
-
|
|
60127
|
-
|
|
60128
|
-
|
|
60129
|
-
|
|
60130
|
-
|
|
60131
|
-
|
|
60132
|
-
|
|
60133
|
-
|
|
60134
|
-
|
|
60135
|
-
|
|
60136
|
-
|
|
60137
|
-
|
|
60138
|
-
|
|
60139
|
-
|
|
60140
|
-
|
|
60141
|
-
|
|
60142
|
-
|
|
60143
|
-
|
|
60144
|
-
|
|
60145
|
-
|
|
60146
|
-
|
|
60147
|
-
|
|
60148
|
-
return
|
|
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
|
|
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"))
|
|
60170
|
+
for (const line of reason.split("\n")) lines2.push(`> ${line}`);
|
|
60156
60171
|
} else {
|
|
60157
|
-
|
|
60172
|
+
lines2.push("> (no reason was recorded)");
|
|
60158
60173
|
}
|
|
60159
|
-
return
|
|
60174
|
+
return lines2;
|
|
60160
60175
|
}
|
|
60161
60176
|
function renderCheckoutList(checkouts) {
|
|
60162
|
-
const
|
|
60177
|
+
const lines2 = ["## Where each branch is checked out", ""];
|
|
60163
60178
|
for (const c of checkouts)
|
|
60164
|
-
|
|
60165
|
-
|
|
60166
|
-
|
|
60167
|
-
|
|
60168
|
-
return
|
|
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
|
|
60172
|
-
|
|
60186
|
+
const lines2 = [];
|
|
60187
|
+
lines2.push(
|
|
60173
60188
|
`# Answer the acceptance review \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`
|
|
60174
60189
|
);
|
|
60175
|
-
|
|
60176
|
-
|
|
60177
|
-
|
|
60178
|
-
|
|
60179
|
-
|
|
60180
|
-
|
|
60181
|
-
|
|
60182
|
-
|
|
60183
|
-
|
|
60184
|
-
|
|
60185
|
-
for (const pr of input.pullRequests)
|
|
60186
|
-
|
|
60187
|
-
|
|
60188
|
-
|
|
60189
|
-
|
|
60190
|
-
|
|
60191
|
-
|
|
60192
|
-
|
|
60193
|
-
|
|
60194
|
-
|
|
60195
|
-
|
|
60196
|
-
|
|
60197
|
-
|
|
60198
|
-
|
|
60199
|
-
|
|
60200
|
-
|
|
60201
|
-
|
|
60202
|
-
|
|
60203
|
-
|
|
60204
|
-
|
|
60205
|
-
|
|
60206
|
-
|
|
60207
|
-
|
|
60208
|
-
|
|
60209
|
-
|
|
60210
|
-
|
|
60211
|
-
|
|
60212
|
-
|
|
60213
|
-
return
|
|
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
|
|
60217
|
-
|
|
60232
|
+
const lines2 = [];
|
|
60233
|
+
lines2.push(
|
|
60218
60234
|
`# Re-record the acceptance video \u2014 ${input.key}${input.title ? ` (${input.title})` : ""}`
|
|
60219
60235
|
);
|
|
60220
|
-
|
|
60221
|
-
|
|
60222
|
-
|
|
60223
|
-
|
|
60224
|
-
|
|
60225
|
-
|
|
60226
|
-
|
|
60227
|
-
|
|
60228
|
-
|
|
60229
|
-
|
|
60230
|
-
|
|
60231
|
-
|
|
60232
|
-
|
|
60233
|
-
|
|
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
|
-
|
|
60237
|
-
|
|
60238
|
-
|
|
60239
|
-
"
|
|
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
|
-
|
|
60242
|
-
|
|
60243
|
-
|
|
60244
|
-
|
|
60245
|
-
return
|
|
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
|
|
60252
|
-
|
|
60253
|
-
|
|
60254
|
-
|
|
60255
|
-
|
|
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
|
-
|
|
60259
|
-
|
|
60260
|
-
|
|
60261
|
-
|
|
60262
|
-
|
|
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"))
|
|
60301
|
+
for (const line of findings.split("\n")) lines2.push(`> ${line}`);
|
|
60266
60302
|
} else {
|
|
60267
|
-
|
|
60268
|
-
}
|
|
60269
|
-
|
|
60270
|
-
|
|
60271
|
-
|
|
60272
|
-
for (const pr of input.pullRequests)
|
|
60273
|
-
|
|
60274
|
-
|
|
60275
|
-
|
|
60276
|
-
|
|
60277
|
-
|
|
60278
|
-
|
|
60279
|
-
|
|
60280
|
-
|
|
60281
|
-
|
|
60282
|
-
|
|
60283
|
-
|
|
60284
|
-
|
|
60285
|
-
|
|
60286
|
-
|
|
60287
|
-
|
|
60288
|
-
|
|
60289
|
-
|
|
60290
|
-
|
|
60291
|
-
|
|
60292
|
-
|
|
60293
|
-
|
|
60294
|
-
|
|
60295
|
-
|
|
60296
|
-
|
|
60297
|
-
|
|
60298
|
-
return
|
|
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((
|
|
60302
|
-
setTimeout(
|
|
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
|
|
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
|
-
|
|
60542
|
+
lines2.push("", `## What the commits say (${commits.length})`, "");
|
|
60507
60543
|
for (const [index, commit] of commits.entries()) {
|
|
60508
|
-
if (index > 0)
|
|
60509
|
-
|
|
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)
|
|
60548
|
+
if (beneath) lines2.push("", beneath);
|
|
60513
60549
|
}
|
|
60514
60550
|
}
|
|
60515
|
-
if (howToTest)
|
|
60551
|
+
if (howToTest) lines2.push("", ...renderHowToTestSection(howToTest, branch));
|
|
60516
60552
|
if (failed.length > 0) {
|
|
60517
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
60673
|
-
for (const out2 of record.outOfLane ?? [])
|
|
60708
|
+
lines2.push(` ${record.key}${record.planId ? ` \u2014 plan ${record.planId}` : ""}`);
|
|
60709
|
+
for (const out2 of record.outOfLane ?? []) lines2.push(` ${out2}`);
|
|
60674
60710
|
}
|
|
60675
|
-
|
|
60676
|
-
blocks.push(
|
|
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
|
|
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
|
-
|
|
60741
|
+
lines2.push(` ${record.key} \u2014 re-read ${refresh.parentKey ?? "no parent"}'s children`);
|
|
60706
60742
|
if (refresh.removed.length > 0) {
|
|
60707
|
-
|
|
60743
|
+
lines2.push(` removed by the plan, NOT run: ${refresh.removed.join(", ")}`);
|
|
60708
60744
|
}
|
|
60709
60745
|
if (refresh.added.length > 0) {
|
|
60710
|
-
|
|
60746
|
+
lines2.push(` added by the plan, run when ready: ${refresh.added.join(", ")}`);
|
|
60711
60747
|
}
|
|
60712
60748
|
}
|
|
60713
|
-
blocks.push(
|
|
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
|
|
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
|
-
...
|
|
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
|
|
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
|
|
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
|
|
61564
|
+
const lines2 = [];
|
|
61529
61565
|
if (refresh.removed.length > 0) {
|
|
61530
|
-
|
|
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
|
-
|
|
61571
|
+
lines2.push(` the plan added ${refresh.added.join(", ")} \u2014 picked up when they are ready.`);
|
|
61536
61572
|
}
|
|
61537
|
-
return
|
|
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,
|
|
61870
|
-
const outstanding =
|
|
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
|
|
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 (
|
|
62039
|
-
if (prior && sameKeys(prior.blockedBy,
|
|
62074
|
+
if (open2.length > 0) {
|
|
62075
|
+
if (prior && sameKeys(prior.blockedBy, open2)) continue;
|
|
62040
62076
|
if (prior) {
|
|
62041
|
-
prior.blockedBy =
|
|
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:
|
|
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:
|
|
62092
|
+
data: { blockedBy: open2 }
|
|
62057
62093
|
});
|
|
62058
62094
|
}
|
|
62059
|
-
const gates =
|
|
62095
|
+
const gates = open2.filter((dep) => heldGates.has(dep));
|
|
62060
62096
|
info(
|
|
62061
|
-
gates.length ===
|
|
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
|
|
62516
|
+
const lines2 = ["", "\u2550\u2550\u2550\u2550 CHECKOUTS \u2550\u2550\u2550\u2550", ""];
|
|
62481
62517
|
for (const repo of repos) {
|
|
62482
|
-
|
|
62518
|
+
lines2.push(
|
|
62483
62519
|
`- ${repo.repoName ?? "(the project repository)"}: ${repo.cwd} \u2014 branch ${repo.branch}`
|
|
62484
62520
|
);
|
|
62485
62521
|
}
|
|
62486
|
-
return
|
|
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: (
|
|
62684
|
-
for (const line of
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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.`, ...
|
|
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:
|
|
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
|
|
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
|
-
|
|
63660
|
+
lines2.push(` ${pr.repo}#${pr.number} \u2014 failing: ${checks} (${pr.url})`);
|
|
63625
63661
|
}
|
|
63626
|
-
|
|
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
|
|
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
|
|
63695
|
-
for (const line of
|
|
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
|
|
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.`, ...
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 `${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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: (
|
|
64893
|
-
for (const line of
|
|
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
|
|
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
|
-
|
|
65090
|
+
lines2.push("");
|
|
65055
65091
|
session.turns.forEach((turn, i) => {
|
|
65056
|
-
|
|
65092
|
+
lines2.push(renderTurn(turn, i + 1));
|
|
65057
65093
|
const disposition = dispositionMarkerFor(session.turns, i);
|
|
65058
|
-
if (disposition)
|
|
65094
|
+
if (disposition) lines2.push(renderDisposition(disposition));
|
|
65059
65095
|
});
|
|
65060
65096
|
}
|
|
65061
65097
|
const awaiting = renderAwaiting(session.turns);
|
|
65062
|
-
if (awaiting)
|
|
65063
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
|
65178
|
+
return lines2;
|
|
65143
65179
|
}
|
|
65144
65180
|
function renderPlan(plan) {
|
|
65145
|
-
const
|
|
65181
|
+
const lines2 = [
|
|
65146
65182
|
`Plan ${plan.id} \u2014 ${plan.status}, ${plan.itemCount} proposal${plan.itemCount === 1 ? "" : "s"}.`
|
|
65147
65183
|
];
|
|
65148
|
-
if (plan.title)
|
|
65149
|
-
if (plan.summary)
|
|
65150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65192
|
+
lines2.push(
|
|
65157
65193
|
"Proposals (indented under their proposed parent):",
|
|
65158
65194
|
...renderProposalTree(plan.items)
|
|
65159
65195
|
);
|
|
65160
65196
|
}
|
|
65161
|
-
|
|
65162
|
-
return
|
|
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((
|
|
65176
|
-
setTimeout(
|
|
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
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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"] ||
|
|
65673
|
-
xdgDataHome: env["XDG_DATA_HOME"] ||
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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:
|
|
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((
|
|
66149
|
-
markExited =
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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", () =>
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
69591
|
+
return join25(dirname8(dirname8(execPath)), "lib", "node_modules");
|
|
67138
69592
|
}
|
|
67139
69593
|
function isWritable(path) {
|
|
67140
69594
|
try {
|