@amistio/cli 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,6 +39,8 @@ Provider-backed model preferences use sanitized catalog fields: `--provider`, `-
39
39
 
40
40
  When `--tool copilot` uses the GitHub Copilot SDK, Amistio approves read-only permission requests by default and denies mutating, network, MCP, hook, memory, and shell requests. Set `AMISTIO_COPILOT_APPROVE_ALL=1` only on a local machine where broad Copilot SDK approval is intentional.
41
41
 
42
+ When `--tool codex` uses the Codex SDK, intermediate progress can be quiet until the final response. For live Codex CLI logs, run `amistio run --watch --tool codex --invocation-channel command`.
43
+
42
44
  `amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
43
45
 
44
46
  The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only; source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, or security remediation plan in the app.
@@ -49,6 +51,8 @@ Runner setup and local-tool execution use bounded failure controls. `amistio run
49
51
 
50
52
  Watch mode prints a completed-work success once per work item, keeps fresh completion visible briefly, and returns old completed work to the ready state when no queued, running, blocked, failed, review, or runner-readiness action needs attention.
51
53
 
54
+ Known validation failures such as `unsafe_context_path` are printed with attention-needed next steps. For project-context refresh path-safety failures, deploy the latest web/API fix, update and restart the runner when applicable, retry the refresh, and capture only bounded non-secret output if it repeats.
55
+
52
56
  For headless startup after login on supported user-level service managers:
53
57
 
54
58
  ```sh
package/dist/index.js CHANGED
@@ -1583,6 +1583,73 @@ function decideSyncState(state) {
1583
1583
  }
1584
1584
 
1585
1585
  // ../shared/src/next-action.ts
1586
+ var workFailureGuidanceCodes = ["unsafe_context_path", "unsafe_context_result", "invalid_context_result", "context_map_too_large", "context_map_store_failed"];
1587
+ var projectContextFailureGuidance = {
1588
+ unsafe_context_path: {
1589
+ code: "unsafe_context_path",
1590
+ title: "Attention needed",
1591
+ message: "Project context refresh was rejected because a returned path looked unsafe.",
1592
+ steps: [
1593
+ "Deploy the latest web/API fix and update the local runner if this started after a path-safety release.",
1594
+ "Retry the project context refresh.",
1595
+ "If it fails again, capture the failing repo-relative path or bounded runner output without sharing secrets."
1596
+ ]
1597
+ },
1598
+ unsafe_context_result: {
1599
+ code: "unsafe_context_result",
1600
+ title: "Attention needed",
1601
+ message: "Project context refresh was rejected because the result looked like it contained unsafe evidence.",
1602
+ steps: [
1603
+ "Review the local runner output for accidental secret-looking evidence without pasting secrets into Amistio.",
1604
+ "Retry the project context refresh after removing or narrowing unsafe evidence.",
1605
+ "If it repeats, capture a bounded redacted summary of the runner output."
1606
+ ]
1607
+ },
1608
+ invalid_context_result: {
1609
+ code: "invalid_context_result",
1610
+ title: "Attention needed",
1611
+ message: "Project context refresh returned structured data the server could not accept.",
1612
+ steps: [
1613
+ "Update and restart the local runner so it uses the latest context refresh schema.",
1614
+ "Retry the project context refresh.",
1615
+ "If it fails again, capture the bounded marker-delimited result without source dumps or secrets."
1616
+ ]
1617
+ },
1618
+ context_map_too_large: {
1619
+ code: "context_map_too_large",
1620
+ title: "Attention needed",
1621
+ message: "Project context refresh produced a map too large to store safely.",
1622
+ steps: [
1623
+ "Retry the refresh after narrowing the context scope or waiting for smaller incremental refresh support.",
1624
+ "Keep raw source and large dumps out of the context result.",
1625
+ "If this repeats on normal output, split the context-map storage model before retrying large repositories."
1626
+ ]
1627
+ },
1628
+ context_map_store_failed: {
1629
+ code: "context_map_store_failed",
1630
+ title: "Attention needed",
1631
+ message: "Project context refresh could not be stored safely after validation.",
1632
+ steps: [
1633
+ "Retry the project context refresh once.",
1634
+ "If it repeats, check the web/API storage logs for the safe rejection reason.",
1635
+ "Keep the runner output bounded and avoid sharing raw source or secrets."
1636
+ ]
1637
+ }
1638
+ };
1639
+ function getWorkFailureGuidance(input) {
1640
+ const normalizedMessage = (input.message ?? "").toLowerCase();
1641
+ const code = workFailureGuidanceCodes.find((candidate) => normalizedMessage.includes(candidate));
1642
+ if (!code) {
1643
+ return void 0;
1644
+ }
1645
+ if (input.workKind && input.workKind !== "projectContextRefresh" && !normalizedMessage.includes("project context")) {
1646
+ return void 0;
1647
+ }
1648
+ return projectContextFailureGuidance[code];
1649
+ }
1650
+ function formatWorkFailureGuidanceDetail(guidance) {
1651
+ return `Next steps: ${guidance.steps.join(" ")}`;
1652
+ }
1586
1653
  var nextActionRunnerHeartbeatFreshMs = 15 * 60 * 1e3;
