@prestyj/cli 5.30.1 → 5.30.3
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 +54 -38
- package/dist/app-sidecar/app-settings.d.ts +6 -2
- package/dist/app-sidecar/app-settings.js +39 -20
- package/dist/app-sidecar/app-settings.js.map +1 -1
- package/dist/app-sidecar/auth-routes.js +4 -1
- package/dist/app-sidecar/auth-routes.js.map +1 -1
- package/dist/app-sidecar/autopilot-runner.js +2 -5
- package/dist/app-sidecar/autopilot-runner.js.map +1 -1
- package/dist/app-sidecar/git-target.d.ts +17 -0
- package/dist/app-sidecar/git-target.js +31 -0
- package/dist/app-sidecar/git-target.js.map +1 -0
- package/dist/app-sidecar/goal-routes.d.ts +26 -0
- package/dist/app-sidecar/goal-routes.js +445 -0
- package/dist/app-sidecar/goal-routes.js.map +1 -0
- package/dist/app-sidecar/history-routes.d.ts +13 -0
- package/dist/app-sidecar/history-routes.js +36 -25
- package/dist/app-sidecar/history-routes.js.map +1 -1
- package/dist/app-sidecar/mcp-routes.js +1 -2
- package/dist/app-sidecar/mcp-routes.js.map +1 -1
- package/dist/app-sidecar/repo-polls.d.ts +5 -2
- package/dist/app-sidecar/repo-polls.js +20 -0
- package/dist/app-sidecar/repo-polls.js.map +1 -1
- package/dist/app-sidecar/route-context.d.ts +14 -0
- package/dist/app-sidecar/run-control-routes.js +2 -1
- package/dist/app-sidecar/run-control-routes.js.map +1 -1
- package/dist/app-sidecar/session-routes.js +41 -0
- package/dist/app-sidecar/session-routes.js.map +1 -1
- package/dist/app-sidecar/session-types.d.ts +2 -0
- package/dist/app-sidecar/workspace-routes.js +13 -95
- package/dist/app-sidecar/workspace-routes.js.map +1 -1
- package/dist/app-sidecar.js +237 -50
- package/dist/app-sidecar.js.map +1 -1
- package/dist/chat-agents/jiwa.js +2 -0
- package/dist/chat-agents/jiwa.js.map +1 -1
- package/dist/chat-agents/memory.js +2 -0
- package/dist/chat-agents/memory.js.map +1 -1
- package/dist/cli/auth.js +13 -9
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/shared.js +1 -1
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli.js +8 -9
- package/dist/cli.js.map +1 -1
- package/dist/core/acceptance-checks.js +17 -4
- package/dist/core/acceptance-checks.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +1 -1
- package/dist/core/agent-session/verification-tracker.d.ts +5 -1
- package/dist/core/agent-session/verification-tracker.js +25 -6
- package/dist/core/agent-session/verification-tracker.js.map +1 -1
- package/dist/core/agent-session.d.ts +83 -5
- package/dist/core/agent-session.js +174 -59
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/ask-user.d.ts +11 -0
- package/dist/core/ask-user.js +6 -1
- package/dist/core/ask-user.js.map +1 -1
- package/dist/core/auth-providers.d.ts +2 -80
- package/dist/core/auth-providers.js +4 -183
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/autopilot-cycle.d.ts +2 -0
- package/dist/core/autopilot-cycle.js +21 -2
- package/dist/core/autopilot-cycle.js.map +1 -1
- package/dist/core/cache-diagnostics.d.ts +1 -1
- package/dist/core/cache-diagnostics.js +2 -1
- package/dist/core/cache-diagnostics.js.map +1 -1
- package/dist/core/checklist-prompt.d.ts +9 -2
- package/dist/core/checklist-prompt.js +26 -7
- package/dist/core/checklist-prompt.js.map +1 -1
- package/dist/core/checklist-snapshot.js +1 -1
- package/dist/core/checklist-snapshot.js.map +1 -1
- package/dist/core/checklist-store.d.ts +5 -1
- package/dist/core/checklist-store.js +35 -4
- package/dist/core/checklist-store.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.d.ts +10 -0
- package/dist/core/compaction/tool-result-pruner.js +12 -0
- package/dist/core/compaction/tool-result-pruner.js.map +1 -1
- package/dist/core/context-files.d.ts +8 -0
- package/dist/core/context-files.js +46 -0
- package/dist/core/context-files.js.map +1 -0
- package/dist/core/env-delta.d.ts +9 -1
- package/dist/core/env-delta.js +20 -0
- package/dist/core/env-delta.js.map +1 -1
- package/dist/core/event-bus.d.ts +1 -1
- package/dist/core/event-bus.js +39 -13
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/extensions/plugin-bundles.js +4 -2
- package/dist/core/extensions/plugin-bundles.js.map +1 -1
- package/dist/core/goals-store.d.ts +208 -0
- package/dist/core/goals-store.js +521 -0
- package/dist/core/goals-store.js.map +1 -0
- package/dist/core/hashline.d.ts +10 -8
- package/dist/core/hashline.js.map +1 -1
- package/dist/core/local-endpoint-store.js +15 -11
- package/dist/core/local-endpoint-store.js.map +1 -1
- package/dist/core/mcp/client.js +2 -0
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/loopback.d.ts +2 -2
- package/dist/core/mcp/loopback.js +2 -2
- package/dist/core/mcp/shared-pool.js +4 -3
- package/dist/core/mcp/shared-pool.js.map +1 -1
- package/dist/core/persistent-shell.js +2 -1
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/process-manager.js +9 -1
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/project-discovery.d.ts +7 -0
- package/dist/core/project-discovery.js +9 -3
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-health-scan.d.ts +109 -0
- package/dist/core/project-health-scan.js +673 -0
- package/dist/core/project-health-scan.js.map +1 -0
- package/dist/core/project-health-score.d.ts +41 -0
- package/dist/core/project-health-score.js +257 -0
- package/dist/core/project-health-score.js.map +1 -0
- package/dist/core/prompt-commands.js +44 -6
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/radio.d.ts +2 -3
- package/dist/core/radio.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -1
- package/dist/core/session-manager.js +17 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-storage.d.ts +4 -0
- package/dist/core/session-storage.js +9 -3
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/steroids.d.ts +8 -0
- package/dist/core/steroids.js +19 -3
- package/dist/core/steroids.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +51 -0
- package/dist/core/subagent-manager.js +114 -22
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-receipt.js +3 -4
- package/dist/core/subagent-receipt.js.map +1 -1
- package/dist/core/subagent-store.js +2 -1
- package/dist/core/subagent-store.js.map +1 -1
- package/dist/core/ui-adoption.js +2 -0
- package/dist/core/ui-adoption.js.map +1 -1
- package/dist/core/ui-registry.js +5 -4
- package/dist/core/ui-registry.js.map +1 -1
- package/dist/core/verification-evidence.d.ts +13 -2
- package/dist/core/verification-evidence.js +256 -3
- package/dist/core/verification-evidence.js.map +1 -1
- package/dist/core/verification-gate.js +4 -2
- package/dist/core/verification-gate.js.map +1 -1
- package/dist/core/verify-commands.js +2 -3
- package/dist/core/verify-commands.js.map +1 -1
- package/dist/goals-agent/goal-team.d.ts +2 -0
- package/dist/goals-agent/goal-team.js +83 -0
- package/dist/goals-agent/goal-team.js.map +1 -0
- package/dist/goals-agent/goals-agent.d.ts +90 -0
- package/dist/goals-agent/goals-agent.js +246 -0
- package/dist/goals-agent/goals-agent.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive.js +4 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/agent-home-mode.js +1 -4
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +14 -0
- package/dist/modes/subagent-worker-mode.js +45 -9
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/system-prompt.d.ts +8 -0
- package/dist/system-prompt.js +41 -20
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash-read-evidence.d.ts +13 -0
- package/dist/tools/bash-read-evidence.js +48 -1
- package/dist/tools/bash-read-evidence.js.map +1 -1
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.js +8 -4
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/checklist.d.ts +5 -4
- package/dist/tools/checklist.js +17 -4
- package/dist/tools/checklist.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -0
- package/dist/tools/edit.js +84 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/grep.js +3 -2
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/index.d.ts +7 -1
- package/dist/tools/index.js +19 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read-only-bash.js +3 -0
- package/dist/tools/read-only-bash.js.map +1 -1
- package/dist/tools/read-tracker.d.ts +6 -12
- package/dist/tools/read-tracker.js +21 -36
- package/dist/tools/read-tracker.js.map +1 -1
- package/dist/tools/read.js +2 -2
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/subagent-control.js +22 -3
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/write.js +110 -8
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/App.js +20 -26
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.d.ts +5 -5
- package/dist/ui/components/InputArea.d.ts +2 -2
- package/dist/ui/components/InputArea.js +3 -5
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/ToolExecution.d.ts +2 -2
- package/dist/ui/components/ToolExecution.js +4 -3
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +7 -6
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useContextCompaction.js +38 -31
- package/dist/ui/hooks/useContextCompaction.js.map +1 -1
- package/dist/ui/sessions.js +1 -5
- package/dist/ui/sessions.js.map +1 -1
- package/dist/ui/utils/highlight.d.ts +0 -5
- package/dist/ui/utils/highlight.js +3 -49
- package/dist/ui/utils/highlight.js.map +1 -1
- package/dist/ui/utils/markdown-renderer.d.ts +6 -0
- package/dist/ui/utils/markdown-renderer.js +21 -5
- package/dist/ui/utils/markdown-renderer.js.map +1 -1
- package/dist/utils/format.d.ts +0 -4
- package/dist/utils/format.js +0 -7
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/process.js +3 -3
- package/dist/utils/process.js.map +1 -1
- package/package.json +5 -8
- package/dist/ui/activity-phrases.d.ts +0 -10
- package/dist/ui/activity-phrases.js +0 -184
- package/dist/ui/activity-phrases.js.map +0 -1
- package/dist/ui/components/PlanApproval.d.ts +0 -7
- package/dist/ui/components/PlanApproval.js +0 -60
- package/dist/ui/components/PlanApproval.js.map +0 -1
- package/dist/ui/components/PlanBanner.d.ts +0 -5
- package/dist/ui/components/PlanBanner.js +0 -31
- package/dist/ui/components/PlanBanner.js.map +0 -1
- package/dist/ui/components/PlanProgress.d.ts +0 -6
- package/dist/ui/components/PlanProgress.js +0 -30
- package/dist/ui/components/PlanProgress.js.map +0 -1
- package/dist/ui/hooks/useMinDisplayTime.d.ts +0 -10
- package/dist/ui/hooks/useMinDisplayTime.js +0 -34
- package/dist/ui/hooks/useMinDisplayTime.js.map +0 -1
- package/dist/ui/hooks/useSessionManager.d.ts +0 -12
- package/dist/ui/hooks/useSessionManager.js +0 -43
- package/dist/ui/hooks/useSessionManager.js.map +0 -1
- package/dist/ui/hooks/useSlashCommands.d.ts +0 -6
- package/dist/ui/hooks/useSlashCommands.js +0 -11
- package/dist/ui/hooks/useSlashCommands.js.map +0 -1
- package/dist/ui/terminal-history-spacing.d.ts +0 -1
- package/dist/ui/terminal-history-spacing.js +0 -2
- package/dist/ui/terminal-history-spacing.js.map +0 -1
- package/dist/ui/utils/markdown-cache.d.ts +0 -10
- package/dist/ui/utils/markdown-cache.js +0 -58
- package/dist/ui/utils/markdown-cache.js.map +0 -1
- package/dist/ui/utils/token-to-ansi.d.ts +0 -8
- package/dist/ui/utils/token-to-ansi.js +0 -323
- package/dist/ui/utils/token-to-ansi.js.map +0 -1
package/dist/app-sidecar.js
CHANGED
|
@@ -42,7 +42,7 @@ import { SettingsManager } from "./core/settings-manager.js";
|
|
|
42
42
|
import { getModel, getDefaultThinkingLevel, getContextWindow, clearRuntimeModels, registerRuntimeModels, } from "./core/model-registry.js";
|
|
43
43
|
import { resolveStartOrFallback } from "./core/resolve-start.js";
|
|
44
44
|
import { getGitBranch, getGitDirtyFileCount, isGitRepo } from "./utils/git.js";
|
|
45
|
-
import {
|
|
45
|
+
import { scoreProjectHealth } from "./core/project-health-score.js";
|
|
46
46
|
import { createRepoPolls } from "./app-sidecar/repo-polls.js";
|
|
47
47
|
import { extractPlanSteps } from "./utils/plan-steps.js";
|
|
48
48
|
import { getSupportedThinkingLevels, isThinkingLevelSupported } from "./core/thinking-level.js";
|
|
@@ -54,7 +54,7 @@ import { installTerminationHandlers } from "./core/shutdown.js";
|
|
|
54
54
|
import { stopRadio } from "./core/radio.js";
|
|
55
55
|
import { enrichProcessPath } from "./core/shell-path.js";
|
|
56
56
|
import { createElicitationBridge } from "./core/mcp/index.js";
|
|
57
|
-
import { askSoftDeadlineMs, createAskUserBridge, deliverLateAnswer } from "./core/ask-user.js";
|
|
57
|
+
import { askSoftDeadlineMs, createAskUserBridge, deliverLateAnswer, } from "./core/ask-user.js";
|
|
58
58
|
import { createAskUserTool } from "./tools/ask-user.js";
|
|
59
59
|
import { createUsageService } from "./app-sidecar/usage.js";
|
|
60
60
|
import { daemonReadBody, daemonJson, json } from "./app-sidecar/http.js";
|
|
@@ -64,6 +64,10 @@ import { ALL_PROVIDERS, loadProjectModelPrefs, loadNolanModelPref, loadAutopilot
|
|
|
64
64
|
import { NOLAN_ALLOWED_TOOLS } from "./app-sidecar/nolan-context.js";
|
|
65
65
|
import { createProgressManager } from "./app-sidecar/progress-manager.js";
|
|
66
66
|
import { handleSessionRoutes } from "./app-sidecar/session-routes.js";
|
|
67
|
+
import { handleGoalRoutes } from "./app-sidecar/goal-routes.js";
|
|
68
|
+
import { readGitState, resolveGitCwd } from "./app-sidecar/git-target.js";
|
|
69
|
+
import { goalPhase, goalsRoot, isGoalDir, startGoalOnAnswer, readGoal, } from "./core/goals-store.js";
|
|
70
|
+
import { createGoalsAgentSession, goalCritterPick, loadGoalBriefExtras, } from "./goals-agent/goals-agent.js";
|
|
67
71
|
import { handleWorkspaceRoutes } from "./app-sidecar/workspace-routes.js";
|
|
68
72
|
import { handleHistoryRoutes } from "./app-sidecar/history-routes.js";
|
|
69
73
|
import { handlePromptRoutes } from "./app-sidecar/prompt-routes.js";
|
|
@@ -176,6 +180,18 @@ async function main() {
|
|
|
176
180
|
for (const ctx of sessions.values())
|
|
177
181
|
ctx.broadcast(type, data);
|
|
178
182
|
};
|
|
183
|
+
const sessionIdsAt = (cwd) => {
|
|
184
|
+
const target = path.resolve(cwd);
|
|
185
|
+
return [...sessions.values()]
|
|
186
|
+
.filter((ctx) => path.resolve(ctx.cwd) === target)
|
|
187
|
+
.map((ctx) => ctx.id)
|
|
188
|
+
.sort();
|
|
189
|
+
};
|
|
190
|
+
/** Whether any live window session in `cwd` is working right now. */
|
|
191
|
+
const busyAt = (cwd) => {
|
|
192
|
+
const target = path.resolve(cwd);
|
|
193
|
+
return [...sessions.values()].some((ctx) => path.resolve(ctx.cwd) === target && ctx.isBusy());
|
|
194
|
+
};
|
|
179
195
|
// Providers currently mid-OAuth in some window. Daemon-level so two windows
|
|
180
196
|
// cannot race two browser flows for the same provider into one auth file.
|
|
181
197
|
const oauthInFlightProviders = new Set();
|
|
@@ -270,6 +286,8 @@ async function main() {
|
|
|
270
286
|
broadcastAll,
|
|
271
287
|
oauthInFlightProviders,
|
|
272
288
|
repoPolls,
|
|
289
|
+
sessionIdsAt,
|
|
290
|
+
busyAt,
|
|
273
291
|
}, { id, mode, chatAgent, cwd: sessionCwd, sessionPath });
|
|
274
292
|
sessions.set(id, ctx);
|
|
275
293
|
log("INFO", "app-sidecar", "session created", {
|
|
@@ -278,7 +296,13 @@ async function main() {
|
|
|
278
296
|
chatAgent,
|
|
279
297
|
cwd: sessionCwd,
|
|
280
298
|
});
|
|
281
|
-
|
|
299
|
+
// The conversation file too, so the app can reopen this exact
|
|
300
|
+
// session if the daemon restarts (a new session has no path yet
|
|
301
|
+
// on the app's side).
|
|
302
|
+
daemonJson(res, 200, {
|
|
303
|
+
sessionId: id,
|
|
304
|
+
sessionPath: ctx.session.getState().sessionPath ?? null,
|
|
305
|
+
});
|
|
282
306
|
}
|
|
283
307
|
catch (err) {
|
|
284
308
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -432,7 +456,7 @@ async function main() {
|
|
|
432
456
|
* logger, PATH, shared auth file, and radio live at the daemon level.
|
|
433
457
|
*/
|
|
434
458
|
async function createSession(deps, opts) {
|
|
435
|
-
const { auth, progress, memoryStore, jiwaStore, broadcastAll, oauthInFlightProviders, repoPolls, } = deps;
|
|
459
|
+
const { auth, progress, memoryStore, jiwaStore, broadcastAll, oauthInFlightProviders, repoPolls, sessionIdsAt, busyAt, } = deps;
|
|
436
460
|
const paths = deps.paths;
|
|
437
461
|
const mode = opts.mode;
|
|
438
462
|
let chatAgent = opts.chatAgent;
|
|
@@ -441,6 +465,10 @@ async function createSession(deps, opts) {
|
|
|
441
465
|
// root; create it on first use so a fresh install can start a video at once.
|
|
442
466
|
if (mode === "motion")
|
|
443
467
|
await fs.mkdir(cwd, { recursive: true });
|
|
468
|
+
// A coding session whose cwd is a Goal folder is that goal's coordinator.
|
|
469
|
+
let goal = mode === "code" && isGoalDir(goalsRoot(paths.agentDir), cwd)
|
|
470
|
+
? await readGoal(cwd).then((record) => (record ? { dir: cwd, record } : null))
|
|
471
|
+
: null;
|
|
444
472
|
// Base host for parsing request-URL query params (value is irrelevant to
|
|
445
473
|
// parsing); the daemon owns the real listen host.
|
|
446
474
|
const host = "127.0.0.1";
|
|
@@ -586,7 +614,9 @@ async function createSession(deps, opts) {
|
|
|
586
614
|
onClosed: (ids) => broadcast("ask_user_closed", { ids }),
|
|
587
615
|
onLateAnswer: (late) => {
|
|
588
616
|
log("INFO", "app-sidecar", "ask_user late answer queued", { id: late.prompt.id });
|
|
589
|
-
|
|
617
|
+
// A late "Start the goal now" starts the goal before EZ Goals hears it,
|
|
618
|
+
// so the run it wakes into may already hand out work.
|
|
619
|
+
void startGoalFrom({ action: "answer", answers: late.answers }, late.prompt.questions).then(() => deliverLateAnswer(late, {
|
|
590
620
|
queueMessage: (text) => session.queueMessage(text),
|
|
591
621
|
isBusy: () => running || runClaim.active || autopilotActive,
|
|
592
622
|
onQueued: () => broadcast("queued", {
|
|
@@ -594,13 +624,44 @@ async function createSession(deps, opts) {
|
|
|
594
624
|
messages: session.listQueuedMessages(),
|
|
595
625
|
}),
|
|
596
626
|
startIdleRun: () => void runStrandedQueue(),
|
|
597
|
-
});
|
|
627
|
+
}));
|
|
598
628
|
},
|
|
599
629
|
});
|
|
600
|
-
|
|
630
|
+
// A goal starts when the user picks "Start the goal now" from EZ Goals'
|
|
631
|
+
// question: answered while it waits, or late, after its deadline (see
|
|
632
|
+
// onLateAnswer). Recorded once, and announced so the window can switch to
|
|
633
|
+
// the goal's world view. Never throws: a failed write leaves it briefing.
|
|
634
|
+
const startGoalFrom = async (result, questions) => {
|
|
635
|
+
const current = goal;
|
|
636
|
+
if (!current)
|
|
637
|
+
return;
|
|
638
|
+
const started = await startGoalOnAnswer(current.dir, current.record, result, new Date(), questions).catch((err) => {
|
|
639
|
+
log("WARN", "app-sidecar", "couldn't record goal start", {
|
|
640
|
+
error: err instanceof Error ? err.message : String(err),
|
|
641
|
+
});
|
|
642
|
+
return null;
|
|
643
|
+
});
|
|
644
|
+
if (started) {
|
|
645
|
+
goal = { dir: current.dir, record: started };
|
|
646
|
+
broadcast("goal_state", { id: started.id, phase: goalPhase(started) });
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
const askUserTool = createAskUserTool(async (request) => {
|
|
650
|
+
const result = await asks.park(request);
|
|
651
|
+
await startGoalFrom(result, request.questions);
|
|
652
|
+
return result;
|
|
653
|
+
});
|
|
601
654
|
// The session file path to resume (passed by the daemon's POST /session);
|
|
602
|
-
// empty/unset starts a fresh session.
|
|
603
|
-
|
|
655
|
+
// empty/unset starts a fresh session. A file that's gone (retention cleaned
|
|
656
|
+
// it up, or its goal was deleted) starts fresh too: failing instead left
|
|
657
|
+
// the window with no session at all, so not even its goal list loaded.
|
|
658
|
+
const resumeSessionPath = opts.sessionPath &&
|
|
659
|
+
(await fs.access(opts.sessionPath).then(() => true, () => false))
|
|
660
|
+
? opts.sessionPath
|
|
661
|
+
: undefined;
|
|
662
|
+
if (opts.sessionPath && !resumeSessionPath) {
|
|
663
|
+
log("WARN", "app-sidecar", "session file gone, starting fresh", { cwd });
|
|
664
|
+
}
|
|
604
665
|
let abort = new AbortController();
|
|
605
666
|
const baseSessionOptions = {
|
|
606
667
|
provider,
|
|
@@ -647,6 +708,19 @@ async function createSession(deps, opts) {
|
|
|
647
708
|
additionalTools: [askUserTool],
|
|
648
709
|
});
|
|
649
710
|
}
|
|
711
|
+
else if (goal) {
|
|
712
|
+
// A Goal window talks to EZ Goals, the orchestrator, not the coding agent.
|
|
713
|
+
session = createGoalsAgentSession({
|
|
714
|
+
...baseSessionOptions,
|
|
715
|
+
goal,
|
|
716
|
+
// Memory and the attached projects' rules, read once for this session.
|
|
717
|
+
extras: await loadGoalBriefExtras(goal),
|
|
718
|
+
// Read live: the goal turns active mid-session when the user starts it.
|
|
719
|
+
isActive: () => Boolean(goal?.record.startedAt),
|
|
720
|
+
getChildModel: () => goalCritterPick(goal?.record),
|
|
721
|
+
additionalTools: [askUserTool],
|
|
722
|
+
});
|
|
723
|
+
}
|
|
650
724
|
else {
|
|
651
725
|
session = new AgentSession({
|
|
652
726
|
...baseSessionOptions,
|
|
@@ -685,6 +759,18 @@ async function createSession(deps, opts) {
|
|
|
685
759
|
});
|
|
686
760
|
}
|
|
687
761
|
await session.initialize();
|
|
762
|
+
// A goal's attached folders join its workspace so the coordinator can read
|
|
763
|
+
// them. A resumed session may already carry them; `addDirectory` skips those.
|
|
764
|
+
if (goal) {
|
|
765
|
+
for (const item of goal.record.context) {
|
|
766
|
+
if (item.kind !== "folder")
|
|
767
|
+
continue;
|
|
768
|
+
const added = await session.addDirectory(item.path);
|
|
769
|
+
if (!added.ok && !added.error.startsWith("Already in the workspace")) {
|
|
770
|
+
log("WARN", "app-sidecar", "goal context folder unavailable", { error: added.error });
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
688
774
|
if (mode === "chat") {
|
|
689
775
|
const restoredAgent = [...session.getAppMarkers()]
|
|
690
776
|
.reverse()
|
|
@@ -798,23 +884,38 @@ async function createSession(deps, opts) {
|
|
|
798
884
|
});
|
|
799
885
|
}
|
|
800
886
|
// Workspace extras (context window, git status, background tasks). Git state
|
|
801
|
-
// is resolved
|
|
802
|
-
//
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
let
|
|
810
|
-
let gitIsRepo = initialGitIsRepo;
|
|
811
|
-
let gitDirtyFileCount = initialDirtyFileCount;
|
|
887
|
+
// is resolved at startup and refreshed after every run; the context window
|
|
888
|
+
// follows the active model. A Goal window reads git from its attached
|
|
889
|
+
// project folder, not the goal folder (see resolveGitCwd), and re-targets
|
|
890
|
+
// when the goal's folders change (`retargetGit`).
|
|
891
|
+
let gitCwd = await resolveGitCwd(cwd, goal?.record ?? null);
|
|
892
|
+
const initialGit = await readGitState(gitCwd);
|
|
893
|
+
let gitBranch = initialGit.branch;
|
|
894
|
+
let gitIsRepo = initialGit.isRepo;
|
|
895
|
+
let gitDirtyFileCount = initialGit.dirtyFileCount;
|
|
812
896
|
// Open issue/PR counts for the origin repo's GitHub slug, via the `gh` CLI's
|
|
813
897
|
// auth. null = unknown (gh missing/unauthed, non-GitHub origin) → chips hidden.
|
|
814
|
-
|
|
898
|
+
let gitHubSlug = initialGit.gitHubSlug;
|
|
815
899
|
let gitHubIssues = null;
|
|
816
900
|
let gitHubPRs = null;
|
|
817
901
|
let gitHubCI = null;
|
|
902
|
+
// Latest Project Health scan (code mode only); scored with the live CI result.
|
|
903
|
+
let projectHealthScan = null;
|
|
904
|
+
// Footer extras go out on every git/CI/task change; score only when the scan
|
|
905
|
+
// or CI result actually changed.
|
|
906
|
+
let scoredHealth = null;
|
|
907
|
+
function currentProjectHealth() {
|
|
908
|
+
if (!projectHealthScan)
|
|
909
|
+
return null;
|
|
910
|
+
if (scoredHealth?.scan !== projectHealthScan || scoredHealth.ci !== gitHubCI) {
|
|
911
|
+
scoredHealth = {
|
|
912
|
+
scan: projectHealthScan,
|
|
913
|
+
ci: gitHubCI,
|
|
914
|
+
health: scoreProjectHealth(projectHealthScan, gitHubCI),
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
return scoredHealth.health;
|
|
918
|
+
}
|
|
818
919
|
function currentContextWindow() {
|
|
819
920
|
const st = session.getState();
|
|
820
921
|
return getContextWindow(st.model, { provider: st.provider, accountId: st.accountId });
|
|
@@ -831,6 +932,7 @@ async function createSession(deps, opts) {
|
|
|
831
932
|
gitHubPRs,
|
|
832
933
|
gitHubCI,
|
|
833
934
|
gitHubRepoUrl: gitHubSlug ? `https://github.com/${gitHubSlug}` : null,
|
|
935
|
+
projectHealth: currentProjectHealth(),
|
|
834
936
|
tasks: session.listBackgroundProcesses(),
|
|
835
937
|
// Roots added with /add-dir — the header shows a badge when non-empty.
|
|
836
938
|
additionalRoots: session.getAdditionalRoots(),
|
|
@@ -1404,7 +1506,11 @@ async function createSession(deps, opts) {
|
|
|
1404
1506
|
}
|
|
1405
1507
|
finally {
|
|
1406
1508
|
const cancelled = runLifecycle.isCancellationRequested(generation);
|
|
1407
|
-
|
|
1509
|
+
// A plain reply has no real end (the user keeps building), so checks are
|
|
1510
|
+
// left to the commit and unchecked edits are not "Unverified". Only an
|
|
1511
|
+
// Autopilot cycle, which does end, reports unchecked work. A check that
|
|
1512
|
+
// ran and failed still shows as failed (from the evidence).
|
|
1513
|
+
const verificationProblem = cancelled || !autopilotActive ? null : session.getVerificationProblem();
|
|
1408
1514
|
if (runSucceeded && verificationProblem && ownsGeneration) {
|
|
1409
1515
|
// Expected control outcome: run_end and the journal already carry Unverified.
|
|
1410
1516
|
// Do not format it as a crash or persist a misleading error marker.
|
|
@@ -1421,14 +1527,15 @@ async function createSession(deps, opts) {
|
|
|
1421
1527
|
// A run may have switched branches, changed files, or spawned/finished
|
|
1422
1528
|
// background tasks. Refresh the workspace extras once it settles.
|
|
1423
1529
|
[gitBranch, gitIsRepo, gitDirtyFileCount] = await Promise.all([
|
|
1424
|
-
getGitBranch(
|
|
1425
|
-
isGitRepo(
|
|
1426
|
-
getGitDirtyFileCount(
|
|
1530
|
+
getGitBranch(gitCwd).catch(() => gitBranch),
|
|
1531
|
+
isGitRepo(gitCwd).catch(() => gitIsRepo),
|
|
1532
|
+
getGitDirtyFileCount(gitCwd).catch(() => gitDirtyFileCount),
|
|
1427
1533
|
]);
|
|
1428
1534
|
// A run may have opened/closed issues or PRs — refresh fire-and-forget so
|
|
1429
1535
|
// teardown isn't delayed by the network. Broadcasts itself on change.
|
|
1430
|
-
|
|
1431
|
-
|
|
1536
|
+
repoWatch.gitHubCounts?.refresh();
|
|
1537
|
+
repoWatch.ci.refresh();
|
|
1538
|
+
repoWatch.health?.refresh();
|
|
1432
1539
|
// Serialize behind any marker/tool-triggered refresh so the terminal
|
|
1433
1540
|
// progress snapshot uses the live plan file. Once every canonical step
|
|
1434
1541
|
// is complete, remove the approved plan from future system prompts and
|
|
@@ -1771,30 +1878,82 @@ async function createSession(deps, opts) {
|
|
|
1771
1878
|
// (they change outside the agent: editor saves, commits, teammates). Branch/
|
|
1772
1879
|
// repo state already refreshes after agent runs. The pollers are shared with
|
|
1773
1880
|
// every other window on the same repo; each window broadcasts only changes.
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
gitDirtyFileCount = next;
|
|
1779
|
-
broadcast("extras", footerExtras());
|
|
1780
|
-
});
|
|
1781
|
-
// Failed checks publish nothing, so the chips keep their last-known numbers
|
|
1782
|
-
// instead of flickering off on a timeout. None when origin isn't GitHub.
|
|
1783
|
-
const gitHubCountsPoll = gitHubSlug
|
|
1784
|
-
? repoPolls.gitHubCounts.subscribe(gitHubSlug, (counts) => {
|
|
1785
|
-
if (counts.issues === gitHubIssues && counts.prs === gitHubPRs)
|
|
1881
|
+
function watchRepo(dir) {
|
|
1882
|
+
const repoKey = path.resolve(dir);
|
|
1883
|
+
const dirtyFiles = repoPolls.dirtyFiles.subscribe(repoKey, (next) => {
|
|
1884
|
+
if (next === gitDirtyFileCount)
|
|
1786
1885
|
return;
|
|
1787
|
-
|
|
1788
|
-
gitHubPRs = counts.prs;
|
|
1886
|
+
gitDirtyFileCount = next;
|
|
1789
1887
|
broadcast("extras", footerExtras());
|
|
1790
|
-
})
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1888
|
+
});
|
|
1889
|
+
// Failed checks publish nothing, so the chips keep their last-known numbers
|
|
1890
|
+
// instead of flickering off on a timeout. None when origin isn't GitHub.
|
|
1891
|
+
const gitHubCounts = gitHubSlug
|
|
1892
|
+
? repoPolls.gitHubCounts.subscribe(gitHubSlug, (counts) => {
|
|
1893
|
+
if (counts.issues === gitHubIssues && counts.prs === gitHubPRs)
|
|
1894
|
+
return;
|
|
1895
|
+
gitHubIssues = counts.issues;
|
|
1896
|
+
gitHubPRs = counts.prs;
|
|
1897
|
+
broadcast("extras", footerExtras());
|
|
1898
|
+
})
|
|
1899
|
+
: null;
|
|
1900
|
+
const ci = repoPolls.gitHubCI.subscribe(repoKey, (next) => {
|
|
1901
|
+
if (JSON.stringify(next) === JSON.stringify(gitHubCI))
|
|
1902
|
+
return;
|
|
1903
|
+
gitHubCI = next;
|
|
1904
|
+
broadcast("extras", footerExtras());
|
|
1905
|
+
});
|
|
1906
|
+
// Project Health is a code-workspace signal; chat and motion windows skip the scan.
|
|
1907
|
+
const health = mode === "code"
|
|
1908
|
+
? repoPolls.projectHealth.subscribe(repoKey, (next) => {
|
|
1909
|
+
if (JSON.stringify(next) === JSON.stringify(projectHealthScan))
|
|
1910
|
+
return;
|
|
1911
|
+
projectHealthScan = next;
|
|
1912
|
+
broadcast("extras", footerExtras());
|
|
1913
|
+
})
|
|
1914
|
+
: null;
|
|
1915
|
+
return {
|
|
1916
|
+
gitHubCounts,
|
|
1917
|
+
ci,
|
|
1918
|
+
health,
|
|
1919
|
+
unsubscribe() {
|
|
1920
|
+
dirtyFiles.unsubscribe();
|
|
1921
|
+
gitHubCounts?.unsubscribe();
|
|
1922
|
+
ci.unsubscribe();
|
|
1923
|
+
health?.unsubscribe();
|
|
1924
|
+
},
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
let repoWatch = watchRepo(gitCwd);
|
|
1928
|
+
let repoWatchStopped = false;
|
|
1929
|
+
let gitTargetGeneration = 0;
|
|
1930
|
+
/**
|
|
1931
|
+
* A goal's attached folders changed: point the titlebar's git state at the
|
|
1932
|
+
* new project folder. No-op when the target is unchanged; a newer call or
|
|
1933
|
+
* teardown supersedes one still reading git.
|
|
1934
|
+
*/
|
|
1935
|
+
async function retargetGit() {
|
|
1936
|
+
const generation = ++gitTargetGeneration;
|
|
1937
|
+
const next = await resolveGitCwd(cwd, goal?.record ?? null);
|
|
1938
|
+
if (generation !== gitTargetGeneration || repoWatchStopped || next === gitCwd)
|
|
1939
|
+
return;
|
|
1940
|
+
const state = await readGitState(next);
|
|
1941
|
+
if (generation !== gitTargetGeneration || repoWatchStopped)
|
|
1794
1942
|
return;
|
|
1795
|
-
|
|
1943
|
+
repoWatch.unsubscribe();
|
|
1944
|
+
gitCwd = next;
|
|
1945
|
+
gitBranch = state.branch;
|
|
1946
|
+
gitIsRepo = state.isRepo;
|
|
1947
|
+
gitDirtyFileCount = state.dirtyFileCount;
|
|
1948
|
+
gitHubSlug = state.gitHubSlug;
|
|
1949
|
+
// The old repo's numbers don't apply; the new watchers fill these in.
|
|
1950
|
+
gitHubIssues = null;
|
|
1951
|
+
gitHubPRs = null;
|
|
1952
|
+
gitHubCI = null;
|
|
1953
|
+
projectHealthScan = null;
|
|
1954
|
+
repoWatch = watchRepo(gitCwd);
|
|
1796
1955
|
broadcast("extras", footerExtras());
|
|
1797
|
-
}
|
|
1956
|
+
}
|
|
1798
1957
|
const routeCtx = {
|
|
1799
1958
|
get session() {
|
|
1800
1959
|
return session;
|
|
@@ -1846,6 +2005,23 @@ async function createSession(deps, opts) {
|
|
|
1846
2005
|
cwd,
|
|
1847
2006
|
asks,
|
|
1848
2007
|
runClaim,
|
|
2008
|
+
sessionId: opts.id,
|
|
2009
|
+
sessionIdsAt,
|
|
2010
|
+
busyAt,
|
|
2011
|
+
get goal() {
|
|
2012
|
+
return goal;
|
|
2013
|
+
},
|
|
2014
|
+
set goal(v) {
|
|
2015
|
+
const foldersBefore = JSON.stringify(goal?.record.context ?? []);
|
|
2016
|
+
goal = v;
|
|
2017
|
+
if (JSON.stringify(goal?.record.context ?? []) !== foldersBefore) {
|
|
2018
|
+
retargetGit().catch((err) => {
|
|
2019
|
+
log("WARN", "app-sidecar", "goal git retarget failed", {
|
|
2020
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2021
|
+
});
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
},
|
|
1849
2025
|
broadcast,
|
|
1850
2026
|
get scheduledRunActive() {
|
|
1851
2027
|
return scheduledRunActive;
|
|
@@ -2003,6 +2179,8 @@ async function createSession(deps, opts) {
|
|
|
2003
2179
|
return;
|
|
2004
2180
|
if (handleWorkspaceRoutes(routeCtx, req, res, url, method))
|
|
2005
2181
|
return;
|
|
2182
|
+
if (handleGoalRoutes(routeCtx, req, res, url, method))
|
|
2183
|
+
return;
|
|
2006
2184
|
if (handleHistoryRoutes(routeCtx, req, res, url, method))
|
|
2007
2185
|
return;
|
|
2008
2186
|
if (handlePromptRoutes(routeCtx, req, res, url, method))
|
|
@@ -2020,6 +2198,12 @@ async function createSession(deps, opts) {
|
|
|
2020
2198
|
json(res, 404, { error: "not found" });
|
|
2021
2199
|
}
|
|
2022
2200
|
async function dispose() {
|
|
2201
|
+
// A window closed (or switched project) mid-run: stop the run itself, any
|
|
2202
|
+
// run-all sweep or autopilot turn, and a model download this window
|
|
2203
|
+
// started. Otherwise they keep streaming tokens and running tools on a
|
|
2204
|
+
// session that is being torn down.
|
|
2205
|
+
abortOwnedWork();
|
|
2206
|
+
cancelHfPull();
|
|
2023
2207
|
releaseRunAwake?.();
|
|
2024
2208
|
releaseRunAwake = null;
|
|
2025
2209
|
releaseSubagentAwake?.();
|
|
@@ -2029,9 +2213,8 @@ async function createSession(deps, opts) {
|
|
|
2029
2213
|
tasksPollStopped = true;
|
|
2030
2214
|
if (tasksPoll)
|
|
2031
2215
|
clearTimeout(tasksPoll);
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
ciPoll.unsubscribe();
|
|
2216
|
+
repoWatchStopped = true;
|
|
2217
|
+
repoWatch.unsubscribe();
|
|
2035
2218
|
// Stop the Telegram serve loop + dispose its per-chat sessions.
|
|
2036
2219
|
if (serveController)
|
|
2037
2220
|
await serveController.stop().catch(() => { });
|
|
@@ -2062,6 +2245,10 @@ async function createSession(deps, opts) {
|
|
|
2062
2245
|
handle,
|
|
2063
2246
|
dispose,
|
|
2064
2247
|
stopBackgroundProcesses,
|
|
2248
|
+
isBusy: () => running ||
|
|
2249
|
+
runClaim.active ||
|
|
2250
|
+
autopilotActive ||
|
|
2251
|
+
session.listSubAgents().some((w) => w.state === "running" || w.state === "starting"),
|
|
2065
2252
|
};
|
|
2066
2253
|
}
|
|
2067
2254
|
main().catch(async (err) => {
|