@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 +4 -0
- package/dist/index.js +114 -6
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|