1587
1654
  var nextActionCompletedWorkFreshMs = 60 * 60 * 1e3;
1588
1655
  function computeProjectNextAction(input) {
@@ -1647,11 +1714,11 @@ function computeProjectNextAction(input) {
1647
1714
  }
1648
1715
  const failedBrainGeneration = latestWorkItem(input.workItems.filter((item) => item.workKind === "brainGeneration" && item.status === "failed"));
1649
1716
  if (failedBrainGeneration) {
1650
- return workAction(failedBrainGeneration, "brainGenerationFailed", "user", "danger", "Brain generation failed", failedBrainGeneration.lastStatusMessage ?? "Retry generation after checking the runner output.");
1717
+ return failedWorkAction(failedBrainGeneration, "brainGenerationFailed", "Brain generation failed", failedBrainGeneration.lastStatusMessage ?? "Retry generation after checking the runner output.");
1651
1718
  }
1652
1719
  const failedPlanRevision = latestWorkItem(input.workItems.filter((item) => item.workKind === "planRevision" && item.status === "failed"));
1653
1720
  if (failedPlanRevision) {
1654
- return workAction(failedPlanRevision, "planRevisionFailed", "user", "danger", "Plan revision failed", failedPlanRevision.lastStatusMessage ?? "Review the conversation and request another revision if needed.");
1721
+ return failedWorkAction(failedPlanRevision, "planRevisionFailed", "Plan revision failed", failedPlanRevision.lastStatusMessage ?? "Review the conversation and request another revision if needed.");
1655
1722
  }
1656
1723
  const blockedWork = latestWorkItem(input.workItems.filter((item) => item.status === "blocked" || item.status === "changesRequested"));
1657
1724
  if (blockedWork) {
@@ -1659,7 +1726,7 @@ function computeProjectNextAction(input) {
1659
1726
  }
1660
1727
  const failedWork = latestWorkItem(input.workItems.filter((item) => item.status === "failed"));
1661
1728
  if (failedWork) {
1662
- return workAction(failedWork, "workFailed", "user", "danger", "Work failed", failedWork.lastStatusMessage ?? "Review the failure and retry or update the plan.");
1729
+ return failedWorkAction(failedWork, "workFailed", "Work failed", failedWork.lastStatusMessage ?? "Review the failure and retry or update the plan.");
1663
1730
  }
1664
1731
  const queuedBrainGeneration = latestWorkItem(input.workItems.filter((item) => item.workKind === "brainGeneration" && item.status === "approved"));
1665
1732
  const queuedPlanRevision = latestWorkItem(input.workItems.filter((item) => item.workKind === "planRevision" && item.status === "approved"));
@@ -1698,7 +1765,7 @@ function computeProjectNextAction(input) {
1698
1765
  };
1699
1766
  }
1700
1767
  function formatProjectNextAction(action) {
1701
- return `${action.title}: ${action.message}`;
1768
+ return `${action.title}: ${action.message}${action.detail ? ` ${action.detail}` : ""}`;
1702
1769
  }
1703
1770
  function runnerWaitAction(readiness, fallbackTitle) {
1704
1771
  const repositoryName = readiness.repositoryLink?.repoName ?? "the paired repository";
@@ -1761,13 +1828,21 @@ function isGeneratedDocument(document) {
1761
1828
  const generatedDraftId = document.frontmatter.generatedDraftId;
1762
1829
  return typeof generatedDraftId === "string" && generatedDraftId.length > 0;
1763
1830
  }
1764
- function workAction(workItem, kind, actor, tone, title, message) {
1831
+ function failedWorkAction(workItem, kind, fallbackTitle, fallbackMessage) {
1832
+ const guidance = getWorkFailureGuidance({ message: workItem.lastStatusMessage, workKind: workItem.workKind });
1833
+ if (guidance) {
1834
+ return workAction(workItem, kind, "user", "danger", guidance.title, guidance.message, formatWorkFailureGuidanceDetail(guidance));
1835
+ }
1836
+ return workAction(workItem, kind, "user", "danger", fallbackTitle, fallbackMessage);
1837
+ }
1838
+ function workAction(workItem, kind, actor, tone, title, message, detail) {
1765
1839
  return {
1766
1840
  kind,
1767
1841
  actor,
1768
1842
  tone,
1769
1843
  title,
1770
1844
  message,
1845
+ ...detail ? { detail } : {},
1771
1846
  workItemId: workItem.workItemId,
1772
1847
  ...workItem.claimedByRunnerId ? { runnerId: workItem.claimedByRunnerId } : {},
1773
1848
  updatedAt: workItem.lastStatusAt
@@ -3198,6 +3273,9 @@ async function runCodexSdk(input) {
3198
3273
  approvalPolicy: "on-request",
3199
3274
  skipGitRepoCheck: true
3200
3275
  });
3276
+ if (input.streamOutput) {
3277
+ process.stderr.write("Codex SDK is running; intermediate progress may be quiet until the final response. Use --invocation-channel command for Codex CLI logs.\n");
3278
+ }
3201
3279
  const result = await thread.run(input.prompt);
3202
3280
  if (input.streamOutput && result.finalResponse) {
3203
3281
  process.stdout.write(result.finalResponse);
@@ -4389,6 +4467,7 @@ var appEvaluationStart = "AMISTIO_APP_EVALUATION_START";
4389
4467
  var appEvaluationEnd = "AMISTIO_APP_EVALUATION_END";
4390
4468
  var projectContextRefreshStart = "AMISTIO_PROJECT_CONTEXT_REFRESH_START";
4391
4469
  var projectContextRefreshEnd = "AMISTIO_PROJECT_CONTEXT_REFRESH_END";
4470
+ var validProjectContextSliceKinds = /* @__PURE__ */ new Set(["overview", "architecture", "domain", "data", "api", "frontend", "backend", "cli", "workflow", "operations", "security", "testing", "unknown"]);
4392
4471
  var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
4393
4472
  var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
4394
4473
  function createWorkExecutionPrompt(workItem, context) {
@@ -4493,6 +4572,7 @@ function createProjectContextRefreshPrompt(workItem, context) {
4493
4572
  "## Mapping Requirements",
4494
4573
  "",
4495
4574
  "- Create slices for architecture, domain, data, API, frontend, backend, CLI, workflows, operations, security, and testing when those surfaces exist.",
4575
+ "- Use only these exact singular slice kind values: overview, architecture, domain, data, api, frontend, backend, cli, workflow, operations, security, testing, unknown.",
4496
4576
  "- Capture entities and relations that explain how the app is put together and where future work should look first.",
4497
4577
  "- Prefer summaries, repository-relative paths, short citations, tags, and freshness status over raw source excerpts.",
4498
4578
  "- Mark stale or missing areas explicitly instead of guessing.",
@@ -4969,7 +5049,9 @@ function projectContextRefreshSubmissionFailureSummary(result) {
4969
5049
  if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
4970
5050
  return void 0;
4971
5051
  }
4972
- return result.refresh.error ?? result.workItem.lastStatusMessage ?? "Server rejected the project context refresh result.";
5052
+ const summary = result.refresh.error ?? result.workItem.lastStatusMessage ?? "Server rejected the project context refresh result.";
5053
+ const guidance = getWorkFailureGuidance({ message: summary, workKind: "projectContextRefresh" });
5054
+ return guidance ? `${summary} ${formatWorkFailureGuidanceDetail(guidance)}` : summary;
4973
5055
  }
4974
5056
  function createBrainGenerationPrompt(workItem) {
4975
5057
  const wish = workItem.sourceWish ?? workItem.title;
@@ -5032,6 +5114,16 @@ function normalizeProjectContextRefreshEnums(value) {
5032
5114
  return value;
5033
5115
  }
5034
5116
  const normalized = { ...value };
5117
+ if (Array.isArray(normalized.slices)) {
5118
+ normalized.slices = normalized.slices.map((slice) => {
5119
+ if (!isObjectRecord(slice)) {
5120
+ return slice;
5121
+ }
5122
+ const normalizedSlice = { ...slice };
5123
+ normalizedSlice.kind = normalizeProjectContextSliceKind(slice.kind);
5124
+ return normalizedSlice;
5125
+ });
5126
+ }
5035
5127
  if (Array.isArray(normalized.entities)) {
5036
5128
  normalized.entities = normalized.entities.map((entity) => {
5037
5129
  if (!isObjectRecord(entity)) {
@@ -5054,6 +5146,22 @@ function normalizeProjectContextRefreshEnums(value) {
5054
5146
  }
5055
5147
  return normalized;
5056
5148
  }
5149
+ function normalizeProjectContextSliceKind(value) {
5150
+ if (typeof value !== "string") {
5151
+ return "unknown";
5152
+ }
5153
+ const normalized = value.trim().toLowerCase();
5154
+ if (validProjectContextSliceKinds.has(normalized)) {
5155
+ return normalized;
5156
+ }
5157
+ if (normalized.endsWith("s")) {
5158
+ const singular = normalized.slice(0, -1);
5159
+ if (validProjectContextSliceKinds.has(singular)) {
5160
+ return singular;
5161
+ }
5162
+ }
5163
+ return "unknown";
5164
+ }
5057
5165
  function normalizeProjectContextRefreshPaths(value, options) {
5058
5166
  if (!isObjectRecord(value)) {
5059
5167
  return value;