@gotgenes/pi-subagents 18.0.1 → 18.0.2
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/CHANGELOG.md +7 -0
- package/docs/architecture/architecture.md +157 -204
- package/docs/architecture/history/phase-19-implement-ui-decisions.md +282 -0
- package/package.json +5 -6
- package/src/lifecycle/subagent.ts +5 -9
- package/src/observation/notification.ts +23 -10
- package/src/observation/subagent-events-observer.ts +1 -3
- package/src/tools/get-result-report.ts +60 -0
- package/src/tools/get-result-tool.ts +25 -40
- package/src/types.ts +1 -1
- package/src/ui/session-navigator.ts +0 -2
- package/.prettierignore +0 -5
- package/AGENTS.md +0 -7
- package/docs/plans/0048-implement-subagents-api.md +0 -304
- package/docs/plans/0049-remove-group-join-output-file-rpc.md +0 -180
- package/docs/plans/0051-update-adr-0001-hard-fork.md +0 -77
- package/docs/plans/0052-remove-scheduled-subagents.md +0 -133
- package/docs/plans/0053-extract-model-resolution-from-execute.md +0 -181
- package/docs/plans/0054-decompose-index-into-modules.md +0 -302
- package/docs/plans/0057-structured-debug-logging.md +0 -154
- package/docs/plans/0061-session-format-transcript.md +0 -284
- package/docs/plans/0066-replace-as-any-with-sdk-types.md +0 -215
- package/docs/plans/0069-create-subagent-runtime.md +0 -345
- package/docs/plans/0070-extract-event-handlers.md +0 -306
- package/docs/plans/0071-extract-session-config-assembler.md +0 -362
- package/docs/plans/0072-inject-agent-manager-collaborators.md +0 -329
- package/docs/plans/0076-inject-cwd-into-agent-manager.md +0 -102
- package/docs/plans/0077-inject-project-agents-dir.md +0 -115
- package/docs/plans/0080-consolidate-agent-config-lookup.md +0 -247
- package/docs/plans/0084-extract-git-worktree-manager.md +0 -142
- package/docs/plans/0087-evolve-subagent-runtime-methods.md +0 -240
- package/docs/plans/0098-extract-agent-record-state-machine.md +0 -435
- package/docs/plans/0099-replace-ctx-with-parent-snapshot.md +0 -488
- package/docs/plans/0100-replace-callback-threading-with-session-subscription.md +0 -454
- package/docs/plans/0102-consolidate-test-record-factory.md +0 -176
- package/docs/plans/0108-extract-agent-type-registry.md +0 -322
- package/docs/plans/0109-extract-settings-manager.md +0 -276
- package/docs/plans/0110-agent-activity-tracker.md +0 -297
- package/docs/plans/0111-split-agent-record-lifecycle.md +0 -582
- package/docs/plans/0112-replace-agent-manager-callbacks.md +0 -241
- package/docs/plans/0113-disambiguate-spawn-options.md +0 -155
- package/docs/plans/0114-narrow-agent-tool-menu-deps.md +0 -279
- package/docs/plans/0115-decompose-agent-tool.md +0 -337
- package/docs/plans/0116-type-housekeeping.md +0 -351
- package/docs/plans/0118-settings-manager-apply-methods.md +0 -271
- package/docs/plans/0123-remove-vi-fn-cast-smell.md +0 -179
- package/docs/plans/0131-consolidate-shared-test-fixtures.md +0 -207
- package/docs/plans/0132-inject-io-into-session-config.md +0 -219
- package/docs/plans/0133-inject-sdk-boundary-into-agent-runner.md +0 -373
- package/docs/plans/0134-reduce-as-any-casts.md +0 -366
- package/docs/plans/0135-extract-display-helpers.md +0 -182
- package/docs/plans/0136-decompose-agent-menu.md +0 -300
- package/docs/plans/0144-consolidate-observation-model.md +0 -263
- package/docs/plans/0145-decompose-execute-push-ctx-to-boundary.md +0 -290
- package/docs/plans/0146-narrow-ui-context.md +0 -319
- package/docs/plans/0147-inject-wrap-text-into-conversation-viewer.md +0 -166
- package/docs/plans/0148-split-agent-widget-rendering.md +0 -255
- package/docs/plans/0152-add-prompt-snippet.md +0 -91
- package/docs/plans/0164-reorganize-into-domain-directories.md +0 -409
- package/docs/plans/0165-decompose-resolved-spawn-config.md +0 -157
- package/docs/plans/0166-extract-parent-session-info.md +0 -231
- package/docs/plans/0167-narrow-runner-io.md +0 -150
- package/docs/plans/0168-extract-tool-filter-config.md +0 -173
- package/docs/plans/0169-extract-run-context-from-run-options.md +0 -194
- package/docs/plans/0170-reduce-build-content-lines-complexity.md +0 -225
- package/docs/plans/0171-reduce-render-result-complexity.md +0 -200
- package/docs/plans/0172-extract-turn-formatting.md +0 -206
- package/docs/plans/0180-reorder-append-prompt-for-kv-cache.md +0 -100
- package/docs/plans/0185-remove-persistent-agent-memory.md +0 -161
- package/docs/plans/0188-replace-any-casts-with-sdk-types.md +0 -162
- package/docs/plans/0192-define-session-context-interface.md +0 -107
- package/docs/plans/0193-runtime-owns-context-queries.md +0 -245
- package/docs/plans/0194-align-tool-interfaces-for-structural-typing.md +0 -139
- package/docs/plans/0195-convert-tool-factories-to-classes.md +0 -264
- package/docs/plans/0196-convert-runner-menu-to-classes.md +0 -268
- package/docs/plans/0205-decompose-render-widget-lines.md +0 -140
- package/docs/plans/0206-decompose-show-agent-detail.md +0 -211
- package/docs/plans/0207-decompose-agent-widget-update.md +0 -242
- package/docs/plans/0208-extract-shared-test-fixtures.md +0 -298
- package/docs/plans/0214-convert-remaining-closure-factories-to-classes.md +0 -261
- package/docs/plans/0215-decompose-build-parent-context.md +0 -166
- package/docs/plans/0216-decompose-start-agent.md +0 -255
- package/docs/plans/0217-extract-overwrite-guard.md +0 -176
- package/docs/plans/0218-push-sdk-boundary-in-settings.md +0 -172
- package/docs/plans/0219-reduce-test-duplication-top-3-clone-families.md +0 -162
- package/docs/plans/0227-evolve-agent-record-into-agent.md +0 -322
- package/docs/plans/0228-async-start-agent-dissolve-run-handle.md +0 -288
- package/docs/plans/0229-agent-born-complete.md +0 -564
- package/docs/plans/0230-extract-concurrency-queue.md +0 -265
- package/docs/plans/0231-push-exec-registry-to-runner.md +0 -245
- package/docs/plans/0232-agent-resume-internal-observer-lifecycle.md +0 -180
- package/docs/plans/0237-remove-disallowed-tools.md +0 -180
- package/docs/plans/0238-remove-extensions-filtering.md +0 -191
- package/docs/plans/0239-collapse-filter-active-tools.md +0 -217
- package/docs/plans/0242-rename-agent-tool-to-subagent.md +0 -165
- package/docs/plans/0256-extract-worktree-isolation.md +0 -256
- package/docs/plans/0257-extract-child-session-factory.md +0 -285
- package/docs/plans/0262-add-workspace-provider-seam.md +0 -264
- package/docs/plans/0264-remove-extension-lifecycle-control.md +0 -277
- package/docs/plans/0265-born-complete-subagent-session.md +0 -333
- package/docs/plans/0270-type-consumable-public-surface.md +0 -204
- package/docs/plans/0272-export-workspace-collaborator-types.md +0 -147
- package/docs/plans/0277-encapsulate-agent-session.md +0 -304
- package/docs/plans/0280-rename-agent-to-subagent.md +0 -197
- package/docs/plans/0360-custom-agents-default-append.md +0 -132
- package/docs/plans/0373-extract-subagent-state.md +0 -250
- package/docs/plans/0374-encapsulate-subagent-start-notification.md +0 -268
- package/docs/plans/0375-extract-run-listener-workspace-bracket.md +0 -300
- package/docs/plans/0376-extract-manager-observer.md +0 -232
- package/docs/plans/0377-split-widget-delegation-from-runtime.md +0 -254
- package/docs/plans/0378-consolidate-lifecycle-test-fixtures.md +0 -243
- package/docs/plans/0379-consolidate-ui-tools-test-fixtures.md +0 -335
- package/docs/plans/0380-extract-layered-settings-loader.md +0 -223
- package/docs/plans/0381-replace-concurrency-queue-with-limiter.md +0 -267
- package/docs/plans/0400-include-parent-prompt-in-replace-mode.md +0 -199
- package/docs/plans/0403-abort-subagents-on-interrupt.md +0 -180
- package/docs/plans/0412-unify-session-mock-builders.md +0 -146
- package/docs/plans/0420-fold-run-metrics-live-activity-onto-record.md +0 -228
- package/docs/plans/0421-migrate-activity-readers-to-record-getters.md +0 -309
- package/docs/plans/0422-delete-activity-tracker-ui-observer.md +0 -204
- package/docs/plans/0423-widget-self-drive-from-lifecycle.md +0 -264
- package/docs/plans/0424-drop-widget-dep-from-subagent-tool.md +0 -166
- package/docs/plans/0425-reconcile-subagent-events-contract.md +0 -144
- package/docs/plans/0426-consolidate-residual-test-clone-families.md +0 -235
- package/docs/plans/0427-reconsider-ui-direction.md +0 -212
- package/docs/plans/0441-remove-agent-definition-management-subtree.md +0 -180
- package/docs/plans/0442-dissolve-agents-conversation-viewer.md +0 -195
- package/docs/plans/0443-consolidate-remaining-test-clone-families.md +0 -268
- package/docs/plans/0444-shrink-widget-to-background-agents.md +0 -156
- package/docs/plans/0445-native-session-navigation.md +0 -365
- package/docs/plans/0446-spike-session-navigation-entry-criteria.md +0 -171
- package/docs/plans/0447-extract-subagents-settings-command.md +0 -220
- package/docs/plans/0448-block-disabled-agent-spawn.md +0 -207
- package/docs/plans/0462-navigation-renderer-tui-components.md +0 -338
- package/docs/plans/0463-file-snapshot-source-evicted-agents.md +0 -306
- package/docs/plans/0470-refresh-readme-command-surface.md +0 -159
- package/docs/retro/0048-implement-subagents-api.md +0 -44
- package/docs/retro/0049-remove-group-join-output-file-rpc.md +0 -49
- package/docs/retro/0051-update-adr-0001-hard-fork.md +0 -39
- package/docs/retro/0053-extract-model-resolution-from-execute.md +0 -40
- package/docs/retro/0054-decompose-index-into-modules.md +0 -53
- package/docs/retro/0057-structured-debug-logging.md +0 -77
- package/docs/retro/0061-session-format-transcript.md +0 -41
- package/docs/retro/0066-replace-as-any-with-sdk-types.md +0 -44
- package/docs/retro/0069-create-subagent-runtime.md +0 -43
- package/docs/retro/0070-extract-event-handlers.md +0 -49
- package/docs/retro/0071-extract-session-config-assembler.md +0 -60
- package/docs/retro/0072-inject-agent-manager-collaborators.md +0 -46
- package/docs/retro/0076-inject-cwd-into-agent-manager.md +0 -35
- package/docs/retro/0077-inject-project-agents-dir.md +0 -33
- package/docs/retro/0080-consolidate-agent-config-lookup.md +0 -35
- package/docs/retro/0084-extract-git-worktree-manager.md +0 -37
- package/docs/retro/0087-evolve-subagent-runtime-methods.md +0 -42
- package/docs/retro/0098-extract-agent-record-state-machine.md +0 -46
- package/docs/retro/0099-replace-ctx-with-parent-snapshot.md +0 -37
- package/docs/retro/0100-replace-callback-threading-with-session-subscription.md +0 -36
- package/docs/retro/0102-consolidate-test-record-factory.md +0 -30
- package/docs/retro/0108-extract-agent-type-registry.md +0 -41
- package/docs/retro/0109-extract-settings-manager.md +0 -55
- package/docs/retro/0110-agent-activity-tracker.md +0 -44
- package/docs/retro/0111-split-agent-record-lifecycle.md +0 -61
- package/docs/retro/0112-replace-agent-manager-callbacks.md +0 -35
- package/docs/retro/0113-disambiguate-spawn-options.md +0 -29
- package/docs/retro/0114-narrow-agent-tool-menu-deps.md +0 -38
- package/docs/retro/0115-decompose-agent-tool.md +0 -51
- package/docs/retro/0116-type-housekeeping.md +0 -42
- package/docs/retro/0118-settings-manager-apply-methods.md +0 -40
- package/docs/retro/0123-remove-vi-fn-cast-smell.md +0 -49
- package/docs/retro/0131-consolidate-shared-test-fixtures.md +0 -46
- package/docs/retro/0132-inject-io-into-session-config.md +0 -33
- package/docs/retro/0133-inject-sdk-boundary-into-agent-runner.md +0 -45
- package/docs/retro/0134-reduce-as-any-casts.md +0 -56
- package/docs/retro/0135-extract-display-helpers.md +0 -38
- package/docs/retro/0136-decompose-agent-menu.md +0 -43
- package/docs/retro/0144-consolidate-observation-model.md +0 -39
- package/docs/retro/0145-decompose-execute-push-ctx-to-boundary.md +0 -56
- package/docs/retro/0146-narrow-ui-context.md +0 -70
- package/docs/retro/0147-inject-wrap-text-into-conversation-viewer.md +0 -90
- package/docs/retro/0148-split-agent-widget-rendering.md +0 -39
- package/docs/retro/0152-add-prompt-snippet.md +0 -34
- package/docs/retro/0164-reorganize-into-domain-directories.md +0 -82
- package/docs/retro/0165-decompose-resolved-spawn-config.md +0 -78
- package/docs/retro/0166-extract-parent-session-info.md +0 -66
- package/docs/retro/0167-narrow-runner-io.md +0 -63
- package/docs/retro/0168-extract-tool-filter-config.md +0 -76
- package/docs/retro/0169-extract-run-context-from-run-options.md +0 -68
- package/docs/retro/0170-reduce-build-content-lines-complexity.md +0 -72
- package/docs/retro/0171-reduce-render-result-complexity.md +0 -77
- package/docs/retro/0172-extract-turn-formatting.md +0 -80
- package/docs/retro/0180-reorder-append-prompt-for-kv-cache.md +0 -62
- package/docs/retro/0185-remove-persistent-agent-memory.md +0 -73
- package/docs/retro/0188-replace-any-casts-with-sdk-types.md +0 -69
- package/docs/retro/0192-define-session-context-interface.md +0 -59
- package/docs/retro/0193-runtime-owns-context-queries.md +0 -75
- package/docs/retro/0194-align-tool-interfaces-for-structural-typing.md +0 -62
- package/docs/retro/0195-convert-tool-factories-to-classes.md +0 -84
- package/docs/retro/0196-convert-runner-menu-to-classes.md +0 -73
- package/docs/retro/0205-decompose-render-widget-lines.md +0 -64
- package/docs/retro/0206-decompose-show-agent-detail.md +0 -67
- package/docs/retro/0207-decompose-agent-widget-update.md +0 -92
- package/docs/retro/0208-extract-shared-test-fixtures.md +0 -102
- package/docs/retro/0214-convert-remaining-closure-factories-to-classes.md +0 -66
- package/docs/retro/0215-decompose-build-parent-context.md +0 -61
- package/docs/retro/0216-decompose-start-agent.md +0 -80
- package/docs/retro/0217-extract-overwrite-guard.md +0 -63
- package/docs/retro/0218-push-sdk-boundary-in-settings.md +0 -72
- package/docs/retro/0219-reduce-test-duplication-top-3-clone-families.md +0 -64
- package/docs/retro/0227-evolve-agent-record-into-agent.md +0 -80
- package/docs/retro/0228-async-start-agent-dissolve-run-handle.md +0 -80
- package/docs/retro/0229-agent-born-complete.md +0 -89
- package/docs/retro/0230-extract-concurrency-queue.md +0 -38
- package/docs/retro/0231-push-exec-registry-to-runner.md +0 -71
- package/docs/retro/0232-agent-resume-internal-observer-lifecycle.md +0 -109
- package/docs/retro/0237-remove-disallowed-tools.md +0 -78
- package/docs/retro/0238-remove-extensions-filtering.md +0 -72
- package/docs/retro/0239-collapse-filter-active-tools.md +0 -70
- package/docs/retro/0242-rename-agent-tool-to-subagent.md +0 -82
- package/docs/retro/0256-extract-worktree-isolation.md +0 -89
- package/docs/retro/0257-extract-child-session-factory.md +0 -33
- package/docs/retro/0262-add-workspace-provider-seam.md +0 -89
- package/docs/retro/0264-remove-extension-lifecycle-control.md +0 -91
- package/docs/retro/0265-born-complete-subagent-session.md +0 -97
- package/docs/retro/0270-type-consumable-public-surface.md +0 -108
- package/docs/retro/0272-export-workspace-collaborator-types.md +0 -38
- package/docs/retro/0277-encapsulate-agent-session.md +0 -80
- package/docs/retro/0280-rename-agent-to-subagent.md +0 -96
- package/docs/retro/0360-custom-agents-default-append.md +0 -81
- package/docs/retro/0373-extract-subagent-state.md +0 -94
- package/docs/retro/0374-encapsulate-subagent-start-notification.md +0 -171
- package/docs/retro/0375-extract-run-listener-workspace-bracket.md +0 -104
- package/docs/retro/0376-extract-manager-observer.md +0 -81
- package/docs/retro/0377-split-widget-delegation-from-runtime.md +0 -105
- package/docs/retro/0378-consolidate-lifecycle-test-fixtures.md +0 -100
- package/docs/retro/0379-consolidate-ui-tools-test-fixtures.md +0 -52
- package/docs/retro/0380-extract-layered-settings-loader.md +0 -97
- package/docs/retro/0381-replace-concurrency-queue-with-limiter.md +0 -95
- package/docs/retro/0400-include-parent-prompt-in-replace-mode.md +0 -84
- package/docs/retro/0403-abort-subagents-on-interrupt.md +0 -90
- package/docs/retro/0412-unify-session-mock-builders.md +0 -80
- package/docs/retro/0420-fold-run-metrics-live-activity-onto-record.md +0 -90
- package/docs/retro/0421-migrate-activity-readers-to-record-getters.md +0 -105
- package/docs/retro/0422-delete-activity-tracker-ui-observer.md +0 -97
- package/docs/retro/0423-widget-self-drive-from-lifecycle.md +0 -112
- package/docs/retro/0424-drop-widget-dep-from-subagent-tool.md +0 -89
- package/docs/retro/0425-reconcile-subagent-events-contract.md +0 -101
- package/docs/retro/0426-consolidate-residual-test-clone-families.md +0 -100
- package/docs/retro/0427-reconsider-ui-direction.md +0 -102
- package/docs/retro/0441-remove-agent-definition-management-subtree.md +0 -97
- package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
- package/docs/retro/0443-consolidate-remaining-test-clone-families.md +0 -28
- package/docs/retro/0444-shrink-widget-to-background-agents.md +0 -81
- package/docs/retro/0445-native-session-navigation.md +0 -122
- package/docs/retro/0446-spike-session-navigation-entry-criteria.md +0 -99
- package/docs/retro/0447-extract-subagents-settings-command.md +0 -91
- package/docs/retro/0448-block-disabled-agent-spawn.md +0 -75
- package/docs/retro/0462-navigation-renderer-tui-components.md +0 -105
- package/docs/retro/0463-file-snapshot-source-evicted-agents.md +0 -103
- package/docs/retro/0470-refresh-readme-command-surface.md +0 -40
- package/src/observation/notification-state.ts +0 -27
- package/vitest.config.ts +0 -14
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 57
|
|
3
|
-
issue_title: "feat: structured debug logging for silenced catch blocks"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Structured debug logging for silenced catch blocks
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
The codebase contains ~20 `catch { /* ignore */ }` blocks spread across `agent-manager.ts`, `worktree.ts`, `output-file.ts`, `skill-loader.ts`, `custom-agents.ts`, `memory.ts`, `env.ts`, and `notification.ts`.
|
|
11
|
-
Each block correctly suppresses a non-essential side-effect failure, but in development the silence makes it impossible to diagnose what is actually failing inside those paths.
|
|
12
|
-
|
|
13
|
-
## Goals
|
|
14
|
-
|
|
15
|
-
- Add a `debugLog(context, err)` utility in `src/debug.ts` gated on
|
|
16
|
-
`PI_SUBAGENTS_DEBUG=1`.
|
|
17
|
-
- Thread `debugLog` into every silenced `catch` block listed in the issue scope.
|
|
18
|
-
- Leave production behavior **completely unchanged** — no new log output unless
|
|
19
|
-
the env var is explicitly set.
|
|
20
|
-
- Provide a `debug.test.ts` unit test covering the on/off branching.
|
|
21
|
-
|
|
22
|
-
## Non-Goals
|
|
23
|
-
|
|
24
|
-
- `usage.ts` catch blocks — they return `0`/`null` on failure and are already
|
|
25
|
-
recoverable, not truly "silent drops"; they are out of scope.
|
|
26
|
-
- `settings.ts` catch block — it returns `false` on failure, already surfaced
|
|
27
|
-
to the caller; out of scope.
|
|
28
|
-
- Log rotation, structured JSON output, or stderr vs. stdout routing.
|
|
29
|
-
- Wiring debug output to the Pi UI; `console.warn` to stderr is sufficient.
|
|
30
|
-
|
|
31
|
-
## Background
|
|
32
|
-
|
|
33
|
-
The `AGENTS.md` rule "prefer explicit configuration over hidden behavior" aligns with gating noise behind an opt-in env var rather than always-on logging.
|
|
34
|
-
The code-style skill notes that business logic should remain pure — `debug.ts` must not import the Pi SDK; it only reads `process.env`.
|
|
35
|
-
|
|
36
|
-
The `DEBUG` constant is evaluated once at module import time (top-level `const`).
|
|
37
|
-
That matches how similar opt-in env vars work in Node.js tooling and keeps the hot path to a single boolean branch.
|
|
38
|
-
|
|
39
|
-
### Catch blocks in scope
|
|
40
|
-
|
|
41
|
-
| File | Line(s) | Context label(s) |
|
|
42
|
-
| ------------------ | ------------------------------------ | ------------------------------------------------------- |
|
|
43
|
-
| `env.ts` | 15, 23 | `"git rev-parse"`, `"git branch"` |
|
|
44
|
-
| `skill-loader.ts` | 74 | `"readdirSync skill root"` |
|
|
45
|
-
| `custom-agents.ts` | 37, 47 | `"readdirSync agents dir"`, `"readFileSync agent file"` |
|
|
46
|
-
| `memory.ts` | 33, 47 | `"lstatSync"`, `"readFileSync"` |
|
|
47
|
-
| `output-file.ts` | 83 | `"write JSONL chunk"` |
|
|
48
|
-
| `worktree.ts` | 40, 56, 110, 130, 132, 147, 151, 161 | `"git rev-parse"`, `"git worktree add"`, etc. |
|
|
49
|
-
| `agent-manager.ts` | 233, 249, 266, 275, 480 | `"outputCleanup"`, `"onComplete callback"`, etc. |
|
|
50
|
-
| `notification.ts` | 145 | `"notification render"` |
|
|
51
|
-
|
|
52
|
-
## Design Overview
|
|
53
|
-
|
|
54
|
-
### `src/debug.ts`
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
export const DEBUG = process.env.PI_SUBAGENTS_DEBUG === "1";
|
|
58
|
-
|
|
59
|
-
export function debugLog(context: string, err: unknown): void {
|
|
60
|
-
if (DEBUG) console.warn(`[pi-subagents:debug] ${context}:`, err);
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The module-level `DEBUG` constant is the canonical source.
|
|
65
|
-
`debugLog` is a pure side-effect function: no return value, no SDK dependency, no coupling to Pi types.
|
|
66
|
-
|
|
67
|
-
### Threading pattern
|
|
68
|
-
|
|
69
|
-
Every silenced `catch` block is updated from:
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
} catch { /* ignore */ }
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
to:
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
} catch (err) { debugLog("<context label>", err); }
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
When the block already had a named error (`catch (err)`), only the `debugLog` call is added; the existing comment, if descriptive, is removed in favour of the context string.
|
|
82
|
-
|
|
83
|
-
### Context label convention
|
|
84
|
-
|
|
85
|
-
Labels read as `"<verb> <noun>"`, lower-case, mirroring the surrounding function name when unambiguous (e.g., `"removeWorktree"`, `"outputCleanup"`).
|
|
86
|
-
|
|
87
|
-
## Module-Level Changes
|
|
88
|
-
|
|
89
|
-
| File | Change |
|
|
90
|
-
| ---------------------- | ---------------------------------------------------------------------------- |
|
|
91
|
-
| `src/debug.ts` | **New.** Exports `DEBUG` constant and `debugLog` function. |
|
|
92
|
-
| `test/debug.test.ts` | **New.** Unit tests for `debugLog` on/off behaviour. |
|
|
93
|
-
| `src/env.ts` | Add `debugLog` import; name the two bare `catch` errors; call `debugLog`. |
|
|
94
|
-
| `src/skill-loader.ts` | Add `debugLog` import; name the bare `catch` error; call `debugLog`. |
|
|
95
|
-
| `src/custom-agents.ts` | Add `debugLog` import; name the two bare `catch` errors; call `debugLog`. |
|
|
96
|
-
| `src/memory.ts` | Add `debugLog` import; name the two bare `catch` errors; call `debugLog`. |
|
|
97
|
-
| `src/output-file.ts` | Add `debugLog` import; name the bare `catch` error; call `debugLog`. |
|
|
98
|
-
| `src/worktree.ts` | Add `debugLog` import; name the eight bare `catch` errors; call `debugLog`. |
|
|
99
|
-
| `src/agent-manager.ts` | Add `debugLog` import; name/update the five `catch` blocks; call `debugLog`. |
|
|
100
|
-
| `src/notification.ts` | Add `debugLog` import; name the bare `catch` error; call `debugLog`. |
|
|
101
|
-
|
|
102
|
-
## Test Impact Analysis
|
|
103
|
-
|
|
104
|
-
1. **New tests enabled** — `debug.test.ts` can directly unit-test `debugLog` in isolation.
|
|
105
|
-
Asserting `console.warn` is/isn't called based on the env var is now trivial.
|
|
106
|
-
Previously this path was entirely untested.
|
|
107
|
-
|
|
108
|
-
2. **Existing tests become redundant** — None.
|
|
109
|
-
The catch blocks were never exercised by existing tests (they were silent drops), so no existing test coverage needs to be removed.
|
|
110
|
-
|
|
111
|
-
3. **Tests that must stay as-is** — All existing tests remain valid.
|
|
112
|
-
The threading adds a call inside each catch but does not change control flow, return values, or thrown exceptions; no test expectations change.
|
|
113
|
-
|
|
114
|
-
## TDD Order
|
|
115
|
-
|
|
116
|
-
1. **`src/debug.ts` + `test/debug.test.ts`** Test surface: `debugLog` calls `console.warn` when `PI_SUBAGENTS_DEBUG=1`; does nothing when unset or `"0"`.
|
|
117
|
-
Use `vi.spyOn(console, 'warn')` + `vi.stubEnv('PI_SUBAGENTS_DEBUG', '1')` + `vi.resetModules()` + dynamic import to exercise both branches.
|
|
118
|
-
Suggested commit: `feat: add debugLog utility gated on PI_SUBAGENTS_DEBUG (#57)`
|
|
119
|
-
|
|
120
|
-
2. **Thread into `env.ts`** Test surface: `env.test.ts` — existing tests still pass (no new assertions required because the catch paths are tested implicitly by the non-git-dir test already covering the swallowed failures).
|
|
121
|
-
Suggested commit: `feat: thread debugLog into env catch blocks (#57)`
|
|
122
|
-
|
|
123
|
-
3. **Thread into `skill-loader.ts`** Test surface: `skill-loader.test.ts` — existing tests still pass.
|
|
124
|
-
The readdirSync-error catch is exercised when the skill root does not exist (covered by existing "not found" test paths).
|
|
125
|
-
Suggested commit: `feat: thread debugLog into skill-loader catch block (#57)`
|
|
126
|
-
|
|
127
|
-
4. **Thread into `custom-agents.ts` and `memory.ts`** These two files have structurally identical filesystem-error catch blocks and can land in a single step.
|
|
128
|
-
Test surface: `custom-agents.test.ts`, `memory.test.ts` — existing tests still pass.
|
|
129
|
-
Suggested commit: `feat: thread debugLog into custom-agents and memory catch blocks (#57)`
|
|
130
|
-
|
|
131
|
-
5. **Thread into `output-file.ts`** Test surface: `output-file.test.ts` — existing tests still pass.
|
|
132
|
-
Suggested commit: `feat: thread debugLog into output-file catch block (#57)`
|
|
133
|
-
|
|
134
|
-
6. **Thread into `worktree.ts`** Test surface: `worktree.test.ts` — existing tests still pass.
|
|
135
|
-
Eight catch sites; name each with its enclosing function for label clarity.
|
|
136
|
-
Suggested commit: `feat: thread debugLog into worktree catch blocks (#57)`
|
|
137
|
-
|
|
138
|
-
7. **Thread into `agent-manager.ts` and `notification.ts`** Test surface: `agent-manager.test.ts`, `notification.test.ts` — existing tests still pass.
|
|
139
|
-
Suggested commit: `feat: thread debugLog into agent-manager and notification catch blocks (#57)`
|
|
140
|
-
|
|
141
|
-
## Risks and Mitigations
|
|
142
|
-
|
|
143
|
-
| Risk | Mitigation |
|
|
144
|
-
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
-
| `DEBUG` is a module-level constant — `vi.stubEnv` alone won't flip it mid-test | Use `vi.resetModules()` + dynamic `import()` inside a test that sets the env var before re-importing; the test skill documents this pattern |
|
|
146
|
-
| Adding `(err)` to a bare `catch` in TypeScript is type-safe (`unknown`) but a `noImplicitAny`-adjacent change | `tsconfig` already uses `"useUnknownInCatchVariables": true` (default in strict mode); no cast needed |
|
|
147
|
-
| Verbose worktree.ts threading (8 sites) could miss one | The TDD step runs `pnpm vitest run worktree` and `pnpm run check` before committing |
|
|
148
|
-
|
|
149
|
-
## Open Questions
|
|
150
|
-
|
|
151
|
-
- Should a future issue expose `debugLog` as part of the public `service.ts` API so consumer extensions can share the same debug flag?
|
|
152
|
-
Deferred — out of scope for this change; no consumer currently needs it.
|
|
153
|
-
- Should `PI_SUBAGENTS_DEBUG` be documented in the package `README.md`?
|
|
154
|
-
Likely yes, but deferred to a follow-up doc PR.
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 61
|
|
3
|
-
issue_title: "feat: port subagent transcript logging to Pi's official JSONL session format"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Port subagent transcripts to Pi's official session format
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
Subagent conversation transcripts are written as JSONL by `output-file.ts`, but use a bespoke flat format (`{ isSidechain, agentId, type, message, timestamp, cwd }`) that does not conform to Pi's official session file format.
|
|
11
|
-
Pi's `SessionManager` writes tree-structured JSONL with a session header, UUIDv7 entry IDs, `parentId` tree structure, and typed entry discriminants (`"message"`, `"compaction"`, etc.).
|
|
12
|
-
The bespoke format cannot be loaded by Pi's session tooling (session selector, export-html, resume) and requires manual `jq` inspection for debugging.
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Subagent transcripts written in Pi's official JSONL session format via `SessionManager`.
|
|
17
|
-
- Transcripts discoverable via the parent session path (nested under a parent-session-relative directory).
|
|
18
|
-
- Parent session linkage via the `parentSession` header field.
|
|
19
|
-
- Delete `output-file.ts` — the SDK's `SessionManager` handles all JSONL writing natively.
|
|
20
|
-
- Existing debugging use case preserved (full turn-by-turn history on disk).
|
|
21
|
-
|
|
22
|
-
## Non-Goals
|
|
23
|
-
|
|
24
|
-
- Making subagent sessions resumable via Pi's `/resume` command (future work; requires session selection UX changes).
|
|
25
|
-
- Cross-extension parent-session resolution (issue #22 — separate track).
|
|
26
|
-
- Changing the `SessionManager` usage for the child `AgentSession` itself (the child session already uses `SessionManager.inMemory()`; we replace it with a persisted one).
|
|
27
|
-
|
|
28
|
-
## Background
|
|
29
|
-
|
|
30
|
-
### Current flow
|
|
31
|
-
|
|
32
|
-
1. `agent-tool.ts` calls `createOutputFilePath(cwd, agentId, parentSessionId)` → creates `/tmp/pi-subagents-<uid>/<encoded-cwd>/<parentSessionId>/tasks/<agentId>.output`.
|
|
33
|
-
2. `writeInitialEntry(path, agentId, prompt, cwd)` writes the first user message in the bespoke format.
|
|
34
|
-
3. `streamToOutputFile(session, path, agentId, cwd)` subscribes to `turn_end` events and appends bespoke JSONL entries.
|
|
35
|
-
4. `agent-manager.ts` calls the cleanup function on completion/error to do a final flush and unsubscribe.
|
|
36
|
-
|
|
37
|
-
### Pi's SessionManager API
|
|
38
|
-
|
|
39
|
-
`SessionManager` from `@earendil-works/pi-coding-agent` provides:
|
|
40
|
-
|
|
41
|
-
- `SessionManager.create(cwd, sessionDir?)` — creates a persisted session file in the given directory.
|
|
42
|
-
- `newSession({ parentSession? })` — writes a `SessionHeader` with `parentSession` linking.
|
|
43
|
-
- `appendMessage(message)` — writes a `SessionMessageEntry` with auto-generated UUIDv7 `id` and `parentId` (tree structure).
|
|
44
|
-
- `appendCompaction(...)`, `appendCustomEntry(...)` — first-class support for all entry types.
|
|
45
|
-
- `getSessionFile()` — returns the path to the JSONL file on disk.
|
|
46
|
-
|
|
47
|
-
The child `AgentSession` (created by `createAgentSession`) accepts a `sessionManager` option.
|
|
48
|
-
Currently set to `SessionManager.inMemory(cwd)`, switching to `SessionManager.create(cwd, sessionDir)` makes the SDK write official-format JSONL automatically — no manual streaming code needed.
|
|
49
|
-
|
|
50
|
-
### Reference implementations
|
|
51
|
-
|
|
52
|
-
nicobailon/pi-subagents places subagent sessions under `<parent-session-dir>/<parent-session-basename>/<runId>/`.
|
|
53
|
-
This keeps them discoverable via the parent session path without cluttering the main session list.
|
|
54
|
-
Our approach follows the same pattern: derive a `sessionDir` from the parent session file.
|
|
55
|
-
|
|
56
|
-
### Constraints
|
|
57
|
-
|
|
58
|
-
- `agent-runner.ts` already imports `SessionManager` for the `inMemory()` call — switching to `create()` adds no new dependency.
|
|
59
|
-
- `ctx.sessionManager` is `ReadonlySessionManager` and provides `getSessionId()`, `getSessionFile()`, and `getSessionDir()`.
|
|
60
|
-
- The `code-style` skill requires keeping IO at the edges and not hiding dependencies.
|
|
61
|
-
The session directory derivation should be a pure function that receives the parent session file path.
|
|
62
|
-
|
|
63
|
-
## Design Overview
|
|
64
|
-
|
|
65
|
-
### Core change: persisted SessionManager in agent-runner
|
|
66
|
-
|
|
67
|
-
Replace `SessionManager.inMemory(cwd)` with `SessionManager.create(cwd, sessionDir)` inside `runAgent()`.
|
|
68
|
-
The `sessionDir` is derived from the parent's session file path, and the `parentSession` option links the child to the parent.
|
|
69
|
-
|
|
70
|
-
The key insight is that the SDK's `createAgentSession` already writes all messages through the `SessionManager` it receives.
|
|
71
|
-
By switching from in-memory to persisted, every message the child agent produces is automatically written in official JSONL format — no manual subscription or streaming code required.
|
|
72
|
-
|
|
73
|
-
### Session directory derivation
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
/**
|
|
77
|
-
* Derive the session directory for a subagent from the parent session file.
|
|
78
|
-
* Layout: <parent-dir>/<parent-basename>/tasks/
|
|
79
|
-
*
|
|
80
|
-
* Example:
|
|
81
|
-
* parent: ~/.pi/agent/sessions/--home-user-project--/2026-05-20T12-00-00Z_.jsonl
|
|
82
|
-
* result: ~/.pi/agent/sessions/--home-user-project--/2026-05-20T12-00-00Z_/tasks/
|
|
83
|
-
*
|
|
84
|
-
* Falls back to a temp directory when the parent session is not persisted.
|
|
85
|
-
*/
|
|
86
|
-
function deriveSubagentSessionDir(
|
|
87
|
-
parentSessionFile: string | undefined,
|
|
88
|
-
cwd: string,
|
|
89
|
-
): string;
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Data flow (after)
|
|
93
|
-
|
|
94
|
-
1. `agent-tool.ts` passes `parentSessionFile` (from `ctx.sessionManager.getSessionFile()`) and `parentSessionId` to `agent-manager.ts` via `SpawnOptions`.
|
|
95
|
-
2. `agent-manager.ts` threads them to `agent-runner.ts` via `RunOptions`.
|
|
96
|
-
3. `agent-runner.ts` calls `deriveSubagentSessionDir(parentSessionFile, cwd)` and creates `SessionManager.create(cwd, sessionDir)` with `{ parentSession: parentSessionId }`.
|
|
97
|
-
4. The `createAgentSession` SDK call receives this persisted `SessionManager`.
|
|
98
|
-
5. All messages are written to disk automatically by the SDK.
|
|
99
|
-
6. `agent-runner.ts` returns the session file path in `RunResult` (via `sessionManager.getSessionFile()`).
|
|
100
|
-
7. `agent-tool.ts` stores the path on `AgentRecord.outputFile` for display in notifications/UI.
|
|
101
|
-
|
|
102
|
-
### What gets deleted
|
|
103
|
-
|
|
104
|
-
- `output-file.ts` — entirely replaced by the persisted `SessionManager`.
|
|
105
|
-
- `output-file.test.ts` — the `encodeCwd` tests are no longer needed (Pi's `SessionManager` handles directory encoding internally).
|
|
106
|
-
- `streamToOutputFile`, `writeInitialEntry`, `createOutputFilePath` imports from `agent-tool.ts`.
|
|
107
|
-
- `outputCleanup` field from `AgentRecord` (no manual cleanup needed; the `SessionManager` is append-only).
|
|
108
|
-
- The `onSessionCreated` wrapper in `agent-tool.ts` that wires output-file streaming.
|
|
109
|
-
|
|
110
|
-
### What changes
|
|
111
|
-
|
|
112
|
-
- `RunOptions` gains `parentSessionFile?: string` and `parentSessionId?: string`.
|
|
113
|
-
- `RunResult` gains `sessionFile?: string` (path to the persisted session JSONL).
|
|
114
|
-
- `SpawnOptions` gains `parentSessionFile?: string` and `parentSessionId?: string`.
|
|
115
|
-
- `AgentRecord.outputCleanup` is removed (no manual flush/unsubscribe lifecycle).
|
|
116
|
-
- `agent-manager.ts` removes the `outputCleanup` calls in completion/error paths.
|
|
117
|
-
- `agent-tool.ts` sets `record.outputFile` from the `RunResult.sessionFile` instead of calling `createOutputFilePath`.
|
|
118
|
-
- `notification.ts`, `renderer.ts`, `types.ts` — `outputFile` field semantics unchanged (still a path string); only the source changes.
|
|
119
|
-
|
|
120
|
-
### Edge cases
|
|
121
|
-
|
|
122
|
-
1. **Parent session not persisted** (e.g., API/headless mode where the parent uses `SessionManager.inMemory()`): `ctx.sessionManager.getSessionFile()` returns `undefined`.
|
|
123
|
-
Fallback: use a temp directory under `/tmp/pi-subagents-<uid>/` (similar to current behavior) so transcripts are still written to disk.
|
|
124
|
-
2. **Worktree isolation**: `effectiveCwd` differs from `ctx.cwd`.
|
|
125
|
-
The `SessionManager.create()` call uses `effectiveCwd` (same as current `inMemory` call), but the `sessionDir` is still derived from the parent session — the session header's `cwd` field correctly records the worktree path.
|
|
126
|
-
|
|
127
|
-
## Module-Level Changes
|
|
128
|
-
|
|
129
|
-
### New file: `src/session-dir.ts`
|
|
130
|
-
|
|
131
|
-
Pure function `deriveSubagentSessionDir(parentSessionFile, cwd)`.
|
|
132
|
-
Extracts the parent session basename, constructs `<parent-dir>/<parent-basename>/tasks/`.
|
|
133
|
-
Falls back to a temp directory when `parentSessionFile` is undefined.
|
|
134
|
-
|
|
135
|
-
### Modified: `src/agent-runner.ts`
|
|
136
|
-
|
|
137
|
-
- Import `deriveSubagentSessionDir` from `session-dir.ts`.
|
|
138
|
-
- Add `parentSessionFile?: string` and `parentSessionId?: string` to `RunOptions`.
|
|
139
|
-
- Add `sessionFile?: string` to `RunResult`.
|
|
140
|
-
- Replace `SessionManager.inMemory(cfg.effectiveCwd)` with:
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
const sessionDir = deriveSubagentSessionDir(options.parentSessionFile, cfg.effectiveCwd);
|
|
144
|
-
const sessionManager = SessionManager.create(cfg.effectiveCwd, sessionDir);
|
|
145
|
-
sessionManager.newSession({ parentSession: options.parentSessionId });
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
- After `session.prompt()`, capture `sessionManager.getSessionFile()` into `RunResult.sessionFile`.
|
|
149
|
-
|
|
150
|
-
### Modified: `src/agent-manager.ts`
|
|
151
|
-
|
|
152
|
-
- Add `parentSessionFile?: string` and `parentSessionId?: string` to `SpawnOptions`.
|
|
153
|
-
- Thread them to `RunOptions` in the `runner.run()` call.
|
|
154
|
-
- Remove `outputCleanup` calls from the completion and error handlers.
|
|
155
|
-
|
|
156
|
-
### Modified: `src/tools/agent-tool.ts`
|
|
157
|
-
|
|
158
|
-
- Remove import of `createOutputFilePath`, `streamToOutputFile`, `writeInitialEntry`.
|
|
159
|
-
- Remove the `onSessionCreated` wrapper that wired output-file streaming.
|
|
160
|
-
- Pass `parentSessionFile: ctx.sessionManager.getSessionFile()` and `parentSessionId: ctx.sessionManager.getSessionId()` in `SpawnOptions`.
|
|
161
|
-
- After spawn, set `record.outputFile` from the returned session file path (available via `onSessionCreated` callback which gives access to the session's `sessionManager`).
|
|
162
|
-
|
|
163
|
-
### Modified: `src/types.ts`
|
|
164
|
-
|
|
165
|
-
- Remove `outputCleanup?: () => void` from `AgentRecord`.
|
|
166
|
-
|
|
167
|
-
### Deleted: `src/output-file.ts`
|
|
168
|
-
|
|
169
|
-
Entire file removed.
|
|
170
|
-
|
|
171
|
-
### Deleted: `test/output-file.test.ts`
|
|
172
|
-
|
|
173
|
-
Entire file removed.
|
|
174
|
-
|
|
175
|
-
### Unchanged: `src/notification.ts`, `src/renderer.ts`
|
|
176
|
-
|
|
177
|
-
These read `record.outputFile` (a path string) — the field still exists, just populated differently.
|
|
178
|
-
The `NotificationDetails.outputFile` field is unchanged.
|
|
179
|
-
|
|
180
|
-
## Test Impact Analysis
|
|
181
|
-
|
|
182
|
-
### New tests enabled by extraction
|
|
183
|
-
|
|
184
|
-
1. `test/session-dir.test.ts` — unit tests for `deriveSubagentSessionDir`:
|
|
185
|
-
- Parent session file present → derives correct nested path.
|
|
186
|
-
- Parent session file undefined → falls back to temp directory.
|
|
187
|
-
- Various path shapes (POSIX, Windows-like).
|
|
188
|
-
|
|
189
|
-
2. `test/agent-runner.test.ts` — updated tests verifying:
|
|
190
|
-
- `SessionManager.create` is called (not `inMemory`) when `parentSessionFile` is provided.
|
|
191
|
-
- `newSession({ parentSession })` is called with the parent session ID.
|
|
192
|
-
- `RunResult.sessionFile` is populated from the session manager.
|
|
193
|
-
|
|
194
|
-
### Tests that become redundant
|
|
195
|
-
|
|
196
|
-
- `test/output-file.test.ts` (`encodeCwd` tests) — the directory encoding is now handled by `deriveSubagentSessionDir` with a different layout.
|
|
197
|
-
The `encodeCwd` function is deleted.
|
|
198
|
-
|
|
199
|
-
### Tests that stay as-is
|
|
200
|
-
|
|
201
|
-
- All `agent-manager.test.ts` tests that mock the runner — they don't depend on `output-file` internals.
|
|
202
|
-
- All `notification.test.ts` and `renderer.test.ts` tests — they read `record.outputFile` which remains a string.
|
|
203
|
-
- The `onSessionCreated` callback tests in `agent-tool.test.ts` need updating to remove the output-file wiring expectations.
|
|
204
|
-
|
|
205
|
-
## TDD Order
|
|
206
|
-
|
|
207
|
-
### Step 1: Add `deriveSubagentSessionDir` with tests
|
|
208
|
-
|
|
209
|
-
1. Create `src/session-dir.ts` with the pure function.
|
|
210
|
-
2. Create `test/session-dir.test.ts` with tests for parent-present and fallback cases.
|
|
211
|
-
3. Commit: `feat: add deriveSubagentSessionDir for session directory derivation (#61)`
|
|
212
|
-
|
|
213
|
-
### Step 2: Thread parent session info through SpawnOptions and RunOptions
|
|
214
|
-
|
|
215
|
-
1. Add `parentSessionFile?: string` and `parentSessionId?: string` to `SpawnOptions` in `agent-manager.ts`.
|
|
216
|
-
2. Add `parentSessionFile?: string` and `parentSessionId?: string` to `RunOptions` in `agent-runner.ts`.
|
|
217
|
-
3. Add `sessionFile?: string` to `RunResult` in `agent-runner.ts`.
|
|
218
|
-
4. Thread the new fields from `SpawnOptions` → `RunOptions` in `agent-manager.ts`.
|
|
219
|
-
5. Update tests to verify the new fields are threaded.
|
|
220
|
-
6. Commit: `feat: thread parent session info through spawn and run options (#61)`
|
|
221
|
-
|
|
222
|
-
### Step 3: Switch agent-runner to persisted SessionManager
|
|
223
|
-
|
|
224
|
-
1. In `runAgent()`, replace `SessionManager.inMemory(cfg.effectiveCwd)` with the persisted variant using `deriveSubagentSessionDir`.
|
|
225
|
-
2. Call `sessionManager.newSession({ parentSession: options.parentSessionId })`.
|
|
226
|
-
3. Capture `sessionManager.getSessionFile()` into `RunResult.sessionFile`.
|
|
227
|
-
4. Update `agent-runner.test.ts` mocks to expect `SessionManager.create` instead of `SessionManager.inMemory`.
|
|
228
|
-
5. Commit: `feat: use persisted SessionManager for subagent sessions (#61)`
|
|
229
|
-
|
|
230
|
-
### Step 4: Remove output-file wiring from agent-tool and agent-manager
|
|
231
|
-
|
|
232
|
-
1. Remove the `onSessionCreated` output-file wrapper in `agent-tool.ts`.
|
|
233
|
-
2. Remove `createOutputFilePath`, `writeInitialEntry`, `streamToOutputFile` imports.
|
|
234
|
-
3. Set `record.outputFile` from the session manager's file path (via `onSessionCreated` callback).
|
|
235
|
-
4. Pass `parentSessionFile` and `parentSessionId` in spawn options.
|
|
236
|
-
5. Remove `outputCleanup` calls from `agent-manager.ts` completion/error handlers.
|
|
237
|
-
6. Remove `outputCleanup` from `AgentRecord` in `types.ts`.
|
|
238
|
-
7. Update agent-tool and agent-manager tests.
|
|
239
|
-
8. Commit: `feat: wire session file path through agent-tool, remove output-file streaming (#61)`
|
|
240
|
-
|
|
241
|
-
### Step 5: Delete output-file.ts and its tests
|
|
242
|
-
|
|
243
|
-
1. Delete `src/output-file.ts`.
|
|
244
|
-
2. Delete `test/output-file.test.ts`.
|
|
245
|
-
3. Run full test suite to verify no remaining references.
|
|
246
|
-
4. Commit: `feat!: remove bespoke output-file transcript format (#61)`
|
|
247
|
-
|
|
248
|
-
### Step 6: Documentation update
|
|
249
|
-
|
|
250
|
-
1. Update `docs/architecture/architecture.md` — remove `output-file.ts` from the module listing, add `session-dir.ts`, mark #61 as complete.
|
|
251
|
-
2. Commit: `docs: update architecture for session format migration (#61)`
|
|
252
|
-
|
|
253
|
-
## Risks and Mitigations
|
|
254
|
-
|
|
255
|
-
### Risk: SessionManager.create may fail in environments without a writable home directory
|
|
256
|
-
|
|
257
|
-
The temp-directory fallback in `deriveSubagentSessionDir` handles this case.
|
|
258
|
-
When the parent session file is unavailable (headless/API mode), we fall back to a temp directory just like the current implementation.
|
|
259
|
-
|
|
260
|
-
### Risk: Persisted sessions accumulate disk space over time
|
|
261
|
-
|
|
262
|
-
Pi's session tooling already manages session storage.
|
|
263
|
-
Subagent sessions nested under the parent session directory are cleaned up when the parent session is deleted.
|
|
264
|
-
This is an improvement over the current `/tmp` location where files persist until system cleanup.
|
|
265
|
-
|
|
266
|
-
### Risk: Tests that mock SessionManager.inMemory need updating
|
|
267
|
-
|
|
268
|
-
Step 3 explicitly plans for updating these mocks.
|
|
269
|
-
The change is localized to `agent-runner.ts` and its direct test file.
|
|
270
|
-
|
|
271
|
-
### Risk: Breaking change for consumers that read outputFile paths
|
|
272
|
-
|
|
273
|
-
The `outputFile` field changes from `/tmp/pi-subagents-<uid>/.../tasks/<agentId>.output` to `~/.pi/agent/sessions/.../<timestamp>_<uuid>.jsonl`.
|
|
274
|
-
Consumers that parse the path (unlikely) would break, but the field is internal to the extension and only used for display.
|
|
275
|
-
The format of the file content changes from bespoke JSONL to Pi's official format — this is the intentional breaking change.
|
|
276
|
-
|
|
277
|
-
## Open Questions
|
|
278
|
-
|
|
279
|
-
1. Should the plan add a `sessionDir` field to `AgentRecord` alongside `outputFile`, or is the session file path sufficient for all use cases?
|
|
280
|
-
Deferred — start with `outputFile` pointing to the session file; add `sessionDir` if needed later.
|
|
281
|
-
2. Should foreground agents also get persisted sessions, or only background agents?
|
|
282
|
-
The current `output-file` code only runs for background agents.
|
|
283
|
-
Persisted sessions are valuable for both — the plan applies the change in `agent-runner.ts` which serves both paths.
|
|
284
|
-
If this proves too noisy, a follow-up can gate it behind a setting.
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 66
|
|
3
|
-
issue_title: "refactor: replace `as any` casts in extracted tool/menu factories with proper SDK types"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Replace `as any` casts in factory dep interfaces with proper SDK types
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
The decomposition in #54 introduced 14 `as any` casts at the wiring boundary in `src/index.ts`.
|
|
11
|
-
These exist because the factory dep interfaces (`AgentToolManager`, `GetResultDeps`, `SteerToolDeps`, `AgentMenuDeps`, `NotificationDeps`) declare `unknown` or `object` for parameters that are actually well-typed SDK exports.
|
|
12
|
-
Every cast papers over a real type that the SDK already exports.
|
|
13
|
-
For comparison, `pi-permission-system` imports SDK types directly in its internal interfaces and has zero `as any` casts.
|
|
14
|
-
|
|
15
|
-
## Goals
|
|
16
|
-
|
|
17
|
-
- Replace every `as any` cast in `src/index.ts` by typing the corresponding dep interface parameter with the actual SDK type.
|
|
18
|
-
- Keep factory modules decoupled from `AgentManager` where possible — use shared types (`types.ts`) or SDK imports.
|
|
19
|
-
- No runtime behavior change — this is purely a type-safety improvement.
|
|
20
|
-
- Existing tests continue to pass without modification (factory `execute` methods use `ctx: any` or `ctx: unknown`, so test mocks are unaffected).
|
|
21
|
-
|
|
22
|
-
## Non-Goals
|
|
23
|
-
|
|
24
|
-
- Fixing `as any` casts in `agent-runner.ts`, `conversation-viewer.ts`, `tools/helpers.ts`, or `tools/agent-tool.ts:550` — those access untyped SDK message internals, a different concern.
|
|
25
|
-
- Removing the `ctx as UICtx` cast (line 196) — that is a named cast, not `as any`.
|
|
26
|
-
- Removing `as any` from test files (e.g., `agent-menu.test.ts`'s `ctx as any`) — test mocks are intentionally narrow.
|
|
27
|
-
|
|
28
|
-
## Background
|
|
29
|
-
|
|
30
|
-
### Current cast inventory (`src/index.ts`)
|
|
31
|
-
|
|
32
|
-
| Line | Cast | Root cause |
|
|
33
|
-
| ---- | ----------------------------------------------- | --------------------------------------------------------------------------- |
|
|
34
|
-
| 59 | `msg as any, opts as any` | `NotificationDeps.sendMessage(msg: unknown, opts: unknown)` |
|
|
35
|
-
| 188 | `ctx as any, opts as any` | `AgentToolManager.spawn(ctx: unknown, …, opts: object)` |
|
|
36
|
-
| 189 | `ctx as any, opts as any` | `AgentToolManager.spawnAndWait(ctx: unknown, …, opts: object)` |
|
|
37
|
-
| 208 | `}) as any` | Cascading from above — `createAgentTool` return doesn't match `defineTool` |
|
|
38
|
-
| 215 | `session as any` | `GetResultDeps.getConversation(session: unknown)` |
|
|
39
|
-
| 223 | `session as any` | `SteerToolDeps.steerAgent(session: unknown, …)` |
|
|
40
|
-
| 232 | `(piArg ?? pi) as any, ctx as any, opts as any` | `AgentMenuManager.spawnAndWait(pi: unknown, ctx: unknown, …, opts: object)` |
|
|
41
|
-
| 242 | `registry as any` | `AgentMenuDeps.getModelLabel(…, registry?: unknown)` |
|
|
42
|
-
| 271 | `ctx as any` | `MenuContext` not structurally compatible with `ExtensionCommandContext` |
|
|
43
|
-
|
|
44
|
-
### SDK types available
|
|
45
|
-
|
|
46
|
-
All are exported from `@earendil-works/pi-coding-agent`:
|
|
47
|
-
|
|
48
|
-
- `ExtensionContext` — tool execute context (has `ui`, `modelRegistry`, `cwd`, etc.)
|
|
49
|
-
- `ExtensionCommandContext` — command handler context (extends `ExtensionContext`)
|
|
50
|
-
- `ExtensionAPI` — the `pi` object passed to extensions
|
|
51
|
-
- `AgentSession` — session handle (has `steer()`, `messages`, etc.)
|
|
52
|
-
|
|
53
|
-
`ModelRegistry` is already defined locally in `src/model-resolver.ts` and matches the SDK shape.
|
|
54
|
-
`SpawnOptions` is defined locally in `src/agent-manager.ts` (not currently exported).
|
|
55
|
-
|
|
56
|
-
### Relevant AGENTS.md constraints
|
|
57
|
-
|
|
58
|
-
- "Avoid `any` unless absolutely necessary."
|
|
59
|
-
- "Keep Pi SDK imports out of business-logic modules."
|
|
60
|
-
Tool definitions, event handlers, and command handlers are SDK consumers — they may import SDK types directly.
|
|
61
|
-
The restriction targets pure helpers, utilities, and domain modules.
|
|
62
|
-
- "When writing event handlers that consume Pi SDK types, prefer lean local payload interfaces over full SDK event types."
|
|
63
|
-
|
|
64
|
-
The factory modules (`tools/agent-tool.ts`, `tools/get-result-tool.ts`, `tools/steer-tool.ts`, `ui/agent-menu.ts`) are SDK consumers (they define tools and command handlers), so importing SDK types is acceptable.
|
|
65
|
-
The notification module (`notification.ts`) is a pure helper — it should use narrow local types rather than SDK imports.
|
|
66
|
-
|
|
67
|
-
## Design Overview
|
|
68
|
-
|
|
69
|
-
### Strategy per interface
|
|
70
|
-
|
|
71
|
-
1. **`NotificationDeps.sendMessage`** — define narrow inline parameter types matching `ExtensionAPI.sendMessage`'s shape.
|
|
72
|
-
No SDK import needed; the notification module stays SDK-independent.
|
|
73
|
-
|
|
74
|
-
2. **`AgentToolManager`** — import `ExtensionContext` for `ctx`, export + import `SpawnOptions` from `agent-manager.ts` for `opts`.
|
|
75
|
-
The tool module is an SDK consumer, so SDK imports are acceptable.
|
|
76
|
-
|
|
77
|
-
3. **`AgentToolWidget.setUICtx`** — stays `ctx: unknown` (not `as any`; currently `ctx as UICtx`).
|
|
78
|
-
Out of scope.
|
|
79
|
-
|
|
80
|
-
4. **`GetResultDeps.getConversation`** — import `AgentSession` for `session`.
|
|
81
|
-
|
|
82
|
-
5. **`SteerToolDeps.steerAgent`** — import `AgentSession` for `session`.
|
|
83
|
-
|
|
84
|
-
6. **`AgentMenuManager.spawnAndWait`** — import `ExtensionAPI` for `pi`, `ExtensionContext` for `ctx`, export + import `SpawnOptions` for `opts`.
|
|
85
|
-
|
|
86
|
-
7. **`AgentMenuDeps.getModelLabel`** — import `ModelRegistry` from `../model-resolver.js` for `registry`.
|
|
87
|
-
|
|
88
|
-
8. **`MenuContext` structural compatibility** — switch `MenuUI` from property syntax (strict function types) to method syntax (bivariant), and type `modelRegistry` as `ModelRegistry`.
|
|
89
|
-
This makes `ExtensionCommandContext` structurally assignable to `MenuContext`, removing the cast without broadening the handler's dependency on the full SDK type.
|
|
90
|
-
Tests continue using narrow mocks because the handler's parameter is still the narrow `MenuContext`.
|
|
91
|
-
|
|
92
|
-
### `SpawnOptions` export
|
|
93
|
-
|
|
94
|
-
`SpawnOptions` in `agent-manager.ts` is currently a private interface.
|
|
95
|
-
Exporting it as a named `type` export adds no runtime cost and avoids duplicating the 15-field type in each factory.
|
|
96
|
-
Both `tools/agent-tool.ts` and `ui/agent-menu.ts` already import `AgentRecord` from `types.ts`, so an intra-package `import type` from `agent-manager.ts` follows the same pattern.
|
|
97
|
-
|
|
98
|
-
### Cascading cast resolution
|
|
99
|
-
|
|
100
|
-
The `createAgentTool({…}) as any` cast on line 208 exists because TypeScript cannot verify the returned object satisfies `ToolDefinition` when inner types are `unknown`/`object`.
|
|
101
|
-
Once the inner types are correct, the factory return type matches `ToolDefinition` naturally, and the outer `as any` cast resolves without further changes.
|
|
102
|
-
|
|
103
|
-
## Module-Level Changes
|
|
104
|
-
|
|
105
|
-
### `src/agent-manager.ts`
|
|
106
|
-
|
|
107
|
-
- Export the existing `SpawnOptions` interface (add `export` keyword).
|
|
108
|
-
|
|
109
|
-
### `src/notification.ts`
|
|
110
|
-
|
|
111
|
-
- Replace `sendMessage: (msg: unknown, opts: unknown) => void` with narrow inline types:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
sendMessage: (
|
|
115
|
-
msg: { customType: string; content: string; display?: boolean; details?: unknown },
|
|
116
|
-
opts?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
117
|
-
) => void;
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### `src/tools/agent-tool.ts`
|
|
121
|
-
|
|
122
|
-
- Add `import type { ExtensionContext } from "@earendil-works/pi-coding-agent";`
|
|
123
|
-
- Add `import type { SpawnOptions } from "../agent-manager.js";`
|
|
124
|
-
- `AgentToolManager.spawn`: `ctx: unknown` → `ctx: ExtensionContext`, `opts: object` → `opts: SpawnOptions`
|
|
125
|
-
- `AgentToolManager.spawnAndWait`: same changes, opts as `Omit<SpawnOptions, "isBackground">`
|
|
126
|
-
|
|
127
|
-
### `src/tools/get-result-tool.ts`
|
|
128
|
-
|
|
129
|
-
- Add `import type { AgentSession } from "@earendil-works/pi-coding-agent";`
|
|
130
|
-
- `GetResultDeps.getConversation`: `session: unknown` → `session: AgentSession`
|
|
131
|
-
|
|
132
|
-
### `src/tools/steer-tool.ts`
|
|
133
|
-
|
|
134
|
-
- Add `import type { AgentSession } from "@earendil-works/pi-coding-agent";`
|
|
135
|
-
- `SteerToolDeps.steerAgent`: `session: unknown` → `session: AgentSession`
|
|
136
|
-
|
|
137
|
-
### `src/ui/agent-menu.ts`
|
|
138
|
-
|
|
139
|
-
- Add `import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";`
|
|
140
|
-
- Add `import type { SpawnOptions } from "../agent-manager.js";`
|
|
141
|
-
- Add `import type { ModelRegistry } from "../model-resolver.js";`
|
|
142
|
-
- `AgentMenuManager.spawnAndWait`: `pi: unknown` → `pi: ExtensionAPI | null`, `ctx: unknown` → `ctx: ExtensionContext`, `opts: object` → `opts: Omit<SpawnOptions, "isBackground">`
|
|
143
|
-
- `AgentMenuDeps.getModelLabel`: `registry?: unknown` → `registry?: ModelRegistry`
|
|
144
|
-
- `MenuUI`: switch from property syntax to method syntax for structural compatibility with `ExtensionUIContext`.
|
|
145
|
-
Update `notify` second parameter from `level: string` to `type?: string` to match SDK.
|
|
146
|
-
- `MenuContext.modelRegistry`: `unknown` → `ModelRegistry`
|
|
147
|
-
|
|
148
|
-
### `src/index.ts`
|
|
149
|
-
|
|
150
|
-
- Remove all 14 `as any` casts.
|
|
151
|
-
- No new imports needed (types already used transitively).
|
|
152
|
-
|
|
153
|
-
## Test Impact Analysis
|
|
154
|
-
|
|
155
|
-
1. **No new unit tests needed** — this is a type-only refactoring with no behavioral change.
|
|
156
|
-
2. **No existing tests become redundant** — the tests exercise tool execution and menu behavior, not type signatures.
|
|
157
|
-
3. **Existing tests must stay as-is** — factory `execute` methods use `ctx: any` or `ctx: unknown`, so `makeCtx()` test helpers remain compatible.
|
|
158
|
-
Test-side `as any` casts (e.g., `handler(ctx as any)` in `agent-menu.test.ts`) are out of scope.
|
|
159
|
-
|
|
160
|
-
## TDD Order
|
|
161
|
-
|
|
162
|
-
Since this is a type-only change, each step is verified by `pnpm run check` (tsc) rather than new vitest tests.
|
|
163
|
-
The full test suite (`pnpm vitest run`) is run at the end as a regression check.
|
|
164
|
-
|
|
165
|
-
1. **Export `SpawnOptions`** — add `export` to the existing `interface SpawnOptions` in `agent-manager.ts`.
|
|
166
|
-
Verify: `pnpm run check`.
|
|
167
|
-
Commit: `refactor: export SpawnOptions from agent-manager`
|
|
168
|
-
|
|
169
|
-
2. **Type `NotificationDeps`** — replace `(msg: unknown, opts: unknown)` with narrow inline types in `notification.ts`.
|
|
170
|
-
Remove `msg as any, opts as any` casts (line 59) in `index.ts`.
|
|
171
|
-
Verify: `pnpm run check`.
|
|
172
|
-
Commit: `refactor: type NotificationDeps.sendMessage parameters (#66)`
|
|
173
|
-
|
|
174
|
-
3. **Type `AgentToolManager`** — import `ExtensionContext` and `SpawnOptions`, update `spawn` and `spawnAndWait` signatures in `agent-tool.ts`.
|
|
175
|
-
Remove `ctx as any, opts as any` casts (lines 188–189) and the cascading `}) as any` cast (line 208) in `index.ts`.
|
|
176
|
-
Verify: `pnpm run check`.
|
|
177
|
-
Commit: `refactor: type AgentToolManager with ExtensionContext and SpawnOptions (#66)`
|
|
178
|
-
|
|
179
|
-
4. **Type `GetResultDeps`** — import `AgentSession`, update `getConversation` in `get-result-tool.ts`.
|
|
180
|
-
Remove `session as any` cast (line 215) in `index.ts`.
|
|
181
|
-
Verify: `pnpm run check`.
|
|
182
|
-
Commit: `refactor: type GetResultDeps.getConversation with AgentSession (#66)`
|
|
183
|
-
|
|
184
|
-
5. **Type `SteerToolDeps`** — import `AgentSession`, update `steerAgent` in `steer-tool.ts`.
|
|
185
|
-
Remove `session as any` cast (line 223) in `index.ts`.
|
|
186
|
-
Verify: `pnpm run check`.
|
|
187
|
-
Commit: `refactor: type SteerToolDeps.steerAgent with AgentSession (#66)`
|
|
188
|
-
|
|
189
|
-
6. **Type `AgentMenuManager` + `AgentMenuDeps`** — import `ExtensionAPI`, `ExtensionContext`, `SpawnOptions`, `ModelRegistry`.
|
|
190
|
-
Update `spawnAndWait` and `getModelLabel` signatures in `agent-menu.ts`.
|
|
191
|
-
Remove `(piArg ?? pi) as any, ctx as any, opts as any` (line 232) and `registry as any` (line 242) casts in `index.ts`.
|
|
192
|
-
Verify: `pnpm run check`.
|
|
193
|
-
Commit: `refactor: type AgentMenu interfaces with SDK types (#66)`
|
|
194
|
-
|
|
195
|
-
7. **Align `MenuContext` with `ExtensionCommandContext`** — switch `MenuUI` to method syntax, fix `notify` parameter, type `modelRegistry` as `ModelRegistry`.
|
|
196
|
-
Remove `ctx as any` cast (line 271) in `index.ts`.
|
|
197
|
-
Verify: `pnpm run check`.
|
|
198
|
-
Commit: `refactor: align MenuContext for structural ExtensionCommandContext compat (#66)`
|
|
199
|
-
|
|
200
|
-
8. **Final verification** — run `pnpm vitest run` for the full test suite.
|
|
201
|
-
Grep `src/index.ts` for remaining `as any` — expect zero.
|
|
202
|
-
Commit: none (verification only).
|
|
203
|
-
|
|
204
|
-
## Risks and Mitigations
|
|
205
|
-
|
|
206
|
-
| Risk | Mitigation |
|
|
207
|
-
| ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
208
|
-
| Exporting `SpawnOptions` from `agent-manager.ts` couples factory modules to the manager's type | The coupling is type-only (`import type`), adds no runtime dependency, and follows the existing pattern (`AgentRecord` in `types.ts` is already shared). |
|
|
209
|
-
| `MenuUI` method-syntax change could break structural compatibility with test mocks | Test mocks use `vi.fn()` which is bivariant; method syntax on the `MenuUI` interface only affects how TypeScript checks assignability from `ExtensionUIContext`, not from mock objects. |
|
|
210
|
-
| SDK type exports could change in a future Pi release | The SDK types used (`ExtensionContext`, `AgentSession`, `ExtensionAPI`) are stable public API. `ModelRegistry` is a local interface. |
|
|
211
|
-
| Cascading `as any` on `createAgentTool` may not resolve automatically | If TypeScript still cannot infer the return type, add explicit `satisfies ToolDefinition<…>` or a return type annotation. Verified in step 3. |
|
|
212
|
-
|
|
213
|
-
## Open Questions
|
|
214
|
-
|
|
215
|
-
- None — the issue's "Proposed change" section is unambiguous and all SDK types are confirmed available.
|