gentle-pi 3.7.0 → 4.0.0
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 +37 -6
- package/assets/agents/gentle-ai-explore.md +4 -4
- package/assets/agents/gentle-ai-verify.md +6 -4
- package/assets/agents/gentle-ai-worker.md +7 -9
- package/assets/orchestrator-delegation.md +40 -41
- package/assets/orchestrator-memory.md +1 -22
- package/assets/orchestrator-skills.md +1 -1
- package/assets/orchestrator.md +12 -24
- package/assets/support/strict-tdd-verify.md +4 -266
- package/assets/support/strict-tdd.md +8 -360
- package/bin/gentle-shell.mjs +254 -29
- package/docs/delegated-verification.md +26 -1
- package/docs/gentle-agents-activity.md +24 -0
- package/docs/gentle-shell.md +92 -28
- package/docs/native-authority-architecture.md +2 -2
- package/docs/prompt-history.md +280 -0
- package/docs/readme-reference.md +112 -206
- package/docs/telemetry.md +1 -1
- package/docs/yolo-mode.md +86 -0
- package/extensions/child-context.ts +26 -0
- package/extensions/child-safety.ts +23 -0
- package/extensions/gentle-agents.ts +429 -262
- package/extensions/gentle-ai.ts +781 -683
- package/extensions/gentle-shell.ts +1375 -88
- package/extensions/gentle-stats.ts +101 -0
- package/extensions/gentle-todo.ts +18 -7
- package/extensions/history/atomic-write.ts +38 -0
- package/extensions/history/hide-prompts.ts +183 -0
- package/extensions/history/index.ts +1419 -0
- package/extensions/history/load-shared-history.ts +39 -0
- package/extensions/history/selector-helpers.ts +538 -0
- package/extensions/history/session-scan.ts +233 -0
- package/extensions/history/store.ts +1119 -0
- package/extensions/nan-provider.ts +6 -0
- package/extensions/quiet-tools.ts +179 -87
- package/extensions/resume-hint.ts +60 -0
- package/extensions/skill-registry.ts +16 -12
- package/extensions/startup-banner.ts +60 -31
- package/lib/agent-assets.ts +604 -0
- package/lib/agent-profile-pin.ts +12 -0
- package/lib/agents-message-delivery.ts +181 -0
- package/lib/agents-protocol.ts +60 -0
- package/lib/agents-runner.ts +96 -98
- package/lib/agents-view.ts +26 -3
- package/lib/agents-widget.ts +16 -9
- package/lib/append-system-prompt.ts +21 -0
- package/lib/bounded-writer-admission.ts +147 -0
- package/lib/card-style-policy.ts +60 -0
- package/lib/child-context-files.ts +166 -0
- package/lib/codemode-renderer.ts +185 -0
- package/lib/command-palette-catalog.ts +3 -9
- package/lib/command-palette.ts +25 -14
- package/lib/destructive-command-guard.ts +144 -0
- package/lib/gentle-ai-elapsed-store.ts +87 -0
- package/lib/gentle-ai-renderer.ts +196 -39
- package/lib/gentle-shell-launcher.ts +24 -13
- package/lib/gentle-shell-resume-hint.ts +176 -0
- package/lib/history-capture-policy.ts +95 -0
- package/lib/model-routing-authority.ts +5 -1
- package/lib/nan-provider.ts +227 -0
- package/lib/native-review-cli.ts +49 -108
- package/lib/odd-phase-inference.ts +231 -0
- package/lib/odd-phase.ts +141 -0
- package/lib/overlay-repaint.ts +26 -0
- package/lib/pi-tui-keys.ts +53 -0
- package/lib/review-candidate-view-owner.ts +67 -17
- package/lib/review-candidate-view.ts +112 -25
- package/lib/review-reminder-receipt.ts +48 -8
- package/lib/review-risk-assessment.ts +156 -11
- package/lib/review-sidebar-state.ts +223 -0
- package/lib/selection-engine.ts +515 -0
- package/lib/session-messaging-grants.ts +135 -0
- package/lib/session-worktree-registry.ts +14 -2
- package/lib/shell-bar.ts +179 -24
- package/lib/shell-card.ts +287 -18
- package/lib/shell-changes-view.ts +2 -1
- package/lib/shell-prompt.ts +102 -6
- package/lib/shell-sidebar-layout.ts +78 -14
- package/lib/shell-sidebar.ts +15 -1
- package/lib/shell-todo.ts +23 -13
- package/lib/shell-usage-view.ts +9 -4
- package/lib/shell-usage.ts +66 -10
- package/lib/stats-collector.ts +381 -0
- package/lib/stats-view.ts +431 -0
- package/lib/theme-customization.ts +52 -0
- package/lib/vim-editor-adapter.ts +379 -0
- package/lib/vim-normal-engine.ts +154 -0
- package/lib/vim-operator-engine.ts +416 -0
- package/lib/vim-policy.ts +49 -0
- package/lib/vim-visual-engine.ts +107 -0
- package/lib/visual-customization-policy.ts +108 -0
- package/lib/visual-customize-view.ts +330 -0
- package/lib/visual-profiles.ts +228 -0
- package/lib/yolo-session-policy.ts +240 -0
- package/package.json +20 -8
- package/runtime/gentle-shell-launcher.mjs +23 -12
- package/runtime/gentle-shell-resume-hint.mjs +177 -0
- package/runtime/native-review-cli.mjs +49 -108
- package/runtime/review-risk-assessment.mjs +154 -9
- package/scripts/build-runtime-modules.mjs +1 -0
- package/scripts/gentle-ai-installer.mjs +14 -13
- package/scripts/mirror-odd-routing.mjs +2 -2
- package/scripts/run-test-suite.mjs +76 -0
- package/scripts/test-packed-runner.mjs +31 -14
- package/scripts/verify-package-files.mjs +11 -22
- package/skills/branch-pr/SKILL.md +24 -52
- package/skills/chained-pr/SKILL.md +31 -15
- package/skills/chained-pr/references/chaining-details.md +31 -20
- package/skills/gentle-ai/SKILL.md +9 -15
- package/skills/issue-creation/SKILL.md +8 -2
- package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
- package/skills/work-unit-commits/SKILL.md +4 -3
- package/tests/agent-profiles.test.ts +18 -0
- package/tests/agents-fake-child.ts +2 -2
- package/tests/agents-message-delivery.test.ts +106 -0
- package/tests/agents-protocol.test.ts +40 -0
- package/tests/agents-runner.test.ts +378 -89
- package/tests/agents-view-thread-identity.test.ts +169 -0
- package/tests/agents-view.test.ts +8 -2
- package/tests/agents-widget.test.ts +154 -15
- package/tests/append-system-prompt-route.test.ts +160 -0
- package/tests/append-system-prompt.test.ts +46 -0
- package/tests/artifact-language.test.ts +19 -213
- package/tests/ask-user-question.test.ts +44 -1
- package/tests/asset-installation-runtime.test.ts +5 -16
- package/tests/autonomous-guard.test.ts +69 -1
- package/tests/bounded-writer-admission.test.ts +95 -0
- package/tests/branch-pr-skill.test.ts +43 -0
- package/tests/card-style-policy.test.ts +55 -0
- package/tests/chained-pr-skill.test.ts +124 -0
- package/tests/child-context-files.test.ts +255 -0
- package/tests/child-safety.test.ts +82 -0
- package/tests/codemode-rendering.test.ts +491 -0
- package/tests/command-palette.test.ts +39 -3
- package/tests/delegated-key-learnings-contract.test.ts +0 -76
- package/tests/destructive-command-guard.test.ts +84 -0
- package/tests/devbinary/native-review-parity.devtest.ts +170 -2
- package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
- package/tests/generic-agent-tools.test.ts +54 -0
- package/tests/gentle-agents.test.ts +1047 -252
- package/tests/gentle-ai-binary.test.ts +3 -3
- package/tests/gentle-ai-elapsed-store.test.ts +68 -0
- package/tests/gentle-ai-installer.test.ts +68 -54
- package/tests/gentle-ai-renderer.test.ts +487 -8
- package/tests/gentle-ai.test.ts +288 -65
- package/tests/gentle-card-text.ts +2 -1
- package/tests/gentle-shell-bin.test.ts +651 -114
- package/tests/gentle-shell-launcher.test.ts +99 -52
- package/tests/gentle-shell-resume-hint.test.ts +270 -0
- package/tests/gentle-shell.test.ts +3839 -187
- package/tests/gentle-stats.test.ts +152 -0
- package/tests/gentle-theme.test.ts +4 -1
- package/tests/gentle-todo.test.ts +80 -8
- package/tests/history-atomic-write.test.ts +57 -0
- package/tests/history-capture-policy.test.ts +102 -0
- package/tests/history-command-registration.test.ts +164 -0
- package/tests/history-dedupe-entries.test.ts +123 -0
- package/tests/history-delete-backfill.test.ts +190 -0
- package/tests/history-delete-confirm.test.ts +460 -0
- package/tests/history-dispatch.test.ts +180 -0
- package/tests/history-drain-hidden.test.ts +110 -0
- package/tests/history-drain-order.test.ts +98 -0
- package/tests/history-expanded-globals.test.ts +62 -0
- package/tests/history-gc.test.ts +832 -0
- package/tests/history-header-layout.test.ts +265 -0
- package/tests/history-hide-prompts.test.ts +275 -0
- package/tests/history-lazy-windowing.test.ts +508 -0
- package/tests/history-legacy-migrate-v2.test.ts +297 -0
- package/tests/history-load-shared-history.test.ts +53 -0
- package/tests/history-max-results-cap.test.ts +76 -0
- package/tests/history-multi-reader.test.ts +203 -0
- package/tests/history-off-path.test.ts +170 -0
- package/tests/history-openflow-integration.test.ts +173 -0
- package/tests/history-overlay-margin.test.ts +326 -0
- package/tests/history-preview-layout.test.ts +93 -0
- package/tests/history-registry.test.ts +143 -0
- package/tests/history-scope-delete.test.ts +411 -0
- package/tests/history-search-caret-keys.test.ts +142 -0
- package/tests/history-seed-bootstrap.test.ts +170 -0
- package/tests/history-seed-regen.test.ts +129 -0
- package/tests/history-selector-windowing.test.ts +94 -0
- package/tests/history-session-scan-directory.test.ts +87 -0
- package/tests/history-session-scan-extract.test.ts +583 -0
- package/tests/history-session-writer.test.ts +351 -0
- package/tests/history-store-paths.test.ts +79 -0
- package/tests/history-tombstone-exact.test.ts +139 -0
- package/tests/history-wheel-mouse.test.ts +242 -0
- package/tests/inprocess-reviewer.test.ts +29 -19
- package/tests/issue-creation-skill.test.ts +61 -0
- package/tests/model-routing-authority.test.ts +16 -0
- package/tests/nan-provider.test.ts +471 -0
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-cli.test.ts +6 -120
- package/tests/native-review-parity-runtime.test.ts +100 -3
- package/tests/odd-integration.test.ts +67 -0
- package/tests/odd-phase-inference.test.ts +213 -0
- package/tests/odd-phase-loader.test.ts +253 -0
- package/tests/odd-phase.test.ts +307 -0
- package/tests/odd-routing-canonical-ratchet.test.ts +11 -6
- package/tests/odd-routing-contract.test.ts +86 -35
- package/tests/orchestrator-budget.test.ts +14 -39
- package/tests/orchestrator-rdd-ownership.test.ts +3 -3
- package/tests/overlay-repaint.test.ts +74 -0
- package/tests/package-manifest.test.ts +252 -115
- package/tests/packed-runner-owned-path.test.ts +46 -0
- package/tests/persona-single-channel.test.ts +6 -6
- package/tests/provider-defect-handoff.test.ts +3 -11
- package/tests/quiet-bash-runtime.test.ts +76 -0
- package/tests/quiet-tool-rendering.test.ts +409 -184
- package/tests/rdd-aware-verification-contract.test.ts +76 -1
- package/tests/rdd-status-line.test.ts +9 -4
- package/tests/resume-hint-extension.test.ts +122 -0
- package/tests/review-agent-end-preflight.test.ts +176 -12
- package/tests/review-candidate-owner-retry.test.ts +22 -1
- package/tests/review-candidate-view.test.ts +298 -0
- package/tests/review-contract-prompt.test.ts +108 -43
- package/tests/review-controller-lock-status.test.ts +0 -1
- package/tests/review-controller-native-routing.test.ts +611 -5
- package/tests/review-controller-workspace-root.test.ts +163 -4
- package/tests/review-host-relay-routing.test.ts +338 -2
- package/tests/review-integration-v2-forward.test.ts +200 -0
- package/tests/review-ledger-contract.test.ts +10 -34
- package/tests/review-reminder-receipt.test.ts +47 -1
- package/tests/review-risk-assessment.test.ts +498 -6
- package/tests/review-sidebar-state.test.ts +402 -0
- package/tests/run-test-suite.test.ts +124 -0
- package/tests/runtime-harness.mjs +145 -786
- package/tests/runtime-metrics-children.test.ts +16 -23
- package/tests/selection-engine.test.ts +421 -0
- package/tests/session-messaging-grants.test.ts +255 -0
- package/tests/session-worktree-registry.test.ts +77 -0
- package/tests/shell-bar.test.ts +382 -1
- package/tests/shell-card.test.ts +353 -1
- package/tests/shell-changes-view.test.ts +52 -0
- package/tests/shell-prompt.test.ts +94 -2
- package/tests/shell-sidebar-layout.test.ts +325 -21
- package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
- package/tests/shell-todo.test.ts +87 -1
- package/tests/shell-usage-view.test.ts +27 -0
- package/tests/shell-usage.test.ts +73 -0
- package/tests/skill-registry.test.ts +50 -1
- package/tests/startup-banner.test.ts +130 -2
- package/tests/stats-collector.test.ts +195 -0
- package/tests/stats-view.test.ts +202 -0
- package/tests/telemetry-trigger.test.ts +81 -20
- package/tests/theme-customization.test.ts +72 -0
- package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
- package/tests/vim-editor-adapter.test.ts +804 -0
- package/tests/vim-normal-engine.test.ts +101 -0
- package/tests/vim-operator-engine.test.ts +215 -0
- package/tests/vim-policy.test.ts +19 -0
- package/tests/vim-visual-engine.test.ts +52 -0
- package/tests/visual-customization-policy.test.ts +110 -0
- package/tests/visual-customize-view.test.ts +418 -0
- package/tests/visual-profiles.test.ts +87 -0
- package/tests/yolo-customize.test.ts +256 -0
- package/tests/yolo-mode-runtime.test.ts +161 -0
- package/tests/yolo-mode.test.ts +261 -0
- package/tests/yolo-session-policy.test.ts +59 -0
- package/themes/Gentle.json +2 -1
- package/themes/Gentleman-Cute.json +2 -1
- package/themes/Gentleman-Sexy.json +2 -1
- package/assets/agents/sdd-apply.md +0 -159
- package/assets/agents/sdd-archive.md +0 -228
- package/assets/agents/sdd-design.md +0 -49
- package/assets/agents/sdd-explore.md +0 -48
- package/assets/agents/sdd-init.md +0 -56
- package/assets/agents/sdd-onboard.md +0 -52
- package/assets/agents/sdd-proposal.md +0 -64
- package/assets/agents/sdd-remediate.md +0 -37
- package/assets/agents/sdd-research.md +0 -49
- package/assets/agents/sdd-spec.md +0 -192
- package/assets/agents/sdd-status.md +0 -54
- package/assets/agents/sdd-tasks.md +0 -108
- package/assets/agents/sdd-verify.md +0 -124
- package/assets/chains/sdd-full.chain.md +0 -83
- package/assets/chains/sdd-plan.chain.md +0 -56
- package/assets/chains/sdd-verify.chain.md +0 -43
- package/assets/sdd-orchestrator-workflow.md +0 -319
- package/assets/support/sdd-status-contract.md +0 -77
- package/docs/assets/diagrams/sdd-cycle.svg +0 -14
- package/extensions/sdd-init.ts +0 -816
- package/lib/openspec-deltas.ts +0 -156
- package/lib/sdd-preflight.ts +0 -1066
- package/lib/sdd-research-capabilities.ts +0 -94
- package/lib/sdd-status.ts +0 -26
- package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
- package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
- package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
- package/tests/openspec-deltas.test.ts +0 -209
- package/tests/sdd-agent-tools.test.ts +0 -156
- package/tests/sdd-archive-replay.test.ts +0 -82
- package/tests/sdd-classical-continuation.test.ts +0 -74
- package/tests/sdd-execution-routing-contract.test.ts +0 -44
- package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
- package/tests/sdd-native-managed-uptake.test.ts +0 -243
- package/tests/sdd-no-attempts-contract.test.ts +0 -15
- package/tests/sdd-odd-integration.test.ts +0 -33
- package/tests/sdd-optional-research.test.ts +0 -124
- package/tests/sdd-planning-routing-contract.test.ts +0 -45
- package/tests/sdd-preflight-rpc-input.test.ts +0 -125
- package/tests/sdd-preflight.test.ts +0 -541
- package/tests/sdd-research-capabilities.test.ts +0 -114
- package/tests/sdd-research-live.test.ts +0 -241
- package/tests/sdd-selection-transport.test.ts +0 -653
- package/tests/sdd-status.test.ts +0 -9
- package/tests/sdd-task-truth.test.ts +0 -43
package/bin/gentle-shell.mjs
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
// process, filesystem, and child process.
|
|
7
7
|
import {
|
|
8
8
|
accessSync,
|
|
9
|
+
chmodSync,
|
|
9
10
|
closeSync,
|
|
10
11
|
constants as fsConstants,
|
|
11
12
|
existsSync,
|
|
12
13
|
mkdirSync,
|
|
14
|
+
mkdtempSync,
|
|
13
15
|
openSync,
|
|
14
16
|
readdirSync,
|
|
15
17
|
readFileSync,
|
|
@@ -19,8 +21,7 @@ import {
|
|
|
19
21
|
statSync,
|
|
20
22
|
writeFileSync,
|
|
21
23
|
} from "node:fs";
|
|
22
|
-
import {
|
|
23
|
-
import { constants as osConstants, homedir } from "node:os";
|
|
24
|
+
import { constants as osConstants, homedir, tmpdir } from "node:os";
|
|
24
25
|
import { delimiter, dirname, join, resolve as resolvePath } from "node:path";
|
|
25
26
|
import { spawn, spawnSync } from "node:child_process";
|
|
26
27
|
import { fileURLToPath } from "node:url";
|
|
@@ -53,6 +54,13 @@ import {
|
|
|
53
54
|
restoreJsonField,
|
|
54
55
|
shellQuote,
|
|
55
56
|
} from "../runtime/gentle-shell-launcher.mjs";
|
|
57
|
+
import {
|
|
58
|
+
parseResumeHandoff,
|
|
59
|
+
planResumeHint,
|
|
60
|
+
RESUME_HANDOFF_DIR_PREFIX,
|
|
61
|
+
RESUME_HANDOFF_ENV,
|
|
62
|
+
RESUME_HANDOFF_FILE,
|
|
63
|
+
} from "../runtime/gentle-shell-resume-hint.mjs";
|
|
56
64
|
import { GENTLE_AI_VERSION, gentleAiBinaryPath, PackageLocalGentleAiBinaryMissingError } from "../runtime/gentle-ai-binary.mjs";
|
|
57
65
|
import { DEFAULT_THEME_NAME, installIsolatedTuiModeSetting } from "../scripts/install-tui-mode-setting.mjs";
|
|
58
66
|
|
|
@@ -72,17 +80,30 @@ function readJsonIfExists(path) {
|
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
83
|
+
// Resolve the public ESM entry without importing the agent or reaching through
|
|
84
|
+
// its exports map. Only an absent optional peer permits PATH fallback; malformed
|
|
85
|
+
// installed metadata must not silently select a different runtime.
|
|
77
86
|
function resolveBundledCli() {
|
|
87
|
+
let publicEntry;
|
|
78
88
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
} catch {
|
|
84
|
-
return undefined;
|
|
89
|
+
publicEntry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error.code === "ERR_MODULE_NOT_FOUND") return undefined;
|
|
92
|
+
throw error;
|
|
85
93
|
}
|
|
94
|
+
const entry = realpathSync(publicEntry);
|
|
95
|
+
const root = dirname(dirname(entry));
|
|
96
|
+
const expectedEntry = join(root, "dist", "index.js");
|
|
97
|
+
const metadata = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
98
|
+
if (entry !== expectedEntry || metadata.name !== "@earendil-works/pi-coding-agent" ||
|
|
99
|
+
metadata.bin?.pi !== "dist/bundle/cli.js") {
|
|
100
|
+
throw new Error("Unsupported adjacent Pi package entry/name/bin metadata");
|
|
101
|
+
}
|
|
102
|
+
const cliPath = join(root, metadata.bin.pi);
|
|
103
|
+
if (realpathSync(cliPath) !== cliPath || !statSync(cliPath).isFile()) {
|
|
104
|
+
throw new Error("Unsupported adjacent Pi CLI path");
|
|
105
|
+
}
|
|
106
|
+
return cliPath;
|
|
86
107
|
}
|
|
87
108
|
|
|
88
109
|
function findOnPath(name) {
|
|
@@ -162,6 +183,45 @@ function isDirectory(path) {
|
|
|
162
183
|
}
|
|
163
184
|
}
|
|
164
185
|
|
|
186
|
+
// Reuse Herdr's managed bridge, never its transport. This process-boundary
|
|
187
|
+
// lookup is deliberately best-effort and does not modify either agent home.
|
|
188
|
+
function managedHerdrExtensionArgs(home, args) {
|
|
189
|
+
const env = process.env;
|
|
190
|
+
if (home.mode !== "isolated" || args.piSubcommand !== undefined || args.passthrough[0] === "mcp") return [];
|
|
191
|
+
if (env.HERDR_ENV !== "1" || !env.HERDR_SOCKET_PATH?.trim() || !env.HERDR_PANE_ID?.trim()) return [];
|
|
192
|
+
if (env.GENTLE_PI_AGENTS_CHILD === "1" || !process.stdin.isTTY || !process.stdout.isTTY) return [];
|
|
193
|
+
// Only automatic interactive loading: a user opt-out must not become an
|
|
194
|
+
// explicit -e (which Pi loads even under --no-extensions). Conservatively
|
|
195
|
+
// skip ambiguous mode flags too; --mode text alone still allows a TUI.
|
|
196
|
+
const forwarded = args.passthrough;
|
|
197
|
+
if (forwarded.some((arg) => ["--no-extensions", "-ne", "--print", "-p", "--export", "--list-models", "-v"].includes(arg))) return [];
|
|
198
|
+
if (forwarded.some((arg, i) => (arg === "--mode" && forwarded[i + 1] !== "text") || arg.startsWith("--mode="))) return [];
|
|
199
|
+
try {
|
|
200
|
+
if (!statSync(env.HERDR_SOCKET_PATH).isSocket()) return [];
|
|
201
|
+
} catch {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
// Prefer a bridge in the selected home over adding a competing copy; keep
|
|
205
|
+
// the incoming Pi home override before falling back to Herdr's usual home.
|
|
206
|
+
const agentHomes = [home.dir, env.PI_CODING_AGENT_DIR, join(homedir(), ".pi", "agent")];
|
|
207
|
+
for (const agentHome of agentHomes) {
|
|
208
|
+
if (!agentHome) continue;
|
|
209
|
+
const bridge = join(agentHome, "extensions", "herdr-agent-state.ts");
|
|
210
|
+
try {
|
|
211
|
+
if (!statSync(bridge).isFile()) continue;
|
|
212
|
+
accessSync(bridge, fsConstants.R_OK);
|
|
213
|
+
// Pi's package-manager.toResolvedPaths and resource-loader.mergePaths
|
|
214
|
+
// dedupe canonical files across discovery, explicit -e and manifests.
|
|
215
|
+
// Existence alone is NOT proof of loading: declare the resource and
|
|
216
|
+
// let that resolver dedupe it, including explicit aliases from argv.
|
|
217
|
+
return ["-e", realpathSync(bridge)];
|
|
218
|
+
} catch {
|
|
219
|
+
// An absent/unreadable bridge never prevents the ordinary launch.
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
|
|
165
225
|
// Real-fs adapter for discoverLooseExtensionEntries (lib/gentle-shell-launcher.ts):
|
|
166
226
|
// statSync-based isFile/isDirectory (not readdirSync's Dirent, which uses
|
|
167
227
|
// lstat and so would treat a symlinked file or directory as neither) so a
|
|
@@ -254,9 +314,46 @@ function readRawConfig(configPath) {
|
|
|
254
314
|
function writeRawConfig(configPath, config) {
|
|
255
315
|
const configDir = dirname(configPath);
|
|
256
316
|
if (!existsSync(configDir)) mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
317
|
+
writeFileAtomically(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// The one atomic write every launcher-owned config/settings update goes
|
|
321
|
+
// through: a temp file next to the real target, then a rename onto it, so a
|
|
322
|
+
// crash or kill never leaves a partial file. When `path` is a symlink (Nix
|
|
323
|
+
// home-manager, stow, and similar dotfile managers ship settings.json and
|
|
324
|
+
// config.json that way), the write lands on the link's real target and the
|
|
325
|
+
// link itself stays in place; renaming onto `path` would replace the link
|
|
326
|
+
// with a regular file. A missing path or a dangling link has no real target
|
|
327
|
+
// to preserve, so it is written at `path` itself, exactly as before this
|
|
328
|
+
// helper existed, instead of creating a file wherever a dangling link points.
|
|
329
|
+
// The permission bits are `mode` when given, otherwise the real target's own
|
|
330
|
+
// (applied with chmod, so the umask cannot narrow them); a new file keeps
|
|
331
|
+
// the default creation mode. The temp file is removed when the write or
|
|
332
|
+
// rename throws, and the error propagates to the caller's own handling.
|
|
333
|
+
function writeFileAtomically(path, data, { mode } = {}) {
|
|
334
|
+
let target = path;
|
|
335
|
+
try {
|
|
336
|
+
target = realpathSync(path);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (error.code !== "ENOENT") throw error;
|
|
339
|
+
}
|
|
340
|
+
let targetMode = mode;
|
|
341
|
+
if (targetMode === undefined) {
|
|
342
|
+
try {
|
|
343
|
+
targetMode = statSync(target).mode & 0o777;
|
|
344
|
+
} catch (error) {
|
|
345
|
+
if (error.code !== "ENOENT") throw error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const tempPath = join(dirname(target), `.${basenameOf(target)}.gentle-shell-${process.pid}.tmp`);
|
|
349
|
+
try {
|
|
350
|
+
writeFileSync(tempPath, data);
|
|
351
|
+
if (targetMode !== undefined) chmodSync(tempPath, targetMode);
|
|
352
|
+
renameSync(tempPath, target);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
rmSync(tempPath, { force: true });
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
260
357
|
}
|
|
261
358
|
|
|
262
359
|
function loadConfig() {
|
|
@@ -512,9 +609,7 @@ function restoreFile(snapshot) {
|
|
|
512
609
|
}
|
|
513
610
|
if (currentBytes !== undefined && currentBytes.equals(snapshot.bytes)) return false;
|
|
514
611
|
mkdirSync(dirname(path), { recursive: true });
|
|
515
|
-
|
|
516
|
-
writeFileSync(tempPath, snapshot.bytes, { mode: snapshot.mode });
|
|
517
|
-
renameSync(tempPath, path);
|
|
612
|
+
writeFileAtomically(path, snapshot.bytes, { mode: snapshot.mode });
|
|
518
613
|
return true;
|
|
519
614
|
}
|
|
520
615
|
|
|
@@ -574,10 +669,7 @@ function restoreManagedAssetDigestField(path, originalText) {
|
|
|
574
669
|
if (currentText === undefined) return false;
|
|
575
670
|
const restoredText = restoreJsonField(originalText, currentText, MANAGED_ASSET_DIGEST_FIELD);
|
|
576
671
|
if (restoredText === undefined) return false;
|
|
577
|
-
|
|
578
|
-
const tempPath = join(dirname(path), `.${basenameOf(path)}.gentle-shell-restore-${process.pid}.tmp`);
|
|
579
|
-
writeFileSync(tempPath, restoredText, { mode });
|
|
580
|
-
renameSync(tempPath, path);
|
|
672
|
+
writeFileAtomically(path, restoredText);
|
|
581
673
|
return true;
|
|
582
674
|
}
|
|
583
675
|
|
|
@@ -618,10 +710,7 @@ function enforceDefaultThemeField(settingsPath, originalSettingsText) {
|
|
|
618
710
|
? restoreJsonField(originalSettingsText, currentText, "theme")
|
|
619
711
|
: forceJsonFieldIfAbsentInOriginal(originalSettingsText, currentText, "theme", DEFAULT_THEME_NAME);
|
|
620
712
|
if (newText === undefined) return false;
|
|
621
|
-
|
|
622
|
-
const tempPath = join(dirname(settingsPath), `.${basenameOf(settingsPath)}.gentle-shell-restore-${process.pid}.tmp`);
|
|
623
|
-
writeFileSync(tempPath, newText, { mode });
|
|
624
|
-
renameSync(tempPath, settingsPath);
|
|
713
|
+
writeFileAtomically(settingsPath, newText);
|
|
625
714
|
return originalHadTheme ? "restored" : "forced";
|
|
626
715
|
}
|
|
627
716
|
|
|
@@ -641,6 +730,53 @@ function safely(label, path, fallback, fn) {
|
|
|
641
730
|
}
|
|
642
731
|
}
|
|
643
732
|
|
|
733
|
+
// gentle-pi replaces Pi's replaceable builtin codemode with its own decorated
|
|
734
|
+
// codemode tool (extensions/quiet-tools.ts -> registerCompactCodemode in
|
|
735
|
+
// lib/codemode-renderer.ts), and Pi prints a startup warning whenever a
|
|
736
|
+
// builtin loses its tool to another extension. Pi's only per-builtin opt-out
|
|
737
|
+
// is a `-builtin:<name>` entry in the settings `extensions` array, so every
|
|
738
|
+
// normal launch ensures that entry in the settings.json of a home
|
|
739
|
+
// gentle-shell owns (see main, below).
|
|
740
|
+
const BUILTIN_CODEMODE_EXTENSION = "builtin:codemode";
|
|
741
|
+
|
|
742
|
+
// Pure: returns `settingsText` with `-<builtin>` appended to its `extensions`
|
|
743
|
+
// array (created when absent), or undefined when nothing should change — the
|
|
744
|
+
// text does not parse as a JSON object, `extensions` exists but is not an
|
|
745
|
+
// array, or the array already holds any explicit entry for the builtin
|
|
746
|
+
// (`+`, `-`, `!`, or bare), which is the user's own decision. Keeps every
|
|
747
|
+
// other key and entry in place, plus the text's own indentation and trailing
|
|
748
|
+
// newline (same detection as detectJsonFormatting in
|
|
749
|
+
// lib/gentle-shell-launcher.ts).
|
|
750
|
+
function withBuiltinExtensionExcluded(settingsText, builtin) {
|
|
751
|
+
let settings;
|
|
752
|
+
try {
|
|
753
|
+
settings = JSON.parse(settingsText);
|
|
754
|
+
} catch {
|
|
755
|
+
return undefined;
|
|
756
|
+
}
|
|
757
|
+
if (typeof settings !== "object" || settings === null || Array.isArray(settings)) return undefined;
|
|
758
|
+
const extensions = Object.prototype.hasOwnProperty.call(settings, "extensions") ? settings.extensions : [];
|
|
759
|
+
if (!Array.isArray(extensions)) return undefined;
|
|
760
|
+
if (extensions.some((entry) => typeof entry === "string" && entry.replace(/^[+!-]/, "") === builtin)) return undefined;
|
|
761
|
+
const indent = settingsText.match(/\{\r?\n([ \t]+)/)?.[1];
|
|
762
|
+
const serialized = JSON.stringify({ ...settings, extensions: [...extensions, `-${builtin}`] }, null, indent);
|
|
763
|
+
return settingsText.endsWith("\n") ? `${serialized}\n` : serialized;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Applies withBuiltinExtensionExcluded to an existing settings.json with the
|
|
767
|
+
// same atomic temp-file-then-rename write as the restores above. A missing
|
|
768
|
+
// settings.json is left missing: only the brand-new-home bootstrap
|
|
769
|
+
// (installIsolatedTuiModeSetting) seeds that file, and this launch-time step
|
|
770
|
+
// never takes over that role. Returns true when it actually wrote the file.
|
|
771
|
+
function ensureBuiltinCodemodeExcluded(settingsPath) {
|
|
772
|
+
const currentText = readJsonIfExists(settingsPath);
|
|
773
|
+
if (currentText === undefined) return false;
|
|
774
|
+
const newText = withBuiltinExtensionExcluded(currentText, BUILTIN_CODEMODE_EXTENSION);
|
|
775
|
+
if (newText === undefined) return false;
|
|
776
|
+
writeFileAtomically(settingsPath, newText);
|
|
777
|
+
return true;
|
|
778
|
+
}
|
|
779
|
+
|
|
644
780
|
// Provisions `home` with everything `gentle-ai install --agent pi` installs
|
|
645
781
|
// into a regular Pi, by spawning the package-local pinned gentle-ai binary
|
|
646
782
|
// (never a PATH `gentle-ai`) with PI_CODING_AGENT_DIR/GENTLE_PI_AGENT_HOME set
|
|
@@ -1139,6 +1275,21 @@ async function main() {
|
|
|
1139
1275
|
// exits with the same signal-derived code instead of falling through
|
|
1140
1276
|
// to launch pi.
|
|
1141
1277
|
if (autoProvisionResult !== undefined) process.exit(autoProvisionResult.exitCode);
|
|
1278
|
+
|
|
1279
|
+
// Runs after auto-provision, so it sees settings.json exactly as that
|
|
1280
|
+
// run left it. Only a home gentle-shell owns, by the same rule
|
|
1281
|
+
// auto-provisioning uses (homeIsForeign): never --link (excluded above),
|
|
1282
|
+
// a foreign --home, or pi's own default agent home, since plain pi may
|
|
1283
|
+
// share those and would lose its builtin codemode. `setup` (including
|
|
1284
|
+
// --dry-run) returned before this point.
|
|
1285
|
+
const settingsPath = join(home.dir, "settings.json");
|
|
1286
|
+
const excluded = safely("exclude Pi's builtin codemode in your Gentle Shell settings", settingsPath, false, () => {
|
|
1287
|
+
const previous = provisionedEntry(readRawConfig(resolveConfigPath()), safeRealpath(home.dir));
|
|
1288
|
+
return !homeIsForeign(home, previous, homeHadContentBeforeBootstrap) && ensureBuiltinCodemodeExcluded(settingsPath);
|
|
1289
|
+
});
|
|
1290
|
+
if (excluded) {
|
|
1291
|
+
process.stderr.write(`gentle-shell: disabled Pi's builtin codemode in ${settingsPath} (Gentle Shell ships its own codemode tool)\n`);
|
|
1292
|
+
}
|
|
1142
1293
|
}
|
|
1143
1294
|
|
|
1144
1295
|
const packageRootExplicit = args.packageRoot !== undefined;
|
|
@@ -1228,22 +1379,96 @@ async function main() {
|
|
|
1228
1379
|
takeOver,
|
|
1229
1380
|
otherPackagePaths,
|
|
1230
1381
|
looseExtensionEntries,
|
|
1231
|
-
passthrough: args.passthrough,
|
|
1382
|
+
passthrough: [...managedHerdrExtensionArgs(home, args), ...args.passthrough],
|
|
1232
1383
|
piSubcommand: args.piSubcommand,
|
|
1233
1384
|
baseEnv: process.env,
|
|
1385
|
+
homedir: homedir(),
|
|
1234
1386
|
});
|
|
1235
1387
|
|
|
1388
|
+
// Only an interactive session ends with pi's exit resume hint, which
|
|
1389
|
+
// gentle-shell completes with its own line; a pi subcommand gets no handoff.
|
|
1390
|
+
const resumeHandoff = args.piSubcommand === undefined ? createResumeHandoff() : undefined;
|
|
1391
|
+
const childEnv = resumeHandoff ? { ...invocation.env, [RESUME_HANDOFF_ENV]: resumeHandoff.path } : invocation.env;
|
|
1392
|
+
|
|
1236
1393
|
const launchPlan = planSpawn({ command: invocation.command, args: invocation.args, platform: process.platform });
|
|
1237
|
-
|
|
1394
|
+
let child;
|
|
1395
|
+
try {
|
|
1396
|
+
child = spawn(launchPlan.command, launchPlan.args, { stdio: "inherit", env: childEnv, shell: launchPlan.shell });
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
resumeHandoff?.dispose();
|
|
1399
|
+
throw error;
|
|
1400
|
+
}
|
|
1401
|
+
// Only SIGHUP means the terminal is gone. pi may survive a forwarded
|
|
1402
|
+
// SIGINT and keep running, so other signals must not silence the hint.
|
|
1403
|
+
let terminalHungUp = false;
|
|
1238
1404
|
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
1239
|
-
process.on(signal, () =>
|
|
1405
|
+
process.on(signal, () => {
|
|
1406
|
+
if (signal === "SIGHUP") terminalHungUp = true;
|
|
1407
|
+
child.kill(signal);
|
|
1408
|
+
});
|
|
1240
1409
|
}
|
|
1241
|
-
child.on("error", (error) =>
|
|
1410
|
+
child.on("error", (error) => {
|
|
1411
|
+
resumeHandoff?.dispose();
|
|
1412
|
+
fail(`Could not start pi: ${error.message}`, 1);
|
|
1413
|
+
});
|
|
1242
1414
|
child.on("exit", (code, signal) => {
|
|
1243
|
-
|
|
1415
|
+
const exitCode = signal ? signalExitCode(signal) : (code ?? 1);
|
|
1416
|
+
if (resumeHandoff) {
|
|
1417
|
+
const hint = planResumeHint({
|
|
1418
|
+
handoff: resumeHandoff.read(),
|
|
1419
|
+
homeFlags: homeSelectorFlags(home),
|
|
1420
|
+
stdoutIsTTY: process.stdout.isTTY === true,
|
|
1421
|
+
terminalHungUp,
|
|
1422
|
+
platform: process.platform,
|
|
1423
|
+
color: process.stdout.hasColors?.() === true,
|
|
1424
|
+
});
|
|
1425
|
+
resumeHandoff.dispose();
|
|
1426
|
+
// TTY writes are asynchronous on Windows: exit only once the
|
|
1427
|
+
// hint is flushed, or it can be lost.
|
|
1428
|
+
if (hint) {
|
|
1429
|
+
// A write error (e.g. EIO on a closed terminal) must not turn
|
|
1430
|
+
// pi's exit into a launcher crash.
|
|
1431
|
+
process.stdout.once("error", () => process.exit(exitCode));
|
|
1432
|
+
process.stdout.write(hint, () => process.exit(exitCode));
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
process.exit(exitCode);
|
|
1244
1437
|
});
|
|
1245
1438
|
}
|
|
1246
1439
|
|
|
1440
|
+
// Private temp dir for the resume-hint handoff (lib/gentle-shell-resume-hint.ts).
|
|
1441
|
+
// Best effort: if it cannot be created, only pi's own hint is printed.
|
|
1442
|
+
function createResumeHandoff() {
|
|
1443
|
+
let dir;
|
|
1444
|
+
try {
|
|
1445
|
+
dir = mkdtempSync(join(tmpdir(), RESUME_HANDOFF_DIR_PREFIX));
|
|
1446
|
+
} catch {
|
|
1447
|
+
return undefined;
|
|
1448
|
+
}
|
|
1449
|
+
const path = join(dir, RESUME_HANDOFF_FILE);
|
|
1450
|
+
return {
|
|
1451
|
+
path,
|
|
1452
|
+
read: () => {
|
|
1453
|
+
try {
|
|
1454
|
+
const text = readJsonIfExists(path);
|
|
1455
|
+
return text === undefined ? undefined : parseResumeHandoff(text);
|
|
1456
|
+
} catch {
|
|
1457
|
+
return undefined;
|
|
1458
|
+
}
|
|
1459
|
+
},
|
|
1460
|
+
// Never throws: it runs inside the exit handler, where an EPERM on
|
|
1461
|
+
// Windows would otherwise replace pi's exit code with a crash.
|
|
1462
|
+
dispose: () => {
|
|
1463
|
+
try {
|
|
1464
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1465
|
+
} catch {
|
|
1466
|
+
// A leftover empty temp dir is harmless.
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1247
1472
|
main().catch((error) => {
|
|
1248
1473
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
1249
1474
|
process.exit(1);
|
|
@@ -6,7 +6,30 @@ How the Gentle Pi orchestrator decides who verifies a bounded writer's work. The
|
|
|
6
6
|
|
|
7
7
|
The bounded writer runs the exact commands the parent lists under `## Verification`, in the foreground, and reports each as `<command>: <observed result>`. That report is the verification of record and the native review is the independent check. `gentle-ai-verify` is on-demand: a `partial` or `blocked` writer, an expensive or external check the parent wants on a cheaper profile, or a parent spot check.
|
|
8
8
|
|
|
9
|
-
This `on` path holds only while the native review actually reaches a terminal outcome for the current candidate (gentle-pi#668). A human decline of the consent envelope for this candidate (candidate-scoped, never the RDD kill switch), a clone-local RDD disable discovered mid-flow, or a refused START/STATUS all mean the review never ran, so the parent falls back to the exact risk-gated path below, as if RDD were `off` -- declining a review never lowers the bar below the RDD-off path.
|
|
9
|
+
This `on` path holds only while the native review actually reaches a terminal outcome for the current candidate (gentle-pi#668). A human decline of the consent envelope for this candidate (candidate-scoped, never the RDD kill switch), a clone-local RDD disable discovered mid-flow, or a refused START/STATUS all mean the review never ran, so the parent falls back to the exact risk-gated path below, as if RDD were `off` -- declining a review never lowers the bar below the RDD-off path.
|
|
10
|
+
|
|
11
|
+
### How `assess` resolves the review outcome (gentle-pi#1175)
|
|
12
|
+
|
|
13
|
+
`gentle_review`'s `assess` operation derives `closed` only from the native `candidate.consumed` fact for this exact candidate. Native writes that fact only when the approved review for that candidate is acknowledged. A caller-supplied `closed` is not authority: without that fact it resolves to `unknown`. A declined, unavailable, or unknown outcome falls back to the RDD-off tier table, and `unknown` is never treated as closed.
|
|
14
|
+
|
|
15
|
+
Rows apply in order; the first match wins.
|
|
16
|
+
|
|
17
|
+
| Outcome evidence | Resolved `nativeReviewOutcome` | `outcome_source` |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| Caller passes `declined`, `unavailable`, or `unknown` | that value | `explicit` |
|
|
20
|
+
| This process recorded `declined`/`unavailable` for this exact candidate | that value (wins over closure) | `derived` |
|
|
21
|
+
| Native reports `candidate.consumed: true` for this candidate | `closed` | `derived` |
|
|
22
|
+
| Anything else, including a bare caller `closed` | `unknown` | `unknown` |
|
|
23
|
+
|
|
24
|
+
A recorded outcome is keyed by the candidate's own target identity, never by repository alone, so one candidate's outcome never leaks into a different candidate's `assess` call in the same clone.
|
|
25
|
+
|
|
26
|
+
### How `assess` resolves the writer profile (gentle-pi#1175)
|
|
27
|
+
|
|
28
|
+
The writer profile comes from the runtime-recorded model and effort of the pending mutations for the root (`writerProfileSource: runtime`). Caller `writerModelId`/`writerEffort` are only a fallback when no runtime evidence exists (`caller`); with neither, the profile is small (`fallback`). A missing model, a `mini` model token (`gemini` is not mini), or `low` effort keeps the conservative small-model bias.
|
|
29
|
+
|
|
30
|
+
### What `assess` projects
|
|
31
|
+
|
|
32
|
+
When native reports them, `assess` projects `reviewDue`, `reviewDueReason`, `candidate.consumed`, and the native preflight continuation (`nextTransition`) verbatim. Older binaries omit them, and nothing is invented. The continuation is opaque: relay it unchanged, never rebuild it.
|
|
10
33
|
|
|
11
34
|
## Receipt-driven development off or unknown (gentle-pi#662)
|
|
12
35
|
|
|
@@ -23,3 +46,5 @@ The `gentle_review` tool's `assess` operation (`extensions/gentle-ai.ts`) combin
|
|
|
23
46
|
|
|
24
47
|
When RDD is `on` and the native review closed for this candidate, the writer's own self-verification is the record and the closed native review is the independent check, except a passive-risk change, which still gets a structural readback instead; any other `nativeReviewOutcome` under `on` follows this same tier table instead (gentle-pi#668). The small-model bias raises the medium tier to high for verification purposes only; an unknown RDD line never lowers a tier below `off`. The parent's own spot check (re-running one reported command before delivery) stays required in every tier.
|
|
25
48
|
|
|
49
|
+
A native code review is not a substitute for applicable functional checks: tests, builds, and functional verification such as browser checks for UI changes still run when applicable. Review outcomes never authorize delivery; human consent for native review and the user-owned RDD switch are unchanged.
|
|
50
|
+
|
|
@@ -90,6 +90,30 @@ When the whole-payload bound is still exceeded after the field- and item-level t
|
|
|
90
90
|
|
|
91
91
|
An active task's `summary` (running, waiting or queued) is never dropped; only its `thread.items` shrink. Finished tasks can be dropped whole by step 3, oldest first.
|
|
92
92
|
|
|
93
|
+
## Generation and watchdog progress
|
|
94
|
+
|
|
95
|
+
While an active assistant message streams a tool-call block, validated fresh,
|
|
96
|
+
nonempty argument deltas renew the runner's idle watchdog independently of
|
|
97
|
+
thread/display events. `summary.lastStep` becomes `generating tool arguments`
|
|
98
|
+
and `lastActivityAt` advances; no partial argument data is stored in the thread,
|
|
99
|
+
diagnostic, or progress tracker. `toolCalls` increments only at execution start.
|
|
100
|
+
Token/cost totals still update only from finalized assistant `message_end` usage,
|
|
101
|
+
not streaming usage; static totals do not establish inactivity.
|
|
102
|
+
|
|
103
|
+
The tracker admits blocks announced by current RPC identity fields or older Pi
|
|
104
|
+
partial snapshots. It rejects empty/malformed deltas, unannounced or closed
|
|
105
|
+
blocks, stale message starts, and duplicate argument fingerprints. RPC provides
|
|
106
|
+
no delta sequence number, so identical chunks within one block are conservatively
|
|
107
|
+
indistinguishable from replay and do not renew liveness. Only hashes are retained,
|
|
108
|
+
with a 4096-fingerprint bound per assistant message; exhaustion fails closed until
|
|
109
|
+
a newer message starts. Unrelated UI and unrecognized event traffic do not renew
|
|
110
|
+
argument liveness. Existing RPC command-response handling is unchanged.
|
|
111
|
+
|
|
112
|
+
Idle and in-flight execution watchdog budgets are **renewable silence bounds**,
|
|
113
|
+
not absolute run/generation duration limits. Argument generation uses the idle
|
|
114
|
+
budget, not the longer announced-execution budget. Later silence still times out;
|
|
115
|
+
execution start/end and cancellation retain their existing behavior.
|
|
116
|
+
|
|
93
117
|
## Coalescing
|
|
94
118
|
|
|
95
119
|
`createRpcActivityPublisher` subscribes to `TaskStore#subscribeSummary` (task added, removed, or changed status) and to `TaskStore#subscribe(id)` for every known task, including ones added after `start()`. Changes inside a 150 ms window collapse into exactly one `setWidget("gentle-agents", [line])` call; `stop()` tears down every subscription and publishes one final frame.
|