@amistio/cli 0.1.31 → 0.1.32
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 +2 -0
- package/dist/index.js +48 -6
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ Runner Update installs the official `@amistio/cli` package and then refreshes th
|
|
|
17
17
|
|
|
18
18
|
Current runners advertise the work kinds they can claim. Older runners that do not send this capability can continue legacy brain generation, implementation, and plan revision work, but they will skip source-aware assistant, impact-preview, semantic brain consolidation, project-context refresh, issue-diagnosis, app-evaluation, security-posture, Test-quality, implementation-Test-gate, and implementation-verification work until updated.
|
|
19
19
|
|
|
20
|
+
Tool session reuse is bounded. One-shot tool sessions close after successful completion, failed runs are blocked, active sessions are treated as already in use, and reusable sessions idle for more than six hours are closed before the next claim selects context. This keeps follow-up work from inheriting stale local AI context while preserving recent reusable sessions for related work.
|
|
21
|
+
|
|
20
22
|
Repository brain auto-sync is disabled until the repository link option is enabled in the app. After pairing, run `amistio sync watch` from the paired checkout to push recognized external brain Markdown/MDX files and explicit HTML artifacts under `docs/html/<area>/`, including local ADRs, plans, prompts, workflows, memory, context, architecture, and feature docs, to the app for review. Markdown is the default generation format; HTML appears only when a runner or user explicitly generated an HTML artifact. `amistio run --watch` also runs the same cycle between work polls when the option is enabled. The CLI skips templates, unsupported paths, oversized files, unchanged managed docs, and conflicts instead of silently overwriting web state.
|
|
21
23
|
|
|
22
24
|
Repository autopilot is disabled until the repository link option is enabled in the app. When enabled, Amistio can attach an audited low-risk autopilot authorization to eligible runner work, including generated brain approval, impact preview, issue diagnosis, security posture scan, app evaluation cleanup, low-risk implementation handoff, requeue, and implementation verification. The Runner panel shows and updates safe work scopes, allowed candidate types, max risk, optional runner binding, daily/concurrent/failure budgets, expiry/review/cooldown windows, and pause state. The CLI shows authorization id, candidate id/type, outcome, policy version, and work kind in `amistio work list`, claim logs, runner prompts, and milestone activity. Autopilot does not widen local runner permissions: pairing, supported work kinds, runner identity, Git worktree isolation, redaction, local-tool permission controls, and unsafe/review-required/blocked/paused/budget stops still apply.
|
package/dist/index.js
CHANGED
|
@@ -4740,8 +4740,32 @@ function runProcess(command, args, timeoutMs) {
|
|
|
4740
4740
|
});
|
|
4741
4741
|
}
|
|
4742
4742
|
|
|
4743
|
+
// src/tool-session-lifecycle.ts
|
|
4744
|
+
var TOOL_SESSION_MAX_IDLE_MS = 6 * 60 * 60 * 1e3;
|
|
4745
|
+
function completedToolSessionStatus(session) {
|
|
4746
|
+
return session.resumabilityScope === "none" ? "closed" : "open";
|
|
4747
|
+
}
|
|
4748
|
+
function completedToolSessionClosedReason(session) {
|
|
4749
|
+
if (session.resumabilityScope !== "none") {
|
|
4750
|
+
return void 0;
|
|
4751
|
+
}
|
|
4752
|
+
return "Completed one-shot tool run; this session is not reusable.";
|
|
4753
|
+
}
|
|
4754
|
+
function staleToolSessionClosedReason(session, now = /* @__PURE__ */ new Date()) {
|
|
4755
|
+
if (session.status !== "open") {
|
|
4756
|
+
return void 0;
|
|
4757
|
+
}
|
|
4758
|
+
const lastActivityMs = Date.parse(session.lastActivityAt);
|
|
4759
|
+
if (!Number.isFinite(lastActivityMs)) {
|
|
4760
|
+
return "Session has an invalid last activity timestamp; closing to prevent stale context reuse.";
|
|
4761
|
+
}
|
|
4762
|
+
if (lastActivityMs + TOOL_SESSION_MAX_IDLE_MS >= now.getTime()) {
|
|
4763
|
+
return void 0;
|
|
4764
|
+
}
|
|
4765
|
+
return "Session idle window expired; closing to prevent stale context reuse.";
|
|
4766
|
+
}
|
|
4767
|
+
|
|
4743
4768
|
// src/session-policy.ts
|
|
4744
|
-
var maxIdleMs = 24 * 60 * 60 * 1e3;
|
|
4745
4769
|
var maxTotalMs = 7 * 24 * 60 * 60 * 1e3;
|
|
4746
4770
|
var maxMessageCount = 80;
|
|
4747
4771
|
var maxEstimatedTokens = 12e4;
|
|
@@ -4825,10 +4849,10 @@ function sessionIneligibleReason(session, input, now) {
|
|
|
4825
4849
|
if (input.workItem.executionWorktreeKey && session.executionWorktreeKey !== input.workItem.executionWorktreeKey) {
|
|
4826
4850
|
return "worktree scope mismatch";
|
|
4827
4851
|
}
|
|
4828
|
-
if (session.status === "closed" || session.status === "archived" || session.status === "blocked" || session.status === "unavailable") {
|
|
4852
|
+
if (session.status === "closed" || session.status === "archived" || session.status === "blocked" || session.status === "unavailable" || session.status === "active") {
|
|
4829
4853
|
return `session is ${session.status}`;
|
|
4830
4854
|
}
|
|
4831
|
-
if (Date.parse(session.lastActivityAt) +
|
|
4855
|
+
if (Date.parse(session.lastActivityAt) + TOOL_SESSION_MAX_IDLE_MS < now.getTime()) {
|
|
4832
4856
|
return "session is idle past the reuse window";
|
|
4833
4857
|
}
|
|
4834
4858
|
if (Date.parse(session.createdAt) + maxTotalMs < now.getTime()) {
|
|
@@ -6035,6 +6059,7 @@ function createAppEvaluationScanPrompt(workItem, context) {
|
|
|
6035
6059
|
"- Treat active proposed, approved, ready, or in-progress plans/prompts with accepted controlling ADRs, features, or work artifacts as active backlog. Do not classify them as cleanup material solely because they are older, unexecuted, or not yet fully implemented; use keepActive when they still describe valid pending or approved work.",
|
|
6036
6060
|
"- Treat intentionally in-progress feature tracks as still-active work when their controlling plan/feature has unchecked requirements or explicit follow-up gaps. For example, a completed first implementation prompt does not make the broader feature stale if PLAN/FEAT evidence says remaining lifecycle work is still open; return proposedLifecycleAction keepActive with evidence instead of cleanup.",
|
|
6037
6061
|
"- Treat prompt frontmatter status Ready as an active execution backlog state by default, not as stale review debt. Only flag a Ready prompt for metadata correction when its controlling plan, feature, prompt index, or verification evidence unambiguously proves the prompt has already completed or been superseded.",
|
|
6062
|
+
"- Treat implemented umbrella plans that explicitly label unchecked checklist items as deferred follow-ups, future candidates, roadmap backlog, or split-out hardening phases as valid deferred backlog. Do not mark the umbrella incomplete or stale solely because those deferred items remain unchecked; use keepActive or no cleanup, and recommend a fresh focused plan only when a concrete deferred slice has current evidence and approval.",
|
|
6038
6063
|
"- When lifecycle metadata disagrees across indexes, frontmatter, feature specs, ADRs, and implementation evidence, cite the conflict and propose a metadata correction or verification step instead of archival/removal.",
|
|
6039
6064
|
"- Check missing memory or workflow updates when repeated lessons or operational rules are visible.",
|
|
6040
6065
|
"- Check release readiness, UX, accessibility, performance, reliability, and security-posture follow-through at a summary level.",
|
|
@@ -11026,6 +11051,8 @@ async function prepareToolSession({
|
|
|
11026
11051
|
workItem
|
|
11027
11052
|
}) {
|
|
11028
11053
|
const { toolSessions } = await apiClient.listToolSessions(projectId);
|
|
11054
|
+
const now = /* @__PURE__ */ new Date();
|
|
11055
|
+
await closeStaleToolSessionsBestEffort(apiClient, projectId, toolSessions, now);
|
|
11029
11056
|
const selection = selectToolSession({
|
|
11030
11057
|
policy: sessionPolicy,
|
|
11031
11058
|
workItem,
|
|
@@ -11034,7 +11061,8 @@ async function prepareToolSession({
|
|
|
11034
11061
|
runnerId,
|
|
11035
11062
|
repositoryLinkId,
|
|
11036
11063
|
machineId,
|
|
11037
|
-
supportsSessionReuse
|
|
11064
|
+
supportsSessionReuse,
|
|
11065
|
+
now
|
|
11038
11066
|
});
|
|
11039
11067
|
if (selection.decision === "skipped") {
|
|
11040
11068
|
return selection;
|
|
@@ -11076,6 +11104,18 @@ async function prepareToolSession({
|
|
|
11076
11104
|
});
|
|
11077
11105
|
return { ...selection, toolSession };
|
|
11078
11106
|
}
|
|
11107
|
+
async function closeStaleToolSessionsBestEffort(apiClient, projectId, toolSessions, now) {
|
|
11108
|
+
const staleSessions = toolSessions.map((session) => ({ session, reason: staleToolSessionClosedReason(session, now) })).filter((item) => Boolean(item.reason));
|
|
11109
|
+
if (!staleSessions.length) {
|
|
11110
|
+
return;
|
|
11111
|
+
}
|
|
11112
|
+
const settlements = await Promise.allSettled(staleSessions.map(({ session, reason }) => apiClient.updateToolSession(projectId, session.toolSessionId, {
|
|
11113
|
+
status: "closed",
|
|
11114
|
+
closedReason: reason,
|
|
11115
|
+
summary: session.summary ?? reason
|
|
11116
|
+
})));
|
|
11117
|
+
logRejectedSettlements("close stale tool sessions", settlements);
|
|
11118
|
+
}
|
|
11079
11119
|
async function finalizeToolSession({
|
|
11080
11120
|
apiClient,
|
|
11081
11121
|
costUsd,
|
|
@@ -11093,8 +11133,10 @@ async function finalizeToolSession({
|
|
|
11093
11133
|
return void 0;
|
|
11094
11134
|
}
|
|
11095
11135
|
const summary = summarizeToolOutput(stdout) ?? session.summary;
|
|
11136
|
+
const nextStatus = status === "completed" ? completedToolSessionStatus(session) : "blocked";
|
|
11137
|
+
const closedReason = status === "completed" ? completedToolSessionClosedReason(session) : "Last run failed or returned a non-zero exit code.";
|
|
11096
11138
|
const { toolSession } = await apiClient.updateToolSession(projectId, session.toolSessionId, {
|
|
11097
|
-
status:
|
|
11139
|
+
status: nextStatus,
|
|
11098
11140
|
runnerId,
|
|
11099
11141
|
lastWorkItemId: workItemId,
|
|
11100
11142
|
messageCount: (session.messageCount ?? 0) + (messageCount ?? 1),
|
|
@@ -11102,7 +11144,7 @@ async function finalizeToolSession({
|
|
|
11102
11144
|
...tokensIn !== void 0 ? { estimatedInputTokens: (session.estimatedInputTokens ?? 0) + tokensIn } : {},
|
|
11103
11145
|
...tokensOut !== void 0 ? { estimatedOutputTokens: (session.estimatedOutputTokens ?? 0) + tokensOut } : {},
|
|
11104
11146
|
...costUsd !== void 0 ? { costUsd: (session.costUsd ?? 0) + costUsd } : {},
|
|
11105
|
-
...
|
|
11147
|
+
...closedReason ? { closedReason } : {}
|
|
11106
11148
|
});
|
|
11107
11149
|
return toolSession;
|
|
11108
11150
|
}
|