@gotgenes/pi-subagents 18.0.0 → 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 +14 -0
- package/README.md +22 -31
- 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/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/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 -45
- package/docs/retro/0442-dissolve-agents-conversation-viewer.md +0 -108
- 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/src/observation/notification-state.ts +0 -27
- package/vitest.config.ts +0 -14
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 169
|
|
3
|
-
issue_title: "refactor(pi-subagents): extract RunContext from RunOptions (12 fields)"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Extract RunContext from RunOptions
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
`RunOptions` in `agent-runner.ts` has 12 fields mixing two distinct concerns: parent execution context ("where/who is running") and per-call execution parameters ("how to run").
|
|
11
|
-
Extracting the context cluster into a named `RunContext` interface makes the separation explicit and reduces the flat field count from 12 to 9 (8 execution fields + 1 nested context).
|
|
12
|
-
|
|
13
|
-
## Goals
|
|
14
|
-
|
|
15
|
-
- Define a `RunContext` interface grouping the 4 parent-context fields: `exec`, `registry`, `cwd`, and `parentSession`.
|
|
16
|
-
- Nest `RunContext` inside `RunOptions` as `context: RunContext`, replacing the 4 flat fields.
|
|
17
|
-
- Update `runAgent()` to read context fields from `options.context.*`.
|
|
18
|
-
- Update `AgentManager.startAgent()` to construct the nested `context` object when building `RunOptions`.
|
|
19
|
-
- Update all test files that construct or assert on `RunOptions` fields.
|
|
20
|
-
- Non-breaking refactor — `RunOptions` is not part of the public API (`service.ts` export boundary).
|
|
21
|
-
|
|
22
|
-
## Non-Goals
|
|
23
|
-
|
|
24
|
-
- Changing the `AgentRunner` interface signature — `run()` keeps its 4 positional parameters; `RunContext` is nested inside `RunOptions`, not a separate parameter.
|
|
25
|
-
- Extracting `RunContext` into its own file — the interface is small (4 fields) and co-located with its consumer (`runAgent`).
|
|
26
|
-
- Further splitting the remaining 8 execution fields — they form a coherent "how to run" cluster.
|
|
27
|
-
- Hoisting `RunContext` construction to `AgentManager` instance level — two of the four fields (`cwd`, `parentSession`) vary per spawn, so a per-spawn construction is appropriate.
|
|
28
|
-
|
|
29
|
-
## Background
|
|
30
|
-
|
|
31
|
-
Issue #164 (closed) reorganized source into domain directories; the runner now lives at `src/lifecycle/agent-runner.ts`.
|
|
32
|
-
Issue #166 (closed) extracted `ParentSessionInfo` and nested it inside `RunOptions.parentSession`.
|
|
33
|
-
Issue #167 (closed) split `RunnerIO` into `EnvironmentIO` and `SessionFactoryIO`.
|
|
34
|
-
Issue #168 (closed) extracted `ToolFilterConfig` from `SessionConfig`.
|
|
35
|
-
|
|
36
|
-
This issue continues the structural improvement by separating the two concerns mixed in `RunOptions`.
|
|
37
|
-
|
|
38
|
-
### Field analysis
|
|
39
|
-
|
|
40
|
-
| Field | Concern | Usage in `runAgent()` |
|
|
41
|
-
| ------------------ | --------- | ------------------------------------------ |
|
|
42
|
-
| `exec` | Context | `io.detectEnv(options.exec, effectiveCwd)` |
|
|
43
|
-
| `registry` | Context | Passed to `assembleSessionConfig` |
|
|
44
|
-
| `cwd` | Context | Override working directory (worktree) |
|
|
45
|
-
| `parentSession` | Context | Session dir derivation + session linking |
|
|
46
|
-
| `model` | Execution | Per-call model override |
|
|
47
|
-
| `maxTurns` | Execution | Turn limit |
|
|
48
|
-
| `signal` | Execution | Abort forwarding |
|
|
49
|
-
| `isolated` | Execution | Extension isolation flag |
|
|
50
|
-
| `thinkingLevel` | Execution | Thinking level override |
|
|
51
|
-
| `onSessionCreated` | Execution | Session delivery callback |
|
|
52
|
-
| `defaultMaxTurns` | Execution | Fallback turn limit from runtime config |
|
|
53
|
-
| `graceTurns` | Execution | Grace window after soft limit |
|
|
54
|
-
|
|
55
|
-
### Consumer analysis
|
|
56
|
-
|
|
57
|
-
`AgentManager.startAgent()` is the sole constructor of `RunOptions`.
|
|
58
|
-
The context fields come from two sources:
|
|
59
|
-
|
|
60
|
-
- Manager instance fields: `this.exec`, `this.registry`
|
|
61
|
-
- Per-spawn values: `worktreeCwd` (computed locally), `options.parentSession` (from `AgentSpawnConfig`)
|
|
62
|
-
|
|
63
|
-
## Design Overview
|
|
64
|
-
|
|
65
|
-
### `RunContext` interface
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
export interface RunContext {
|
|
69
|
-
/** Shell-exec callback for detectEnv — injected from pi.exec(). */
|
|
70
|
-
exec: ShellExec;
|
|
71
|
-
/** Agent config lookup — provides resolveAgentConfig and getToolNamesForType. */
|
|
72
|
-
registry: AgentConfigLookup;
|
|
73
|
-
/** Override working directory (e.g. for worktree isolation). */
|
|
74
|
-
cwd?: string;
|
|
75
|
-
/** Parent session identity (file path + session ID). */
|
|
76
|
-
parentSession?: ParentSessionInfo;
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Updated `RunOptions`
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
export interface RunOptions {
|
|
84
|
-
/** Parent execution context — where/who is running. */
|
|
85
|
-
context: RunContext;
|
|
86
|
-
model?: Model<any>;
|
|
87
|
-
maxTurns?: number;
|
|
88
|
-
signal?: AbortSignal;
|
|
89
|
-
isolated?: boolean;
|
|
90
|
-
thinkingLevel?: ThinkingLevel;
|
|
91
|
-
onSessionCreated?: (session: AgentSession) => void;
|
|
92
|
-
defaultMaxTurns?: number;
|
|
93
|
-
graceTurns?: number;
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Call-site sketch — `AgentManager.startAgent`
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
const promise = this.runner.run(snapshot, type, prompt, {
|
|
101
|
-
context: {
|
|
102
|
-
exec: this.exec,
|
|
103
|
-
registry: this.registry,
|
|
104
|
-
cwd: worktreeCwd,
|
|
105
|
-
parentSession: options.parentSession,
|
|
106
|
-
},
|
|
107
|
-
model: options.model,
|
|
108
|
-
maxTurns: options.maxTurns,
|
|
109
|
-
// ... remaining execution fields
|
|
110
|
-
});
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Access pattern in `runAgent`
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
const effectiveCwd = options.context.cwd ?? snapshot.cwd;
|
|
117
|
-
const env = await io.detectEnv(options.context.exec, effectiveCwd);
|
|
118
|
-
// ...
|
|
119
|
-
const sessionDir = io.deriveSessionDir(
|
|
120
|
-
options.context.parentSession?.parentSessionFile,
|
|
121
|
-
cfg.effectiveCwd,
|
|
122
|
-
);
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Module-Level Changes
|
|
126
|
-
|
|
127
|
-
### `src/lifecycle/agent-runner.ts`
|
|
128
|
-
|
|
129
|
-
1. Add `RunContext` interface (4 fields, exported) before `RunOptions`.
|
|
130
|
-
2. Replace the 4 flat context fields on `RunOptions` with `context: RunContext`.
|
|
131
|
-
3. Update all `options.*` reads in `runAgent()`:
|
|
132
|
-
- `options.exec` → `options.context.exec`
|
|
133
|
-
- `options.cwd` → `options.context.cwd`
|
|
134
|
-
- `options.parentSession` → `options.context.parentSession`
|
|
135
|
-
- `options.registry` → `options.context.registry`
|
|
136
|
-
4. Move JSDoc from the removed flat fields to `RunContext` interface members.
|
|
137
|
-
5. Export `RunContext`.
|
|
138
|
-
|
|
139
|
-
### `src/lifecycle/agent-manager.ts`
|
|
140
|
-
|
|
141
|
-
1. Update the `RunOptions` object literal in `startAgent()` to nest the four context fields under `context: { ... }`.
|
|
142
|
-
2. No import changes needed — `RunOptions` is consumed via the `AgentRunner` interface, not imported directly.
|
|
143
|
-
|
|
144
|
-
### No changes needed
|
|
145
|
-
|
|
146
|
-
- `src/lifecycle/agent-runner.ts` — `AgentRunner` interface signature unchanged (`options: RunOptions`).
|
|
147
|
-
- `src/lifecycle/agent-runner.ts` — `createAgentRunner()` unchanged.
|
|
148
|
-
- `src/index.ts` — no changes (doesn't import `RunOptions`).
|
|
149
|
-
- `src/runtime.ts` — comment-only reference to `RunOptions`; update comment if desired.
|
|
150
|
-
- `src/session/session-config.ts` — comment-only reference; update comment if desired.
|
|
151
|
-
|
|
152
|
-
## Test Impact Analysis
|
|
153
|
-
|
|
154
|
-
### New unit tests enabled
|
|
155
|
-
|
|
156
|
-
The extraction does not enable new test surfaces — `RunContext` is a plain data carrier with no behavior.
|
|
157
|
-
A type-check verification (`pnpm run check`) confirms the structural compatibility.
|
|
158
|
-
|
|
159
|
-
### Existing tests that need updates
|
|
160
|
-
|
|
161
|
-
1. `test/lifecycle/agent-runner.test.ts` — 9 `runAgent()` call sites: wrap `exec`, `registry`, `cwd`, and `parentSession` fields in `context: { ... }`.
|
|
162
|
-
2. `test/lifecycle/agent-runner-extension-tools.test.ts` — 7 `runAgent()` call sites: same wrapping.
|
|
163
|
-
3. `test/lifecycle/agent-manager.test.ts` — 3 assertion sites: update `runOpts.parentSession` → `runOpts.context.parentSession`, `runOpts.defaultMaxTurns` stays flat (execution field).
|
|
164
|
-
|
|
165
|
-
### Tests that stay as-is
|
|
166
|
-
|
|
167
|
-
- `test/lifecycle/agent-runner-settings.test.ts` — tests `normalizeMaxTurns` (pure function, no `RunOptions` involvement).
|
|
168
|
-
- All other test files — no `RunOptions` construction or assertion.
|
|
169
|
-
|
|
170
|
-
## TDD Order
|
|
171
|
-
|
|
172
|
-
1. **Define `RunContext` and update `RunOptions`** — add `RunContext` interface, replace 4 flat fields with `context: RunContext` on `RunOptions`.
|
|
173
|
-
Update all `options.*` reads in `runAgent()` to `options.context.*`.
|
|
174
|
-
Update `agent-manager.ts` `startAgent()` to construct nested context.
|
|
175
|
-
Update `agent-runner.test.ts` (9 call sites) and `agent-runner-extension-tools.test.ts` (7 call sites) to nest context fields.
|
|
176
|
-
Update `agent-manager.test.ts` assertions (3 sites) to read from `runOpts.context.*`.
|
|
177
|
-
Run `pnpm run check` and `pnpm vitest run` to verify.
|
|
178
|
-
Commit: `refactor: extract RunContext from RunOptions (#169)`
|
|
179
|
-
|
|
180
|
-
2. **Update comments** — update comment references in `runtime.ts` and `session-config.ts` that mention `RunOptions` field names.
|
|
181
|
-
Commit: `docs: update RunOptions field references in comments (#169)`
|
|
182
|
-
|
|
183
|
-
## Risks and Mitigations
|
|
184
|
-
|
|
185
|
-
| Risk | Mitigation |
|
|
186
|
-
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
-
| Test factories using spread patterns lose context fields silently | No test factory returns `Partial<RunOptions>` — all call sites construct the options inline, so TypeScript will reject missing `context` |
|
|
188
|
-
| `agent-manager.test.ts` assertions on execution fields break | Only context-field assertions change; execution-field assertions (`defaultMaxTurns`, `graceTurns`) remain on `runOpts.*` |
|
|
189
|
-
| Nested access adds verbosity to `runAgent()` | Only 6 access sites gain the `.context` prefix; readability trade-off is minimal for the structural clarity gained |
|
|
190
|
-
|
|
191
|
-
## Open Questions
|
|
192
|
-
|
|
193
|
-
None — the extraction follows the natural "where/who vs. how" seam identified in the issue body.
|
|
194
|
-
The issue's proposed flat `parentSessionFile`/`parentSessionId` fields have been superseded by the already-implemented `parentSession?: ParentSessionInfo` grouping from #166.
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 170
|
|
3
|
-
issue_title: "refactor(pi-subagents): reduce buildContentLines complexity (cognitive 71)"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Reduce `buildContentLines` complexity
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
`buildContentLines` in `ui/conversation-viewer.ts` has cyclomatic complexity 30 and cognitive complexity 71 — the highest in the codebase (fallow #2 refactoring target, score 9.7).
|
|
11
|
-
The method formats session events for display, handling user messages, assistant messages, tool calls, tool results, bash execution output, and a streaming indicator in a single function.
|
|
12
|
-
|
|
13
|
-
## Goals
|
|
14
|
-
|
|
15
|
-
- Extract per-content-type formatting into standalone pure functions in a new module.
|
|
16
|
-
- Reduce `buildContentLines` to a dispatch loop that delegates to formatters.
|
|
17
|
-
- Make each formatter independently testable with clear input/output.
|
|
18
|
-
|
|
19
|
-
## Non-Goals
|
|
20
|
-
|
|
21
|
-
- Changing the visual output or behavior of the conversation viewer.
|
|
22
|
-
- Refactoring `render()` or the chrome/scrolling logic.
|
|
23
|
-
- Restructuring the `ConversationViewer` class itself (constructor, options, etc.).
|
|
24
|
-
|
|
25
|
-
## Background
|
|
26
|
-
|
|
27
|
-
Issue #164 (reorganize source into domain directories) is implemented — files are already in `src/ui/`.
|
|
28
|
-
|
|
29
|
-
`buildContentLines` currently handles five concerns in a single method body:
|
|
30
|
-
|
|
31
|
-
1. **User messages** — extract text from string or content array, wrap, push with `[User]` header.
|
|
32
|
-
2. **Assistant messages** — separate text parts from tool calls, wrap text, append `[Tool: name]` lines.
|
|
33
|
-
3. **Tool results** — extract text, truncate to 500 chars, wrap in dim styling.
|
|
34
|
-
4. **Bash execution** — render command line, truncate/wrap output.
|
|
35
|
-
5. **Streaming indicator** — append activity description for running agents.
|
|
36
|
-
|
|
37
|
-
Each branch uses `this.theme` and `this.wrapText` but has no other instance dependencies.
|
|
38
|
-
The method also manages separator logic (`needsSeparator`) and applies a final `truncateToWidth` safety net.
|
|
39
|
-
|
|
40
|
-
Dependencies consumed by the formatters:
|
|
41
|
-
|
|
42
|
-
- `Theme` from `display.ts` — for `fg()` and `bold()`.
|
|
43
|
-
- `truncateToWidth` from `@earendil-works/pi-tui`.
|
|
44
|
-
- `extractText` from `session/context.ts`.
|
|
45
|
-
- `getToolCallName` and `isBashExecution` — file-local helpers in `conversation-viewer.ts`.
|
|
46
|
-
|
|
47
|
-
## Design Overview
|
|
48
|
-
|
|
49
|
-
### New module: `ui/message-formatters.ts`
|
|
50
|
-
|
|
51
|
-
A new file containing pure functions that convert a single message into display lines.
|
|
52
|
-
Each formatter receives the message, a `width`, and a narrow `FormatterContext` (theme + wrapText).
|
|
53
|
-
Each returns `string[]` — the formatted lines for that message, **excluding** separators and the final `truncateToWidth` pass (those remain in `buildContentLines`).
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
/** Narrow context shared by all message formatters. */
|
|
57
|
-
export interface FormatterContext {
|
|
58
|
-
theme: Theme;
|
|
59
|
-
wrapText: (text: string, width: number) => string[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function formatUserMessage(
|
|
63
|
-
content: string | unknown[],
|
|
64
|
-
width: number,
|
|
65
|
-
ctx: FormatterContext,
|
|
66
|
-
): string[] | null;
|
|
67
|
-
|
|
68
|
-
export function formatAssistantMessage(
|
|
69
|
-
content: Array<{ type: string; text?: string }>,
|
|
70
|
-
width: number,
|
|
71
|
-
ctx: FormatterContext,
|
|
72
|
-
): string[];
|
|
73
|
-
|
|
74
|
-
export function formatToolResult(
|
|
75
|
-
content: unknown[],
|
|
76
|
-
width: number,
|
|
77
|
-
ctx: FormatterContext,
|
|
78
|
-
): string[] | null;
|
|
79
|
-
|
|
80
|
-
export function formatBashExecution(
|
|
81
|
-
msg: BashExecutionMessage,
|
|
82
|
-
width: number,
|
|
83
|
-
ctx: FormatterContext,
|
|
84
|
-
): string[];
|
|
85
|
-
|
|
86
|
-
export function formatStreamingIndicator(
|
|
87
|
-
activeTools: ReadonlyMap<string, string>,
|
|
88
|
-
responseText: string | undefined,
|
|
89
|
-
width: number,
|
|
90
|
-
theme: Theme,
|
|
91
|
-
): string[];
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
`formatUserMessage` and `formatToolResult` return `null` when the content is empty (matching the current `continue` behavior), letting the caller skip the separator.
|
|
95
|
-
|
|
96
|
-
### Relocated helpers
|
|
97
|
-
|
|
98
|
-
`getToolCallName`, the `ToolCallContent` interface, `BashExecutionMessage`, and `isBashExecution` move to `message-formatters.ts` — they are consumed only by the formatters.
|
|
99
|
-
The type guard `isBashExecution` remains exported so `buildContentLines` can use it in the dispatch condition.
|
|
100
|
-
|
|
101
|
-
### Simplified `buildContentLines`
|
|
102
|
-
|
|
103
|
-
After extraction, `buildContentLines` becomes a ~25-line dispatch loop:
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
private buildContentLines(width: number): string[] {
|
|
107
|
-
if (width <= 0) return [];
|
|
108
|
-
const ctx = { theme: this.theme, wrapText: this.wrapText };
|
|
109
|
-
const messages = this.session.messages;
|
|
110
|
-
if (messages.length === 0) {
|
|
111
|
-
return [this.theme.fg("dim", "(waiting for first message...)")];
|
|
112
|
-
}
|
|
113
|
-
const lines: string[] = [];
|
|
114
|
-
let needsSeparator = false;
|
|
115
|
-
for (const msg of messages) {
|
|
116
|
-
const formatted = formatMessage(msg, width, ctx);
|
|
117
|
-
if (!formatted) continue;
|
|
118
|
-
if (needsSeparator) lines.push(this.theme.fg("dim", "───"));
|
|
119
|
-
lines.push(...formatted);
|
|
120
|
-
needsSeparator = true;
|
|
121
|
-
}
|
|
122
|
-
if (this.record.status === "running" && this.activity) {
|
|
123
|
-
lines.push(...formatStreamingIndicator(
|
|
124
|
-
this.activity.activeTools, this.activity.responseText, width, this.theme,
|
|
125
|
-
));
|
|
126
|
-
}
|
|
127
|
-
return lines.map(l => truncateToWidth(l, width));
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
A private `formatMessage` dispatcher selects the right formatter by `msg.role`, keeping the per-role logic in the new module.
|
|
132
|
-
The `formatMessage` function lives in `message-formatters.ts` and encapsulates the role-based dispatch:
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
export function formatMessage(
|
|
136
|
-
msg: { role: string; [key: string]: unknown },
|
|
137
|
-
width: number,
|
|
138
|
-
ctx: FormatterContext,
|
|
139
|
-
): string[] | null;
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Design principles applied
|
|
143
|
-
|
|
144
|
-
- **SRP**: Each formatter has one reason to change (its content type's display rules).
|
|
145
|
-
- **ISP**: `FormatterContext` is a 2-field interface — narrower than `ConversationViewerOptions`.
|
|
146
|
-
- **Tell-Don't-Ask**: The caller tells the formatter "format this message at this width" and receives lines back — no interrogation of the message's internals in the caller.
|
|
147
|
-
- **No output arguments**: Formatters return new arrays; they don't mutate a shared `lines` accumulator.
|
|
148
|
-
|
|
149
|
-
## Module-Level Changes
|
|
150
|
-
|
|
151
|
-
### New file: `src/ui/message-formatters.ts`
|
|
152
|
-
|
|
153
|
-
- `FormatterContext` interface.
|
|
154
|
-
- `ToolCallContent` interface (moved from `conversation-viewer.ts`).
|
|
155
|
-
- `BashExecutionMessage` interface (moved from `conversation-viewer.ts`).
|
|
156
|
-
- `getToolCallName` function (moved from `conversation-viewer.ts`).
|
|
157
|
-
- `isBashExecution` type guard (moved from `conversation-viewer.ts`).
|
|
158
|
-
- `formatUserMessage` function.
|
|
159
|
-
- `formatAssistantMessage` function.
|
|
160
|
-
- `formatToolResult` function.
|
|
161
|
-
- `formatBashExecution` function.
|
|
162
|
-
- `formatStreamingIndicator` function.
|
|
163
|
-
- `formatMessage` dispatcher function.
|
|
164
|
-
|
|
165
|
-
### Modified: `src/ui/conversation-viewer.ts`
|
|
166
|
-
|
|
167
|
-
- Remove `ToolCallContent`, `BashExecutionMessage`, `getToolCallName`, `isBashExecution` (moved to `message-formatters.ts`).
|
|
168
|
-
- Import `formatMessage`, `formatStreamingIndicator` from `message-formatters.ts`.
|
|
169
|
-
- Replace `buildContentLines` body with the dispatch loop above.
|
|
170
|
-
|
|
171
|
-
### New file: `test/message-formatters.test.ts`
|
|
172
|
-
|
|
173
|
-
- Unit tests for each formatter function and the `formatMessage` dispatcher.
|
|
174
|
-
|
|
175
|
-
### Modified: `test/conversation-viewer.test.ts`
|
|
176
|
-
|
|
177
|
-
- Existing tests remain as-is — they exercise the integrated `render()` and `buildContentLines` paths (width-safety and clamping), which are genuine integration tests for the viewer.
|
|
178
|
-
- No tests become redundant; the new unit tests cover formatter logic that was previously only reachable through the viewer's `render()` method.
|
|
179
|
-
|
|
180
|
-
## Test Impact Analysis
|
|
181
|
-
|
|
182
|
-
1. **New unit tests enabled**: Each formatter can now be tested in isolation — verifying header labels, text wrapping, truncation thresholds (500-char limit), tool-call name extraction, empty-content null returns, and streaming indicator formatting — without constructing a full `ConversationViewer` with mock `TUI`, `AgentSession`, and `AgentRecord`.
|
|
183
|
-
2. **No existing tests become redundant**: The existing `conversation-viewer.test.ts` tests are render-width-safety integration tests.
|
|
184
|
-
They exercise the full `render()` → `buildContentLines` → `truncateToWidth` pipeline and should remain.
|
|
185
|
-
3. **Existing tests stay as-is**: They test the viewer's chrome, scrolling, and width-clamping behavior — concerns orthogonal to the per-message formatting logic being extracted.
|
|
186
|
-
|
|
187
|
-
## TDD Order
|
|
188
|
-
|
|
189
|
-
1. **Red → Green**: Add unit tests for `formatUserMessage` — plain string content, content-array content, empty content returning null, header and wrapping behavior.
|
|
190
|
-
Commit: `test: add formatUserMessage unit tests`
|
|
191
|
-
|
|
192
|
-
2. **Red → Green**: Add unit tests for `formatAssistantMessage` — text-only content, tool-call-only content, mixed content, empty text parts.
|
|
193
|
-
Commit: `test: add formatAssistantMessage unit tests`
|
|
194
|
-
|
|
195
|
-
3. **Red → Green**: Add unit tests for `formatToolResult` — normal content, content exceeding 500 chars (truncation), empty content returning null.
|
|
196
|
-
Commit: `test: add formatToolResult unit tests`
|
|
197
|
-
|
|
198
|
-
4. **Red → Green**: Add unit tests for `formatBashExecution` — command rendering, output wrapping, long output truncation, empty output.
|
|
199
|
-
Commit: `test: add formatBashExecution unit tests`
|
|
200
|
-
|
|
201
|
-
5. **Red → Green**: Add unit tests for `formatStreamingIndicator` — active tools, response text fallback, no-activity "thinking" fallback.
|
|
202
|
-
Commit: `test: add formatStreamingIndicator unit tests`
|
|
203
|
-
|
|
204
|
-
6. **Red → Green**: Add unit tests for `formatMessage` dispatcher — correct delegation by role, unknown role returning null.
|
|
205
|
-
Commit: `test: add formatMessage dispatcher tests`
|
|
206
|
-
|
|
207
|
-
7. **Green → Refactor**: Create `message-formatters.ts` with all formatter functions and the dispatcher.
|
|
208
|
-
Move `ToolCallContent`, `BashExecutionMessage`, `getToolCallName`, `isBashExecution` from `conversation-viewer.ts`.
|
|
209
|
-
Commit: `refactor: extract message formatters from conversation-viewer`
|
|
210
|
-
|
|
211
|
-
8. **Green → Refactor**: Simplify `buildContentLines` to use the new `formatMessage` dispatcher and `formatStreamingIndicator`.
|
|
212
|
-
Verify all existing `conversation-viewer.test.ts` tests still pass.
|
|
213
|
-
Commit: `refactor: simplify buildContentLines to dispatch loop`
|
|
214
|
-
|
|
215
|
-
## Risks and Mitigations
|
|
216
|
-
|
|
217
|
-
| Risk | Mitigation |
|
|
218
|
-
| ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
219
|
-
| Formatter output differs subtly from inline code (missing separator, wrong styling) | Steps 7–8 keep all existing integration tests passing — any visual regression fails the width-safety suite. |
|
|
220
|
-
| `FormatterContext` grows over time as new formatters need more dependencies | The interface is deliberately minimal (2 fields); if a future formatter needs something new, it should accept it as a parameter rather than widening the context. |
|
|
221
|
-
| `msg` type is `unknown`-heavy due to Pi SDK not exporting narrow types | Preserve the existing file-local type guards and interfaces — they already handle the runtime shape safely. |
|
|
222
|
-
|
|
223
|
-
## Open Questions
|
|
224
|
-
|
|
225
|
-
- None — the extraction is mechanical and the issue's approach section is unambiguous.
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 171
|
|
3
|
-
issue_title: "refactor(pi-subagents): reduce renderResult complexity (cognitive 43)"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Reduce `renderResult` complexity
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
`renderResult` in `tools/agent-tool.ts` has cyclomatic complexity 26 and cognitive complexity 43.
|
|
11
|
-
It formats the agent result text returned to the parent LLM, branching on status (completed, error, aborted, stopped, steered) and handling fallback notes, duration, stats, and worktree info.
|
|
12
|
-
Fallow ranks this as the #1 refactoring target (score 13.4).
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Extract per-status result formatting into standalone pure functions in a new module.
|
|
17
|
-
- Extract the inline `stats()` helper as a shared, testable function.
|
|
18
|
-
- Reduce `renderResult` to a thin guard (no-details fallback) plus a dispatcher call.
|
|
19
|
-
- Make each formatter independently testable with clear input/output.
|
|
20
|
-
|
|
21
|
-
## Non-Goals
|
|
22
|
-
|
|
23
|
-
- Changing the visual output or behavior of any status rendering.
|
|
24
|
-
- Refactoring `renderCall`, `execute`, or other parts of `createAgentTool`.
|
|
25
|
-
- Changing the `AgentDetails` interface shape.
|
|
26
|
-
- Modifying the widget renderer (`widget-renderer.ts`) — it has its own rendering pipeline.
|
|
27
|
-
|
|
28
|
-
## Background
|
|
29
|
-
|
|
30
|
-
Issue #164 (reorganize source into domain directories) is implemented — files are already in `src/tools/`.
|
|
31
|
-
|
|
32
|
-
`renderResult` currently handles six concerns in a single method body:
|
|
33
|
-
|
|
34
|
-
1. **No-details guard** — when `result.details` is absent, fall back to raw text.
|
|
35
|
-
2. **Running/partial** — spinner frame + stats + activity line.
|
|
36
|
-
3. **Background** — dim "Running in background" message with agent ID.
|
|
37
|
-
4. **Completed/steered** — success/warning icon + stats + duration + optional expanded result text (first 50 lines).
|
|
38
|
-
5. **Stopped** — dim stop icon + stats + "Stopped" message.
|
|
39
|
-
6. **Error/aborted** — error icon + stats + error message or "Aborted (max turns exceeded)".
|
|
40
|
-
|
|
41
|
-
An inline `stats()` closure builds the "haiku · thinking: high · ⟳5≤30 · 3 tool uses · 33.8k tokens" string used by every branch except no-details and background.
|
|
42
|
-
|
|
43
|
-
The existing `Theme` type in `display.ts` already captures the `fg()`/`bold()` pattern the formatters need.
|
|
44
|
-
|
|
45
|
-
The sister module `widget-renderer.ts` in `ui/` demonstrates the established pattern: pure rendering functions that receive data and a theme, returning formatted strings.
|
|
46
|
-
The new module follows the same shape.
|
|
47
|
-
|
|
48
|
-
## Design Overview
|
|
49
|
-
|
|
50
|
-
### New module: `src/tools/result-renderer.ts`
|
|
51
|
-
|
|
52
|
-
A new file containing pure functions that convert an `AgentDetails` snapshot into a formatted result string for a specific status.
|
|
53
|
-
Each formatter receives `AgentDetails`, a `Theme`, and any status-specific data (result text, expanded flag).
|
|
54
|
-
Each returns a `string` — the formatted lines for that status.
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import type { AgentDetails, Theme } from "#src/ui/display";
|
|
58
|
-
|
|
59
|
-
/** Build the stats string: "haiku · thinking: high · ⟳5≤30 · 3 tool uses · 33.8k tokens". */
|
|
60
|
-
export function renderStats(details: AgentDetails, theme: Theme): string;
|
|
61
|
-
|
|
62
|
-
/** Render running/partial status: spinner + stats + activity. */
|
|
63
|
-
export function renderRunning(details: AgentDetails, theme: Theme): string;
|
|
64
|
-
|
|
65
|
-
/** Render background launch status. */
|
|
66
|
-
export function renderBackground(details: AgentDetails, theme: Theme): string;
|
|
67
|
-
|
|
68
|
-
/** Render completed or steered status with optional expanded result text. */
|
|
69
|
-
export function renderCompleted(
|
|
70
|
-
details: AgentDetails,
|
|
71
|
-
resultText: string,
|
|
72
|
-
expanded: boolean,
|
|
73
|
-
theme: Theme,
|
|
74
|
-
): string;
|
|
75
|
-
|
|
76
|
-
/** Render stopped status: dim icon + stats. */
|
|
77
|
-
export function renderStopped(details: AgentDetails, theme: Theme): string;
|
|
78
|
-
|
|
79
|
-
/** Render error or aborted status: error icon + stats + message. */
|
|
80
|
-
export function renderFailed(details: AgentDetails, theme: Theme): string;
|
|
81
|
-
|
|
82
|
-
/** Dispatch to the per-status renderer. */
|
|
83
|
-
export function renderAgentResult(
|
|
84
|
-
details: AgentDetails,
|
|
85
|
-
resultText: string,
|
|
86
|
-
expanded: boolean,
|
|
87
|
-
isPartial: boolean,
|
|
88
|
-
theme: Theme,
|
|
89
|
-
): string;
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Grouping decisions
|
|
93
|
-
|
|
94
|
-
- **Completed + steered** stay in one function — they share 90% of logic, differing only in icon color (`success` vs `warning`) and collapsed text (`"Done"` vs `"Wrapped up (turn limit)"`).
|
|
95
|
-
A discriminator inside the function is justified because both statuses represent the same structural outcome (agent finished with result).
|
|
96
|
-
- **Error + aborted** stay in one function — they share icon+stats structure, differing only in the status line.
|
|
97
|
-
Both represent failure-class outcomes.
|
|
98
|
-
|
|
99
|
-
### Simplified `renderResult`
|
|
100
|
-
|
|
101
|
-
After extraction, `renderResult` becomes a ~10-line method:
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
renderResult(result: any, { expanded, isPartial }: any, theme: any) {
|
|
105
|
-
const details = result.details as AgentDetails | undefined;
|
|
106
|
-
if (!details) {
|
|
107
|
-
const text = result.content[0]?.type === "text" ? result.content[0].text : "";
|
|
108
|
-
return new Text(text, 0, 0);
|
|
109
|
-
}
|
|
110
|
-
const resultText = result.content[0]?.type === "text" ? result.content[0].text : "";
|
|
111
|
-
return new Text(
|
|
112
|
-
renderAgentResult(details, resultText, expanded, isPartial, theme),
|
|
113
|
-
0, 0,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
The no-details guard stays inline because it needs the SDK-specific `Text` constructor and raw `result` content access — extracting it would pull SDK concerns into the pure module.
|
|
119
|
-
|
|
120
|
-
### Design principles applied
|
|
121
|
-
|
|
122
|
-
- **SRP**: Each formatter has one reason to change (its status's display rules).
|
|
123
|
-
- **ISP**: Formatters use the existing narrow `Theme` interface (2 methods) rather than the full SDK theme object.
|
|
124
|
-
- **No output arguments**: Formatters return strings; they don't mutate shared state.
|
|
125
|
-
- **Stepdown rule**: `renderAgentResult` dispatcher at the top of the module, per-status formatters below, `renderStats` helper at the bottom.
|
|
126
|
-
|
|
127
|
-
## Module-Level Changes
|
|
128
|
-
|
|
129
|
-
### New file: `src/tools/result-renderer.ts`
|
|
130
|
-
|
|
131
|
-
- `renderStats` — extracted from the inline `stats()` closure.
|
|
132
|
-
- `renderRunning` — running/partial branch.
|
|
133
|
-
- `renderBackground` — background branch.
|
|
134
|
-
- `renderCompleted` — completed/steered branch.
|
|
135
|
-
- `renderStopped` — stopped branch.
|
|
136
|
-
- `renderFailed` — error/aborted branch.
|
|
137
|
-
- `renderAgentResult` — dispatcher function.
|
|
138
|
-
|
|
139
|
-
### Modified: `src/tools/agent-tool.ts`
|
|
140
|
-
|
|
141
|
-
- Remove the inline `stats()` closure.
|
|
142
|
-
- Remove the five status branches from `renderResult`.
|
|
143
|
-
- Import `renderAgentResult` from `result-renderer.ts`.
|
|
144
|
-
- Reduce `renderResult` body to guard + dispatcher call.
|
|
145
|
-
- The `SPINNER` import is removed (moved to `result-renderer.ts`).
|
|
146
|
-
- The `formatMs`, `formatTurns` imports are removed (consumed only by the extracted code).
|
|
147
|
-
|
|
148
|
-
### New file: `test/tools/result-renderer.test.ts`
|
|
149
|
-
|
|
150
|
-
- Unit tests for `renderStats`, each per-status renderer, and the `renderAgentResult` dispatcher.
|
|
151
|
-
|
|
152
|
-
## Test Impact Analysis
|
|
153
|
-
|
|
154
|
-
1. **New unit tests enabled**: Each formatter can now be tested in isolation — verifying icon selection, stats assembly, expanded-text truncation (50-line limit), activity text, and error message rendering — without constructing a full `createAgentTool` or mocking the Pi SDK `Text` class.
|
|
155
|
-
2. **No existing tests become redundant**: The existing `agent-tool.test.ts` tests cover `execute` paths (resume, background, foreground), `renderCall`, and tool definition properties.
|
|
156
|
-
None of them test `renderResult` — there are zero existing `renderResult` tests.
|
|
157
|
-
3. **Existing tests stay as-is**: All `agent-tool.test.ts` tests exercise `execute` and tool metadata, orthogonal to the rendering extraction.
|
|
158
|
-
|
|
159
|
-
## TDD Order
|
|
160
|
-
|
|
161
|
-
1. **Red → Green**: Add unit tests for `renderStats` — model name, tags, turn count with/without max, tool uses singular/plural, tokens, empty details producing empty string.
|
|
162
|
-
Commit: `test: add renderStats unit tests`
|
|
163
|
-
|
|
164
|
-
2. **Red → Green**: Add unit tests for `renderRunning` — spinner frame, stats inclusion, activity text, default "thinking…" fallback.
|
|
165
|
-
Commit: `test: add renderRunning unit tests`
|
|
166
|
-
|
|
167
|
-
3. **Red → Green**: Add unit tests for `renderBackground` — agent ID in output, dim styling.
|
|
168
|
-
Commit: `test: add renderBackground unit tests`
|
|
169
|
-
|
|
170
|
-
4. **Red → Green**: Add unit tests for `renderCompleted` — completed icon (success), steered icon (warning), duration formatting, expanded view with result text, expanded view truncation at 50 lines, collapsed view "Done" vs "Wrapped up (turn limit)" text.
|
|
171
|
-
Commit: `test: add renderCompleted unit tests`
|
|
172
|
-
|
|
173
|
-
5. **Red → Green**: Add unit tests for `renderStopped` — dim icon, stats, "Stopped" text.
|
|
174
|
-
Commit: `test: add renderStopped unit tests`
|
|
175
|
-
|
|
176
|
-
6. **Red → Green**: Add unit tests for `renderFailed` — error status with error message, error with missing message defaulting to "unknown", aborted status with "max turns exceeded" text.
|
|
177
|
-
Commit: `test: add renderFailed unit tests`
|
|
178
|
-
|
|
179
|
-
7. **Red → Green**: Add unit tests for `renderAgentResult` dispatcher — correct delegation by status (running, background, completed, steered, stopped, error, aborted), `isPartial` triggering running renderer.
|
|
180
|
-
Commit: `test: add renderAgentResult dispatcher tests`
|
|
181
|
-
|
|
182
|
-
8. **Green → Refactor**: Create `result-renderer.ts` with all renderer functions and the dispatcher, extracted from `renderResult` in `agent-tool.ts`.
|
|
183
|
-
Commit: `refactor: extract result renderers from agent-tool`
|
|
184
|
-
|
|
185
|
-
9. **Green → Refactor**: Simplify `renderResult` in `agent-tool.ts` to the guard + dispatcher pattern.
|
|
186
|
-
Remove unused imports (`SPINNER`, `formatMs`, `formatTurns`).
|
|
187
|
-
Verify all existing `agent-tool.test.ts` tests still pass.
|
|
188
|
-
Commit: `refactor: simplify renderResult to dispatcher`
|
|
189
|
-
|
|
190
|
-
## Risks and Mitigations
|
|
191
|
-
|
|
192
|
-
| Risk | Mitigation |
|
|
193
|
-
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
194
|
-
| Formatter output differs subtly from inline code (wrong icon, missing newline) | Steps 1–7 lock in the expected output before extraction; step 9 confirms no regressions via existing tests. |
|
|
195
|
-
| `theme` parameter is `any` in the tool hook — extracted functions use `Theme` type | The `Theme` interface in `display.ts` already matches the runtime shape; existing `widget-renderer.ts` demonstrates this pattern works. |
|
|
196
|
-
| Future statuses added to `AgentDetails["status"]` need a new formatter | The dispatcher's fallback branch (currently the error/aborted block) handles unknown statuses; adding a status requires one new function + one dispatch case. |
|
|
197
|
-
|
|
198
|
-
## Open Questions
|
|
199
|
-
|
|
200
|
-
- None — the extraction is mechanical and the issue's approach section is unambiguous.
|