@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,206 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 172
|
|
3
|
-
issue_title: "refactor(pi-subagents): extract shared turn-formatting logic"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Extract shared turn-formatting logic
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
Fallow identified 18 lines of duplicated production code between `lifecycle/agent-runner.ts` and `ui/message-formatters.ts` (originally `conversation-viewer.ts` before #170 extracted formatters).
|
|
11
|
-
Both sites iterate over assistant message content items, extracting tool names and text parts for display.
|
|
12
|
-
The `ToolCallContent` interface and `getToolCallName` helper are duplicated verbatim in both files.
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Extract the duplicated `ToolCallContent` type and `getToolCallName` function into a single shared module.
|
|
17
|
-
- Extract the content-iteration pattern (collecting text parts and tool names from assistant message content) into a reusable `extractAssistantContent` function.
|
|
18
|
-
- Both consumers (`getAgentConversation` in `agent-runner.ts` and `formatAssistantMessage` in `message-formatters.ts`) import from the shared module.
|
|
19
|
-
- Eliminate the fallow production-duplication finding.
|
|
20
|
-
|
|
21
|
-
## Non-Goals
|
|
22
|
-
|
|
23
|
-
- Moving `extractText` from `session/context.ts` to the new module — same concern (content parsing) but out of scope; note as a follow-up.
|
|
24
|
-
- Refactoring `getAgentConversation` itself (it has no tests and its full-loop structure mixes user/assistant/toolResult formatting) — separate concern.
|
|
25
|
-
- Changing `buildParentContext` in `session/context.ts`, which has a similar but simpler iteration pattern (no tool calls, different data source).
|
|
26
|
-
|
|
27
|
-
## Background
|
|
28
|
-
|
|
29
|
-
### Current duplication sites
|
|
30
|
-
|
|
31
|
-
`lifecycle/agent-runner.ts` (private scope):
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
interface ToolCallContent {
|
|
35
|
-
type: "toolCall";
|
|
36
|
-
name?: string;
|
|
37
|
-
toolName?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getToolCallName(c: { type: string }): string {
|
|
41
|
-
if (c.type !== "toolCall") return "unknown";
|
|
42
|
-
const tc = c as ToolCallContent;
|
|
43
|
-
return tc.name ?? tc.toolName ?? "unknown";
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
`ui/message-formatters.ts` (exported):
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
interface ToolCallContent { /* identical */ }
|
|
51
|
-
export function getToolCallName(c: { type: string }): string { /* identical */ }
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
The content-iteration pattern appears in:
|
|
55
|
-
|
|
56
|
-
1. `getAgentConversation` (agent-runner.ts lines 480–486) — plain-text output for LLM consumption
|
|
57
|
-
2. `formatAssistantMessage` (message-formatters.ts lines 144–148) — themed display lines for TUI
|
|
58
|
-
|
|
59
|
-
Both collect `textParts: string[]` and tool names via the same `for (const c of content)` loop with identical guards.
|
|
60
|
-
|
|
61
|
-
### Structural analysis
|
|
62
|
-
|
|
63
|
-
Per the code-design skill's "structural reasons before extracting duplication" check: the two consumers differ only in *presentation* (plain text vs. themed TUI lines).
|
|
64
|
-
The *data extraction* — identifying text items and tool-call items, extracting tool names — is the same logical operation.
|
|
65
|
-
This is incidental duplication suitable for extraction.
|
|
66
|
-
|
|
67
|
-
### Dependencies
|
|
68
|
-
|
|
69
|
-
- Issue #164 (domain directory reorganization): ✓ closed — files are already in `lifecycle/` and `ui/`.
|
|
70
|
-
- Issue #170 (buildContentLines complexity reduction): ✓ closed — formatting logic is already in `message-formatters.ts`.
|
|
71
|
-
- Issue #170 is related: the extraction may simplify `formatAssistantMessage` slightly (the issue body predicted this).
|
|
72
|
-
|
|
73
|
-
### Placement
|
|
74
|
-
|
|
75
|
-
The architecture doc (Phase 10, Step 9) says: "extracts into a shared function in the session domain."
|
|
76
|
-
The `session/` directory already hosts `context.ts` which exports the related `extractText` function.
|
|
77
|
-
A new `session/content-items.ts` module keeps the concern focused without overloading `context.ts`.
|
|
78
|
-
|
|
79
|
-
## Design Overview
|
|
80
|
-
|
|
81
|
-
### New module: `session/content-items.ts`
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
/** Tool-call content item — SDK exposes this at runtime but doesn't export the type. */
|
|
85
|
-
export interface ToolCallContent {
|
|
86
|
-
type: "toolCall";
|
|
87
|
-
name?: string;
|
|
88
|
-
toolName?: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Extracts the display name from a tool-call content item. */
|
|
92
|
-
export function getToolCallName(c: { type: string }): string {
|
|
93
|
-
if (c.type !== "toolCall") return "unknown";
|
|
94
|
-
const tc = c as ToolCallContent;
|
|
95
|
-
return tc.name ?? tc.toolName ?? "unknown";
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Extracted text parts and tool names from assistant message content. */
|
|
99
|
-
export interface AssistantContentParts {
|
|
100
|
-
textParts: string[];
|
|
101
|
-
toolNames: string[];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Extract text and tool-call names from assistant message content items.
|
|
106
|
-
* Pure data extraction — consumers apply their own formatting.
|
|
107
|
-
*/
|
|
108
|
-
export function extractAssistantContent(
|
|
109
|
-
content: { type: string; [key: string]: unknown }[],
|
|
110
|
-
): AssistantContentParts {
|
|
111
|
-
const textParts: string[] = [];
|
|
112
|
-
const toolNames: string[] = [];
|
|
113
|
-
for (const c of content) {
|
|
114
|
-
if (c.type === "text" && c.text) textParts.push(c.text as string);
|
|
115
|
-
else if (c.type === "toolCall") toolNames.push(getToolCallName(c));
|
|
116
|
-
}
|
|
117
|
-
return { textParts, toolNames };
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Consumer call sites (pseudocode)
|
|
122
|
-
|
|
123
|
-
`getAgentConversation` (agent-runner.ts):
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
const { textParts, toolNames } = extractAssistantContent(msg.content);
|
|
127
|
-
if (textParts.length > 0) parts.push(`[Assistant]: ${textParts.join("\n")}`);
|
|
128
|
-
if (toolNames.length > 0) parts.push(`[Tool Calls]:\n${toolNames.map(n => ` Tool: ${n}`).join("\n")}`);
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
`formatAssistantMessage` (message-formatters.ts):
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
const { textParts, toolNames } = extractAssistantContent(content);
|
|
135
|
-
const lines: string[] = [theme.bold("[Assistant]")];
|
|
136
|
-
if (textParts.length > 0) lines.push(...wrapText(textParts.join("\n").trim(), width));
|
|
137
|
-
for (const name of toolNames) lines.push(truncateToWidth(theme.fg("muted", ` [Tool: ${name}]`), width));
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Both consumers call the same extraction, then apply their own presentation.
|
|
141
|
-
This follows Tell-Don't-Ask: the shared function returns structured data, not formatted strings.
|
|
142
|
-
|
|
143
|
-
## Module-Level Changes
|
|
144
|
-
|
|
145
|
-
### New files
|
|
146
|
-
|
|
147
|
-
| File | Description |
|
|
148
|
-
| ------------------------------------ | ---------------------------------------------------------------------------------------- |
|
|
149
|
-
| `src/session/content-items.ts` | `ToolCallContent`, `getToolCallName`, `AssistantContentParts`, `extractAssistantContent` |
|
|
150
|
-
| `test/session/content-items.test.ts` | Unit tests for `getToolCallName` and `extractAssistantContent` |
|
|
151
|
-
|
|
152
|
-
### Changed files
|
|
153
|
-
|
|
154
|
-
| File | Change |
|
|
155
|
-
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
156
|
-
| `src/lifecycle/agent-runner.ts` | Remove `ToolCallContent` and `getToolCallName`; import `extractAssistantContent` from `session/content-items`; refactor `getAgentConversation` assistant branch |
|
|
157
|
-
| `src/ui/message-formatters.ts` | Remove `ToolCallContent` and `getToolCallName`; import `extractAssistantContent` and `getToolCallName` from `session/content-items`; refactor `formatAssistantMessage` |
|
|
158
|
-
| `docs/architecture/architecture.md` | Update Step 9 status to "✓ Done"; update `session/` module listing to include `content-items.ts`; update production duplication section |
|
|
159
|
-
|
|
160
|
-
### Architecture doc updates
|
|
161
|
-
|
|
162
|
-
The architecture doc references this issue in three places:
|
|
163
|
-
|
|
164
|
-
1. Line 487 — "Production duplication" subsection: update to note the duplication is resolved.
|
|
165
|
-
2. Line 247 — `session/` module listing: add `content-items.ts`.
|
|
166
|
-
3. Line 653 — Phase 10, Step 9: mark "✓ Done".
|
|
167
|
-
|
|
168
|
-
## Test Impact Analysis
|
|
169
|
-
|
|
170
|
-
1. **New unit tests enabled**: `getToolCallName` and `extractAssistantContent` get direct unit tests for the first time.
|
|
171
|
-
Previously, `getToolCallName` was only exercised indirectly through `formatAssistantMessage` tests.
|
|
172
|
-
2. **Existing tests that stay as-is**: `message-formatters.test.ts` tests for `formatAssistantMessage` continue to exercise the full pipeline (extraction + formatting).
|
|
173
|
-
They become integration-level tests relative to the new extraction layer — they should not be simplified or removed.
|
|
174
|
-
3. **No existing tests become redundant**: `getAgentConversation` has no tests today, so nothing to deduplicate.
|
|
175
|
-
|
|
176
|
-
## TDD Order
|
|
177
|
-
|
|
178
|
-
1. `test:` Write tests for `getToolCallName` in `test/session/content-items.test.ts` — covers `toolCall` with `name`, `toolName`, both (prefers `name`), neither (returns "unknown"), and non-toolCall type.
|
|
179
|
-
Commit: `test: add getToolCallName unit tests`
|
|
180
|
-
2. `test:` Write tests for `extractAssistantContent` in the same file — covers empty array, text-only items, toolCall-only items, mixed items, and items with other types (e.g., `image`).
|
|
181
|
-
Commit: `test: add extractAssistantContent unit tests`
|
|
182
|
-
3. `feat:` Create `src/session/content-items.ts` with `ToolCallContent`, `getToolCallName`, `AssistantContentParts`, and `extractAssistantContent`.
|
|
183
|
-
All tests go green.
|
|
184
|
-
Commit: `feat: extract shared content-item parsing into session/content-items`
|
|
185
|
-
4. `refactor:` Update `message-formatters.ts` — remove local `ToolCallContent` and `getToolCallName`; import `getToolCallName` and `extractAssistantContent` from `session/content-items`; refactor `formatAssistantMessage` to use `extractAssistantContent`.
|
|
186
|
-
Existing `message-formatters.test.ts` stays green.
|
|
187
|
-
Commit: `refactor: use shared content-items in message-formatters`
|
|
188
|
-
5. `refactor:` Update `agent-runner.ts` — remove local `ToolCallContent` and `getToolCallName`; import `extractAssistantContent` from `session/content-items`; refactor `getAgentConversation` assistant branch.
|
|
189
|
-
Existing `agent-runner.test.ts` stays green.
|
|
190
|
-
Commit: `refactor: use shared content-items in agent-runner`
|
|
191
|
-
6. `docs:` Update `docs/architecture/architecture.md` — mark Step 9 done, update module listing, update duplication section.
|
|
192
|
-
Commit: `docs: mark step 9 (extract turn-formatting) done in architecture`
|
|
193
|
-
|
|
194
|
-
## Risks and Mitigations
|
|
195
|
-
|
|
196
|
-
| Risk | Mitigation |
|
|
197
|
-
| ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
198
|
-
| `getToolCallName` signature change breaking callers | Signature is identical — no change needed. Import path changes are the only difference. |
|
|
199
|
-
| `extractAssistantContent` return shape mismatch with consumers | Consumer pseudocode verified above; both sites restructure trivially around `{ textParts, toolNames }`. |
|
|
200
|
-
| `message-formatters.ts` re-exports `getToolCallName` — removing the local definition could break external imports | Grep confirms no external consumers import `getToolCallName` from `message-formatters.ts`. Drop the re-export. |
|
|
201
|
-
| `isBashExecution` in `message-formatters.ts` also uses a local type — could be confused with this extraction | `isBashExecution` and `BashExecutionMessage` are unrelated to the tool-call duplication; leave them in place. |
|
|
202
|
-
|
|
203
|
-
## Open Questions
|
|
204
|
-
|
|
205
|
-
- Should `extractText` (currently in `session/context.ts`) move to `session/content-items.ts` for consistency?
|
|
206
|
-
Deferred — it works fine where it is, and moving it means updating all importers for no functional benefit.
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 180
|
|
3
|
-
issue_title: "perf(pi-subagents): reorder append-mode system prompt to enable KV cache reuse"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Reorder append-mode system prompt for KV cache reuse
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
In append mode, `buildAgentPrompt()` places varying, agent-specific content (the `<active_agent>` tag and env block) *before* the large shared inherited system prompt (~8k tokens).
|
|
11
|
-
LLM KV caching works on prefixes — the cache is only reusable when the beginning of the prompt matches.
|
|
12
|
-
Every subagent spawn reprocesses the entire inherited prompt from scratch because the prefix differs per agent.
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Reorder the append-mode system prompt so shared/stable content comes first and varying content follows.
|
|
17
|
-
- Preserve the `<active_agent>` tag at any position — pi-permission-system's `ACTIVE_AGENT_TAG_REGEX.exec()` searches the full string.
|
|
18
|
-
- Keep replace-mode prompt ordering unchanged (it has no shared inherited content to cache).
|
|
19
|
-
- Update tests and JSDoc to reflect the new ordering.
|
|
20
|
-
|
|
21
|
-
## Non-Goals
|
|
22
|
-
|
|
23
|
-
- Changing replace-mode prompt assembly (no shared prefix to cache).
|
|
24
|
-
- Modifying pi-permission-system (its regex parsing is already position-independent).
|
|
25
|
-
- Changing the *content* of any prompt section — only reordering.
|
|
26
|
-
|
|
27
|
-
## Background
|
|
28
|
-
|
|
29
|
-
`buildAgentPrompt()` in `src/session/prompts.ts` assembles the system prompt for subagents.
|
|
30
|
-
In append mode, the current ordering is:
|
|
31
|
-
|
|
32
|
-
```text
|
|
33
|
-
1. <active_agent name="${name}"/> ← VARIES per agent
|
|
34
|
-
2. # Environment ... ← VARIES per runtime
|
|
35
|
-
3. <inherited_system_prompt> ← SHARED (~8k tokens)
|
|
36
|
-
4. <sub_agent_context> ← SHARED (static)
|
|
37
|
-
5. <agent_instructions> ← VARIES per agent
|
|
38
|
-
6. memory / skills ← VARIES
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
pi-permission-system's `getActiveAgentNameFromSystemPrompt()` in `src/active-agent.ts` uses `ACTIVE_AGENT_TAG_REGEX.exec(systemPrompt)` — a regex search that finds the tag at any position, confirmed by reading the source.
|
|
42
|
-
|
|
43
|
-
## Design Overview
|
|
44
|
-
|
|
45
|
-
Move shared/stable sections to the front of the append-mode prompt:
|
|
46
|
-
|
|
47
|
-
```text
|
|
48
|
-
1. <inherited_system_prompt> ← SHARED (~8k tokens, NOW CACHEABLE)
|
|
49
|
-
2. <sub_agent_context> ← SHARED (static)
|
|
50
|
-
3. <active_agent name="${name}"/> ← VARIES (after cached prefix)
|
|
51
|
-
4. # Environment ... ← VARIES
|
|
52
|
-
5. <agent_instructions> ← VARIES per agent
|
|
53
|
-
6. memory / skills ← VARIES
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
This is a pure reordering — no content changes.
|
|
57
|
-
The `<active_agent>` tag remains in the system prompt for pi-permission-system to find via regex.
|
|
58
|
-
The env block and agent instructions still provide context to the model; their position relative to the inherited prompt is not semantically significant.
|
|
59
|
-
|
|
60
|
-
## Module-Level Changes
|
|
61
|
-
|
|
62
|
-
### `src/session/prompts.ts`
|
|
63
|
-
|
|
64
|
-
1. Reorder the return statement in the `config.promptMode === "append"` branch to place `identity` (wrapped in `<inherited_system_prompt>`) and `bridge` before `activeAgentTag` and `envBlock`.
|
|
65
|
-
2. Update the JSDoc comment on `buildAgentPrompt()` — replace "Both modes prepend" language with a description that notes the tag is included (not necessarily prepended) in append mode.
|
|
66
|
-
|
|
67
|
-
### `test/session/prompts.test.ts`
|
|
68
|
-
|
|
69
|
-
1. Update "prepends `<active_agent>` tag in append mode" — change from asserting `prompt.startsWith()` to asserting the tag appears *after* the inherited system prompt.
|
|
70
|
-
2. Update "active_agent tag appears before envBlock in both modes" — the append-mode assertions change: the tag should still appear before the env block, but no longer at index 0.
|
|
71
|
-
The replace-mode assertions remain unchanged (`tagIdx === 0`).
|
|
72
|
-
|
|
73
|
-
## Test Impact Analysis
|
|
74
|
-
|
|
75
|
-
- Two existing tests assert `<active_agent>` is prepended (index 0) in append mode — these must change to assert the new ordering.
|
|
76
|
-
- All other prompt tests use `toContain()` and are position-independent — they pass without changes.
|
|
77
|
-
- No new test files or test surfaces are needed; the existing test suite covers the reordering adequately once the positional assertions are updated.
|
|
78
|
-
|
|
79
|
-
## TDD Order
|
|
80
|
-
|
|
81
|
-
1. **Red: update positional assertions for append mode.**
|
|
82
|
-
Change the two append-mode tests to assert the new ordering: `<inherited_system_prompt>` appears before `<active_agent>`, and the tag appears before the env block but not at index 0.
|
|
83
|
-
Commit: `test: assert cache-friendly prompt ordering in append mode (#180)`
|
|
84
|
-
|
|
85
|
-
2. **Green: reorder the append-mode return statement.**
|
|
86
|
-
Move `identity` + `<inherited_system_prompt>` wrapper and `bridge` before `activeAgentTag` + `envBlock` in the return expression.
|
|
87
|
-
Update the JSDoc on `buildAgentPrompt()`.
|
|
88
|
-
Commit: `perf: reorder append-mode prompt for KV cache reuse (#180)`
|
|
89
|
-
|
|
90
|
-
## Risks and Mitigations
|
|
91
|
-
|
|
92
|
-
| Risk | Mitigation |
|
|
93
|
-
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
-
| pi-permission-system depends on tag position | Confirmed `ACTIVE_AGENT_TAG_REGEX.exec()` searches the full string — position-independent. |
|
|
95
|
-
| Model behavior changes with reordered prompt | The same content is present; only ordering changes. The inherited system prompt as the "base" followed by specialization is arguably more natural. |
|
|
96
|
-
| Replace mode accidentally affected | Replace mode has its own code path and is not touched by this change. |
|
|
97
|
-
|
|
98
|
-
## Open Questions
|
|
99
|
-
|
|
100
|
-
None — the design is straightforward and confirmed safe by code inspection.
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 185
|
|
3
|
-
issue_title: "pi-subagents: Remove persistent agent memory feature"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Remove persistent agent memory feature
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
The `memory.ts` module and all supporting code for persistent agent memory (`MEMORY.md`, `agent-memory/` directories) should be removed from `pi-subagents`.
|
|
11
|
-
The memory feature invents its own filesystem layout, file format, and security model — all outside the stated scope of `pi-subagents`, which is agent spawning, execution, and result retrieval.
|
|
12
|
-
This follows the same scope-reduction rationale as the scheduling subsystem removal (issue #52).
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Remove the `memory.ts` module and all memory-related code from the package.
|
|
17
|
-
- Remove `MemoryScope` type and `memory` field from `AgentConfig`.
|
|
18
|
-
- Remove memory block injection from session assembly.
|
|
19
|
-
- Remove memory tool augmentation from agent-types.
|
|
20
|
-
- Remove memory parsing from custom agent loading.
|
|
21
|
-
- Remove memory display from UI components.
|
|
22
|
-
- Extract `isSymlink`, `isUnsafeName`, and `safeReadFile` to a shared utility module — `skill-loader.ts` depends on them independently of memory.
|
|
23
|
-
- Update architecture documentation to reflect the removal.
|
|
24
|
-
|
|
25
|
-
## Non-Goals
|
|
26
|
-
|
|
27
|
-
- Replacing memory with an alternative persistence mechanism — that is out of scope.
|
|
28
|
-
- Changing the `skill-loader.ts` logic beyond updating the import source for the extracted utilities.
|
|
29
|
-
|
|
30
|
-
## Background
|
|
31
|
-
|
|
32
|
-
The memory system spans six source modules and two UI modules:
|
|
33
|
-
|
|
34
|
-
| File | Memory surface |
|
|
35
|
-
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
36
|
-
| `src/session/memory.ts` | Core module: `buildMemoryBlock`, `buildReadOnlyMemoryBlock`, `resolveMemoryDir`, `ensureMemoryDir`, `readMemoryIndex`, plus shared utilities `isSymlink`, `isUnsafeName`, `safeReadFile` |
|
|
37
|
-
| `src/types.ts` | `MemoryScope` type, `memory?: MemoryScope` field on `AgentConfig` |
|
|
38
|
-
| `src/session/session-config.ts` | `AssemblerIO.buildMemoryBlock` / `buildReadOnlyMemoryBlock` fields, memory logic block (~25 lines), `MemoryScope` import |
|
|
39
|
-
| `src/config/agent-types.ts` | `getMemoryToolNames()`, `getReadOnlyMemoryToolNames()`, `MEMORY_TOOL_NAMES`, `READONLY_MEMORY_TOOL_NAMES` |
|
|
40
|
-
| `src/config/custom-agents.ts` | `parseMemory()` function, `memory: parseMemory(fm.memory)` assignment |
|
|
41
|
-
| `src/session/prompts.ts` | `memoryBlock` field on `PromptExtras`, injection into system prompt |
|
|
42
|
-
| `src/ui/agent-config-editor.ts` | `if (cfg.memory) fmFields.push(...)` |
|
|
43
|
-
| `src/ui/agent-creation-wizard.ts` | `memory:` line in frontmatter help text |
|
|
44
|
-
| `src/index.ts` | Imports `buildMemoryBlock`, `buildReadOnlyMemoryBlock`; wires them into `assemblerIO` |
|
|
45
|
-
|
|
46
|
-
The three utility functions (`isSymlink`, `isUnsafeName`, `safeReadFile`) are imported by `skill-loader.ts` for filesystem safety — they are not memory-specific and must survive the removal.
|
|
47
|
-
|
|
48
|
-
AGENTS.md constraint: the architecture doc in `docs/architecture/architecture.md` references `memory.ts` in the module listing and domain model diagram — both need updating.
|
|
49
|
-
|
|
50
|
-
## Design Overview
|
|
51
|
-
|
|
52
|
-
This is a pure removal with one extraction.
|
|
53
|
-
The only design decision is where to place the extracted utilities.
|
|
54
|
-
|
|
55
|
-
The three functions (`isSymlink`, `isUnsafeName`, `safeReadFile`) are filesystem safety primitives used by `skill-loader.ts`.
|
|
56
|
-
They belong in a new `src/session/safe-fs.ts` module in the session domain, co-located with their sole remaining consumer.
|
|
57
|
-
|
|
58
|
-
The removal works consumers-first, declaration-last:
|
|
59
|
-
|
|
60
|
-
1. Extract shared utilities to `safe-fs.ts` (additive, no behavior change).
|
|
61
|
-
2. Remove all memory consumers — session-config, prompts, agent-types, custom-agents, UI, index wiring.
|
|
62
|
-
3. Remove the declarations — `MemoryScope`, `AgentConfig.memory`, `memory.ts`.
|
|
63
|
-
4. Update architecture docs.
|
|
64
|
-
|
|
65
|
-
## Module-Level Changes
|
|
66
|
-
|
|
67
|
-
### New files
|
|
68
|
-
|
|
69
|
-
- `src/session/safe-fs.ts` — extracted `isSymlink`, `isUnsafeName`, `safeReadFile`.
|
|
70
|
-
- `test/session/safe-fs.test.ts` — tests moved from `memory.test.ts` for these three functions.
|
|
71
|
-
|
|
72
|
-
### Modified files
|
|
73
|
-
|
|
74
|
-
- `src/session/skill-loader.ts` — change import from `#src/session/memory` to `#src/session/safe-fs`.
|
|
75
|
-
- `src/session/session-config.ts` — remove `buildMemoryBlock` / `buildReadOnlyMemoryBlock` from `AssemblerIO`; remove `MemoryScope` import; remove entire memory logic block (~lines 215–242).
|
|
76
|
-
- `src/session/prompts.ts` — remove `memoryBlock` from `PromptExtras` interface; remove `extras?.memoryBlock` injection.
|
|
77
|
-
- `src/config/agent-types.ts` — remove `MEMORY_TOOL_NAMES`, `READONLY_MEMORY_TOOL_NAMES`, `getMemoryToolNames()`, `getReadOnlyMemoryToolNames()`.
|
|
78
|
-
- `src/config/custom-agents.ts` — remove `MemoryScope` import; remove `memory: parseMemory(fm.memory)` assignment; remove `parseMemory()` function.
|
|
79
|
-
- `src/types.ts` — remove `MemoryScope` type; remove `memory?: MemoryScope` from `AgentConfig`; remove associated doc comment.
|
|
80
|
-
- `src/ui/agent-config-editor.ts` — remove `if (cfg.memory)` line.
|
|
81
|
-
- `src/ui/agent-creation-wizard.ts` — remove `memory:` line from frontmatter help text.
|
|
82
|
-
- `src/index.ts` — remove `buildMemoryBlock` / `buildReadOnlyMemoryBlock` import; remove those fields from `assemblerIO` object.
|
|
83
|
-
- `docs/architecture/architecture.md` — remove `memory.ts` from module listing; remove Memory node from domain model Mermaid diagram; update session domain description.
|
|
84
|
-
|
|
85
|
-
### Deleted files
|
|
86
|
-
|
|
87
|
-
- `src/session/memory.ts` — entire module.
|
|
88
|
-
- `test/session/memory.test.ts` — entire test file (tests for `isSymlink`, `isUnsafeName`, `safeReadFile` are moved to `safe-fs.test.ts` first; remaining memory-specific tests are deleted).
|
|
89
|
-
|
|
90
|
-
### Test files modified
|
|
91
|
-
|
|
92
|
-
- `test/session/session-config.test.ts` — remove `mockBuildMemoryBlock` / `mockBuildReadOnlyMemoryBlock` mocks from `AssemblerIO` construction; remove "assembleSessionConfig — memory block selection" describe block (~lines 354–427).
|
|
93
|
-
- `test/session/prompts.test.ts` — remove "injects memory block in replace mode", "injects memory block in append mode", and "injects both memory and skills" test cases.
|
|
94
|
-
- `test/config/agent-types.test.ts` — remove `getMemoryToolNames` / `getReadOnlyMemoryToolNames` imports and test suite (~lines 26–51).
|
|
95
|
-
- `test/config/custom-agents.test.ts` — remove memory scope parsing tests (~lines 361–403).
|
|
96
|
-
|
|
97
|
-
## Test Impact Analysis
|
|
98
|
-
|
|
99
|
-
1. The extraction of `isSymlink`, `isUnsafeName`, `safeReadFile` to `safe-fs.ts` enables their tests to exist independently of the memory module — currently they are co-located with memory-specific tests in `memory.test.ts`.
|
|
100
|
-
2. The memory-specific tests in `memory.test.ts` (`resolveMemoryDir`, `ensureMemoryDir`, `readMemoryIndex`, `buildMemoryBlock`, `buildReadOnlyMemoryBlock`) become redundant and are deleted — the code they test is being removed.
|
|
101
|
-
3. The memory block selection tests in `session-config.test.ts` (~70 lines) test the memory branching logic in `assembleSessionConfig` — they are deleted because that logic is removed.
|
|
102
|
-
4. The memory injection tests in `prompts.test.ts` test `memoryBlock` injection — deleted because the field and injection code are removed.
|
|
103
|
-
5. The memory parsing tests in `custom-agents.test.ts` test `parseMemory` — deleted because the function is removed.
|
|
104
|
-
6. The memory tool name helper tests in `agent-types.test.ts` test `getMemoryToolNames` / `getReadOnlyMemoryToolNames` — deleted because the functions are removed.
|
|
105
|
-
7. All other tests remain as-is — they do not depend on memory functionality.
|
|
106
|
-
|
|
107
|
-
## TDD Order
|
|
108
|
-
|
|
109
|
-
1. **Extract utilities to `safe-fs.ts`.**
|
|
110
|
-
Create `src/session/safe-fs.ts` with `isSymlink`, `isUnsafeName`, `safeReadFile`.
|
|
111
|
-
Create `test/session/safe-fs.test.ts` with tests moved from `memory.test.ts` for these three functions.
|
|
112
|
-
Update `src/session/skill-loader.ts` import to point to `#src/session/safe-fs`.
|
|
113
|
-
Update `src/session/memory.ts` import to point to `#src/session/safe-fs` (temporary — keeps memory working until removal).
|
|
114
|
-
Verify: `pnpm vitest run` and `pnpm run check`.
|
|
115
|
-
Commit: `refactor: extract safe-fs utilities from memory module`
|
|
116
|
-
|
|
117
|
-
2. **Remove memory from session assembly and config layers.**
|
|
118
|
-
Remove `buildMemoryBlock` / `buildReadOnlyMemoryBlock` from `AssemblerIO` in `session-config.ts`.
|
|
119
|
-
Remove `MemoryScope` import and all memory logic from `session-config.ts`.
|
|
120
|
-
Remove `memoryBlock` from `PromptExtras` in `prompts.ts` and its injection logic.
|
|
121
|
-
Remove `getMemoryToolNames`, `getReadOnlyMemoryToolNames`, `MEMORY_TOOL_NAMES`, `READONLY_MEMORY_TOOL_NAMES` from `agent-types.ts`.
|
|
122
|
-
Remove `getMemoryToolNames` / `getReadOnlyMemoryToolNames` import from `session-config.ts`.
|
|
123
|
-
Remove `buildMemoryBlock` / `buildReadOnlyMemoryBlock` import and `assemblerIO` fields from `index.ts`.
|
|
124
|
-
Update `test/session/session-config.test.ts`: remove memory mocks from IO construction and memory block selection test suite.
|
|
125
|
-
Update `test/session/prompts.test.ts`: remove memory injection tests.
|
|
126
|
-
Update `test/config/agent-types.test.ts`: remove memory tool name helper tests.
|
|
127
|
-
Verify: `pnpm vitest run` and `pnpm run check`.
|
|
128
|
-
Commit: `feat!: remove memory from session assembly and config layers`
|
|
129
|
-
|
|
130
|
-
3. **Remove memory from types, custom-agents, and UI.**
|
|
131
|
-
Remove `MemoryScope` type and `memory` field from `AgentConfig` in `types.ts`.
|
|
132
|
-
Remove `parseMemory()` function, `MemoryScope` import, and `memory:` assignment from `custom-agents.ts`.
|
|
133
|
-
Remove `if (cfg.memory)` line from `agent-config-editor.ts`.
|
|
134
|
-
Remove `memory:` help text line from `agent-creation-wizard.ts`.
|
|
135
|
-
Update `test/config/custom-agents.test.ts`: remove memory parsing tests.
|
|
136
|
-
Verify: `pnpm vitest run` and `pnpm run check`.
|
|
137
|
-
Commit: `feat!: remove MemoryScope type and memory config field`
|
|
138
|
-
|
|
139
|
-
4. **Delete `memory.ts` and its test file.**
|
|
140
|
-
Delete `src/session/memory.ts`.
|
|
141
|
-
Delete `test/session/memory.test.ts`.
|
|
142
|
-
Verify: `pnpm vitest run` and `pnpm run check`.
|
|
143
|
-
Commit: `feat!: delete memory module`
|
|
144
|
-
|
|
145
|
-
5. **Update architecture documentation.**
|
|
146
|
-
Remove Memory node from the domain model Mermaid diagram.
|
|
147
|
-
Remove `memory.ts` from the module listing.
|
|
148
|
-
Update session domain description.
|
|
149
|
-
Commit: `docs: update architecture after memory removal`
|
|
150
|
-
|
|
151
|
-
## Risks and Mitigations
|
|
152
|
-
|
|
153
|
-
| Risk | Mitigation |
|
|
154
|
-
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
155
|
-
| Users with `memory:` in custom agent frontmatter — the field silently becomes a no-op. | `parseMemory` removal means unknown frontmatter keys are ignored by the markdown parser. No error, no crash — just no memory. This is acceptable for a feature removal. |
|
|
156
|
-
| `skill-loader.ts` breaks if the utility extraction has a typo or missing re-export. | Step 1 runs full test suite before proceeding. The skill-loader tests exercise `isUnsafeName` and `safeReadFile` indirectly. |
|
|
157
|
-
| Architecture doc Mermaid diagram breaks after node removal. | Verify the diagram renders correctly after editing — remove both the node and all edges referencing it. |
|
|
158
|
-
|
|
159
|
-
## Open Questions
|
|
160
|
-
|
|
161
|
-
None — the issue scope is unambiguous and all affected files have been traced.
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
issue: 188
|
|
3
|
-
issue_title: "refactor(pi-subagents): replace any casts with SDK types in extractText and SubscribableSession"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Replace `any` casts with SDK types
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
Two places in pi-subagents use `any` where proper SDK types are available and already imported in adjacent files.
|
|
11
|
-
`extractText` in `session/context.ts` uses `(c: any)` in a filter/map chain, requiring a top-level `eslint-disable` for `no-unsafe-member-access` and `no-unsafe-return`.
|
|
12
|
-
`record-observer.ts` and `ui-observer.ts` each define an identical local `SubscribableSession` interface with `(event: any) => void`, creating both a type hole and duplicated boilerplate.
|
|
13
|
-
|
|
14
|
-
## Goals
|
|
15
|
-
|
|
16
|
-
- Replace `any` casts in `extractText` with a `TextContent` type predicate.
|
|
17
|
-
- Remove the `eslint-disable` comment from `session/context.ts`.
|
|
18
|
-
- Replace `any` in the `SubscribableSession` interface with `AgentSessionEvent`.
|
|
19
|
-
- Deduplicate the `SubscribableSession` interface into a single shared definition.
|
|
20
|
-
|
|
21
|
-
## Non-Goals
|
|
22
|
-
|
|
23
|
-
- Changing the `extractText` parameter type from `unknown[]` — callers in `message-formatters.ts` pass `unknown[]`, and widening the refactoring surface is out of scope.
|
|
24
|
-
- Replacing the `SubscribableSession` interface with the full `AgentSession` class — ISP requires a narrow interface (the observers only need `subscribe`).
|
|
25
|
-
- Addressing the `eslint-disable` in `record-observer.ts` and `ui-observer.ts` for `no-unsafe-member-access` / `no-unsafe-assignment` — those are caused by the `event` property access pattern inside the callback body, not by the parameter type.
|
|
26
|
-
Once the callback parameter is typed as `AgentSessionEvent`, the unsafe-access rules should be satisfied and those `eslint-disable` comments can be removed too.
|
|
27
|
-
|
|
28
|
-
## Background
|
|
29
|
-
|
|
30
|
-
### Existing conventions
|
|
31
|
-
|
|
32
|
-
`content-items.ts` already imports `TextContent` from `@earendil-works/pi-ai` and uses `(c as TextContent).text` after a `c.type === "text"` guard.
|
|
33
|
-
`agent-runner.ts` already imports `AgentSessionEvent` from `@earendil-works/pi-coding-agent` and uses it as the parameter type in `session.subscribe((event: AgentSessionEvent) => { ... })`.
|
|
34
|
-
Both SDK types are proven to work in this package.
|
|
35
|
-
|
|
36
|
-
### `extractText` callers
|
|
37
|
-
|
|
38
|
-
`extractText(content: unknown[])` is called from:
|
|
39
|
-
|
|
40
|
-
- `session/context.ts` — `buildParentContext` passes `msg.content` from session entries.
|
|
41
|
-
- `lifecycle/agent-runner.ts` — `getLastAssistantText` and `getAgentConversation` pass `msg.content`.
|
|
42
|
-
- `ui/message-formatters.ts` — `formatUserMessage` and `formatToolResult` pass `unknown[]` content.
|
|
43
|
-
|
|
44
|
-
The parameter type stays `unknown[]` to avoid rippling through callers.
|
|
45
|
-
The type predicate narrows inside the function body.
|
|
46
|
-
|
|
47
|
-
### `SubscribableSession` consumers
|
|
48
|
-
|
|
49
|
-
Both `subscribeRecordObserver` and `subscribeUIObserver` accept a `SubscribableSession` parameter.
|
|
50
|
-
Tests use `createMockSession()` from `test/helpers/mock-session.ts`, which returns a `MockSession` with `subscribe: Mock<(fn: (event: unknown) => void) => () => void>`.
|
|
51
|
-
|
|
52
|
-
Changing `SubscribableSession.subscribe` to accept `(event: AgentSessionEvent) => void` is structurally sound: the mock's `subscribe` accepting `(fn: (event: unknown) => void)` is a supertype — a function that accepts any event can accept an `AgentSessionEvent`.
|
|
53
|
-
The TypeScript compiler allows this because of function parameter contravariance.
|
|
54
|
-
Tests construct inline event objects that match `AgentSessionEvent` member shapes, so no test changes are needed.
|
|
55
|
-
|
|
56
|
-
### Shared location for `SubscribableSession`
|
|
57
|
-
|
|
58
|
-
The interface is used by two domains (observation, UI).
|
|
59
|
-
A new shared types location is needed.
|
|
60
|
-
The existing `types.ts` at the package root contains cross-cutting types (`SubagentType`, `ThinkingLevel`, `ShellExec`).
|
|
61
|
-
`SubscribableSession` fits there — it's a narrow cross-domain interface for session event subscription.
|
|
62
|
-
|
|
63
|
-
## Design Overview
|
|
64
|
-
|
|
65
|
-
### `extractText` type predicate
|
|
66
|
-
|
|
67
|
-
Replace the `any` casts with a user-defined type guard:
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import type { TextContent } from "@earendil-works/pi-ai";
|
|
71
|
-
|
|
72
|
-
function isTextContent(c: unknown): c is TextContent {
|
|
73
|
-
return typeof c === "object" && c !== null && (c as { type: string }).type === "text";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function extractText(content: unknown[]): string {
|
|
77
|
-
return content
|
|
78
|
-
.filter(isTextContent)
|
|
79
|
-
.map((c) => c.text ?? "")
|
|
80
|
-
.join("\n");
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
The type predicate eliminates both `any` casts and the `eslint-disable` at the top of the file.
|
|
85
|
-
|
|
86
|
-
### `SubscribableSession` with `AgentSessionEvent`
|
|
87
|
-
|
|
88
|
-
Move the interface to `types.ts` and type the callback:
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
92
|
-
|
|
93
|
-
export interface SubscribableSession {
|
|
94
|
-
subscribe(fn: (event: AgentSessionEvent) => void): () => void;
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Both observer files import from `types.ts` instead of defining their own.
|
|
99
|
-
|
|
100
|
-
### Event property access in observer callbacks
|
|
101
|
-
|
|
102
|
-
Once the callback parameter is typed as `AgentSessionEvent`, TypeScript knows the event's discriminated union members.
|
|
103
|
-
The `event.type` checks narrow the union, so `event.toolName`, `event.message`, etc. become type-safe.
|
|
104
|
-
The `eslint-disable` comments for `no-unsafe-member-access` and `no-unsafe-assignment` can be removed from both observer files.
|
|
105
|
-
|
|
106
|
-
## Module-Level Changes
|
|
107
|
-
|
|
108
|
-
| File | Change |
|
|
109
|
-
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
110
|
-
| `src/session/context.ts` | Import `TextContent`; add `isTextContent` type predicate; replace `any` filter/map; remove top-level `eslint-disable` |
|
|
111
|
-
| `src/types.ts` | Add `SubscribableSession` interface with `AgentSessionEvent` callback type; add `AgentSessionEvent` import |
|
|
112
|
-
| `src/observation/record-observer.ts` | Import `SubscribableSession` from `types.ts`; remove local interface; remove top-level `eslint-disable`; remove inline `any` annotation on callback parameter |
|
|
113
|
-
| `src/ui/ui-observer.ts` | Import `SubscribableSession` from `types.ts`; remove local interface; remove top-level `eslint-disable`; remove inline `any` annotation on callback parameter |
|
|
114
|
-
|
|
115
|
-
No test file changes expected — the mock session's structural typing remains compatible.
|
|
116
|
-
|
|
117
|
-
## Test Impact Analysis
|
|
118
|
-
|
|
119
|
-
1. No new unit tests are needed — the refactoring is type-only (no behavioral change).
|
|
120
|
-
2. No existing tests become redundant.
|
|
121
|
-
3. All existing tests for `subscribeRecordObserver` and `subscribeUIObserver` must pass as-is — they verify the same event-handling behavior.
|
|
122
|
-
|
|
123
|
-
## TDD Order
|
|
124
|
-
|
|
125
|
-
This is a pure refactoring with no behavioral change.
|
|
126
|
-
Each step should pass `pnpm run check` (type-check) and `pnpm vitest run` (tests) before committing.
|
|
127
|
-
|
|
128
|
-
1. **Add `isTextContent` type predicate and remove `any` from `extractText`.**
|
|
129
|
-
Import `TextContent` from `@earendil-works/pi-ai`.
|
|
130
|
-
Add `isTextContent` predicate function.
|
|
131
|
-
Replace the `any`-cast filter/map chain with the predicate.
|
|
132
|
-
Remove the top-level `eslint-disable` comment.
|
|
133
|
-
Verify: `pnpm run check`, `pnpm vitest run`.
|
|
134
|
-
Commit: `refactor: replace any casts in extractText with TextContent type predicate (#188)`
|
|
135
|
-
|
|
136
|
-
2. **Move `SubscribableSession` to `types.ts` with `AgentSessionEvent`.**
|
|
137
|
-
Add `AgentSessionEvent` import and `SubscribableSession` interface to `src/types.ts`.
|
|
138
|
-
Update `record-observer.ts`: import from `types.ts`, remove local interface, remove `eslint-disable`, remove `any` from callback parameter.
|
|
139
|
-
Update `ui-observer.ts`: import from `types.ts`, remove local interface, remove `eslint-disable`, remove `any` from callback parameter.
|
|
140
|
-
Verify: `pnpm run check`, `pnpm vitest run`.
|
|
141
|
-
Commit: `refactor: replace any in SubscribableSession with AgentSessionEvent (#188)`
|
|
142
|
-
|
|
143
|
-
## Risks and Mitigations
|
|
144
|
-
|
|
145
|
-
1. **`AgentSessionEvent` union may not cover all event shapes accessed in observers.**
|
|
146
|
-
Mitigation: `agent-runner.ts` already uses the same type for identical event patterns (`event.type`, `event.toolName`, `event.message`).
|
|
147
|
-
The type checker will flag any property access that the union doesn't support.
|
|
148
|
-
Run `pnpm run check` after each step.
|
|
149
|
-
|
|
150
|
-
2. **Mock session type incompatibility.**
|
|
151
|
-
The mock's `subscribe` accepts `(fn: (event: unknown) => void)`.
|
|
152
|
-
A `SubscribableSession` with `(fn: (event: AgentSessionEvent) => void)` is structurally compatible via contravariance.
|
|
153
|
-
If the compiler disagrees, the mitigation is to update `MockSession.subscribe` to accept `(fn: (event: AgentSessionEvent) => void)` — a one-line change.
|
|
154
|
-
|
|
155
|
-
3. **`TextContent.text` is non-optional in the SDK type.**
|
|
156
|
-
The current code uses `c.text ?? ""` which implies `text` could be undefined.
|
|
157
|
-
`TextContent` defines `text: string` (required), so the nullish coalescing is harmless but unnecessary.
|
|
158
|
-
Keep it for safety — removing it is a separate cleanup.
|
|
159
|
-
|
|
160
|
-
## Open Questions
|
|
161
|
-
|
|
162
|
-
None — the issue's proposed approach is unambiguous and the SDK types are already validated in adjacent files.
|