@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21
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 +292 -0
- package/README.md +8 -11
- package/docs/prefix-cache-architecture.md +14 -0
- package/package.json +22 -24
- package/packages/ask-user/README.md +55 -107
- package/packages/ask-user/ask-ui.ts +306 -732
- package/packages/ask-user/clipboard.ts +50 -0
- package/packages/ask-user/config.ts +74 -78
- package/packages/ask-user/package.json +2 -2
- package/packages/ask-user/questions.ts +151 -0
- package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
- package/packages/ask-user/tools.ts +198 -432
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +38 -43
- package/packages/autocomplete/src/sorting.ts +8 -0
- package/packages/background-tasks/package.json +2 -2
- package/packages/background-tasks/src/config.ts +0 -73
- package/packages/background-tasks/src/index.ts +20 -16
- package/packages/background-tasks/src/registry.ts +1 -171
- package/packages/background-tasks/src/types.ts +0 -32
- package/packages/btw/README.md +13 -56
- package/packages/btw/extensions/btw.ts +403 -1796
- package/packages/btw/package.json +5 -2
- package/packages/btw/skills/btw/SKILL.md +7 -143
- package/packages/compactor/README.md +56 -287
- package/packages/compactor/package.json +5 -9
- package/packages/compactor/skills/compactor/SKILL.md +13 -20
- package/packages/compactor/src/card.ts +90 -0
- package/packages/compactor/src/commands/index.ts +73 -130
- package/packages/compactor/src/compaction/compact-args.ts +5 -3
- package/packages/compactor/src/compaction/filter-noise.ts +1 -1
- package/packages/compactor/src/compaction/hooks.ts +487 -311
- package/packages/compactor/src/compaction/source.ts +178 -0
- package/packages/compactor/src/compaction/summarize.ts +586 -52
- package/packages/compactor/src/config/manager.ts +130 -127
- package/packages/compactor/src/config/schema.ts +32 -48
- package/packages/compactor/src/index.ts +40 -431
- package/packages/compactor/src/info-screen.ts +44 -190
- package/packages/compactor/src/session/recall-blocks.ts +1 -1
- package/packages/compactor/src/stats.ts +76 -0
- package/packages/compactor/src/tools/context-budget.ts +17 -34
- package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
- package/packages/compactor/src/tools/register.ts +29 -269
- package/packages/compactor/src/types.ts +47 -186
- package/packages/core/README.md +25 -0
- package/packages/core/child-agent.ts +1 -0
- package/packages/core/command-echo.ts +63 -0
- package/packages/core/compaction-context.ts +41 -0
- package/packages/core/constants.ts +8 -38
- package/packages/core/events.ts +0 -53
- package/packages/core/fusion-status.ts +4 -0
- package/packages/core/global.d.ts +10 -0
- package/packages/core/harness-messages.ts +392 -0
- package/packages/core/index.ts +14 -0
- package/packages/core/kanboard-status.ts +49 -0
- package/packages/core/long-horizon-owner-status.ts +46 -0
- package/packages/core/model-cache.ts +81 -7
- package/packages/core/package.json +6 -3
- package/packages/core/src/attach/detect.ts +163 -0
- package/packages/core/src/child-agent/child-args.ts +31 -0
- package/packages/core/src/child-agent/delivery.ts +41 -0
- package/packages/core/src/child-agent/index.ts +5 -0
- package/packages/core/src/child-agent/readers.ts +203 -0
- package/packages/core/src/child-agent/runtime.ts +584 -0
- package/packages/core/src/evidence.ts +78 -0
- package/packages/core/src/hints/browser.ts +278 -0
- package/packages/core/src/hints/crab-data.ts +19 -0
- package/packages/core/src/hints/index.ts +865 -0
- package/packages/core/src/hints/lines.ts +607 -0
- package/packages/core/src/hints/store.ts +120 -0
- package/packages/core/src/jev/chatter.ts +24 -0
- package/packages/core/src/jev/settings.ts +177 -14
- package/packages/core/src/package-colors.ts +2 -0
- package/packages/core/src/settings/catalog.ts +39 -15
- package/packages/core/src/settings/engine.ts +48 -2
- package/packages/core/src/settings/hub.ts +83 -20
- package/packages/core/src/settings/open.ts +49 -0
- package/packages/core/src/settings/schema.ts +27 -7
- package/packages/core/src/tui/hub-kit.ts +18 -5
- package/packages/core/src/tui/kit.ts +198 -0
- package/packages/core/src/tui/progress.ts +73 -0
- package/packages/core/src/turn/arbiter.ts +290 -0
- package/packages/footer/README.md +4 -6
- package/packages/footer/package.json +3 -3
- package/packages/footer/src/glance-editor.ts +19 -11
- package/packages/footer/src/help.ts +1 -1
- package/packages/footer/src/index.ts +36 -3
- package/packages/footer/src/presets.ts +3 -4
- package/packages/footer/src/rendering/icons.ts +0 -20
- package/packages/footer/src/segments/compactor.ts +52 -142
- package/packages/footer/src/segments/status-ext.ts +0 -1
- package/packages/fusion/README.md +14 -4
- package/packages/fusion/package.json +4 -3
- package/packages/fusion/src/child-args.ts +1 -0
- package/packages/fusion/src/index.ts +161 -8
- package/packages/fusion/src/pi-settings.ts +57 -0
- package/packages/fusion/src/picker.ts +186 -51
- package/packages/fusion/src/preset.ts +56 -2
- package/packages/fusion/src/prompts.ts +1 -1
- package/packages/fusion/src/sidekick-runtime.ts +1 -420
- package/packages/fusion/src/tools.ts +107 -128
- package/packages/fusion/src/transcript.ts +27 -85
- package/packages/info-screen/package.json +2 -2
- package/packages/input-shortcuts/README.md +18 -30
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/burst.ts +88 -0
- package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
- package/packages/input-shortcuts/src/cleared-input.ts +97 -0
- package/packages/input-shortcuts/src/index.ts +99 -193
- package/packages/input-shortcuts/src/last-response.ts +40 -0
- package/packages/input-shortcuts/src/registers.ts +8 -23
- package/packages/input-shortcuts/src/types.ts +4 -16
- package/packages/input-shortcuts/src/undo-redo.ts +59 -47
- package/packages/kanboard/README.md +51 -47
- package/packages/kanboard/index.ts +293 -55
- package/packages/kanboard/package.json +8 -8
- package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
- package/packages/kanboard/src/badges.ts +93 -0
- package/packages/kanboard/src/bin.ts +4 -2
- package/packages/kanboard/src/commands.ts +218 -84
- package/packages/kanboard/src/debug.ts +20 -0
- package/packages/kanboard/src/guard.ts +267 -109
- package/packages/kanboard/src/monitor.ts +330 -0
- package/packages/kanboard/src/notice-buffer.ts +74 -0
- package/packages/kanboard/src/progress.ts +27 -0
- package/packages/kanboard/src/reminders.ts +234 -0
- package/packages/kanboard/src/settings.ts +69 -15
- package/packages/kanboard/src/shapes.ts +5 -0
- package/packages/long-horizon/index.ts +190 -13
- package/packages/long-horizon/package.json +3 -3
- package/packages/long-horizon/src/commands.ts +103 -7
- package/packages/long-horizon/src/compaction-brief.ts +35 -0
- package/packages/long-horizon/src/engine/continuation.ts +84 -15
- package/packages/long-horizon/src/engine/goal-state.ts +64 -13
- package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
- package/packages/long-horizon/src/engine/ralph.ts +3 -2
- package/packages/long-horizon/src/engine/runaway.ts +68 -11
- package/packages/long-horizon/src/engine/verifier.ts +19 -0
- package/packages/long-horizon/src/gate.ts +37 -12
- package/packages/long-horizon/src/judge/resolve.ts +1 -0
- package/packages/long-horizon/src/owner.ts +35 -2
- package/packages/long-horizon/src/progress.ts +87 -0
- package/packages/long-horizon/src/prompts/goal.ts +1 -1
- package/packages/long-horizon/src/runtime.ts +172 -18
- package/packages/long-horizon/src/settings.ts +54 -39
- package/packages/long-horizon/src/tools/goal.ts +6 -0
- package/packages/long-horizon/src/tools/graph.ts +1 -1
- package/packages/long-horizon/src/tools/swarm.ts +3 -3
- package/packages/mcp/package.json +2 -2
- package/packages/memory/commands.ts +7 -2
- package/packages/memory/index.ts +210 -32
- package/packages/memory/package.json +3 -3
- package/packages/memory/save-session.ts +264 -0
- package/packages/memory/settings.ts +17 -0
- package/packages/memory/tools.ts +253 -203
- package/packages/notify/package.json +2 -2
- package/packages/skill-registry/README.md +53 -16
- package/packages/skill-registry/index.ts +230 -0
- package/packages/skill-registry/package.json +19 -8
- package/packages/skill-registry/src/editor.ts +206 -0
- package/packages/skill-registry/src/judge.ts +289 -0
- package/packages/skill-registry/src/registry.ts +87 -0
- package/packages/skill-registry/src/settings.ts +259 -0
- package/packages/skill-registry/src/vault.ts +80 -0
- package/packages/subagents/README.md +39 -119
- package/packages/subagents/package.json +6 -13
- package/packages/subagents/skills/subagents/SKILL.md +18 -42
- package/packages/subagents/src/agents.ts +213 -0
- package/packages/subagents/src/cards.ts +182 -0
- package/packages/subagents/src/index.ts +609 -1084
- package/packages/subagents/src/manager.ts +429 -0
- package/packages/subagents/src/profiles.ts +130 -0
- package/packages/subagents/src/transcript.ts +154 -0
- package/packages/subagents/src/ui.ts +389 -0
- package/packages/unipi/bundled.js +39756 -45569
- package/packages/unipi/index.ts +55 -10
- package/packages/updater/package.json +2 -2
- package/packages/utility/README.md +76 -69
- package/packages/utility/package.json +2 -2
- package/packages/utility/skills/image/SKILL.md +37 -0
- package/packages/utility/skills/utility/SKILL.md +17 -60
- package/packages/utility/src/answer/extract.ts +128 -0
- package/packages/utility/src/answer/index.ts +213 -0
- package/packages/utility/src/answer/reply.ts +133 -0
- package/packages/utility/src/answer/web.ts +167 -0
- package/packages/utility/src/attach/index.ts +205 -0
- package/packages/utility/src/commands.ts +38 -142
- package/packages/utility/src/diagnostics/engine.ts +50 -5
- package/packages/utility/src/image/index.ts +33 -0
- package/packages/utility/src/image/models.ts +221 -0
- package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
- package/packages/utility/src/image/settings.ts +178 -0
- package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
- package/packages/utility/src/image/tools.ts +168 -0
- package/packages/utility/src/image/transports.ts +186 -0
- package/packages/utility/src/index.ts +68 -389
- package/packages/utility/src/lifecycle/cleanup.ts +116 -311
- package/packages/utility/src/rename/gate.ts +102 -0
- package/packages/utility/src/rename/index.ts +191 -0
- package/packages/utility/src/rename/session.ts +170 -0
- package/packages/utility/src/render/delegated.ts +285 -0
- package/packages/utility/src/render/harness.ts +683 -0
- package/packages/utility/src/render/parse.ts +156 -0
- package/packages/utility/src/render/pi-settings.ts +38 -0
- package/packages/utility/src/render/reply-bg.ts +307 -0
- package/packages/utility/src/render/simple.ts +724 -0
- package/packages/utility/src/render/spacing.ts +235 -0
- package/packages/utility/src/render/styled.ts +251 -0
- package/packages/utility/src/render/tools.ts +353 -0
- package/packages/utility/src/settings.ts +166 -233
- package/packages/utility/src/types.ts +1 -222
- package/packages/watchdog/index.ts +17 -6
- package/packages/watchdog/package.json +3 -3
- package/packages/watchdog/src/config.ts +3 -2
- package/packages/web-api/package.json +2 -2
- package/packages/workflow/README.md +3 -0
- package/packages/workflow/index.ts +0 -2
- package/packages/workflow/package.json +2 -2
- package/packages/workflow/src/permission/decide.ts +6 -4
- package/packages/workflow/src/permission/index.ts +17 -7
- package/packages/workflow/src/permission/settings.ts +39 -4
- package/packages/workflow/src/plan/index.ts +19 -4
- package/packages/background-tasks/extensions/delegate-child.ts +0 -1
- package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
- package/packages/background-tasks/src/context-token-budget.ts +0 -903
- package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
- package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
- package/packages/background-tasks/src/delegate/budget.ts +0 -415
- package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
- package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
- package/packages/background-tasks/src/delegate/launch.ts +0 -484
- package/packages/background-tasks/src/delegate/result-package.ts +0 -459
- package/packages/background-tasks/src/delegate/runner.ts +0 -430
- package/packages/background-tasks/src/delegate/seed.ts +0 -423
- package/packages/background-tasks/src/delegate/types.ts +0 -323
- package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
- package/packages/background-tasks/src/delegate-extension.ts +0 -654
- package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
- package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
- package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
- package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
- package/packages/compactor/src/compaction/build-sections.ts +0 -76
- package/packages/compactor/src/compaction/extract/goals.ts +0 -36
- package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
- package/packages/compactor/src/compaction/format.ts +0 -53
- package/packages/compactor/src/compaction/merge.ts +0 -143
- package/packages/compactor/src/config/presets.ts +0 -160
- package/packages/compactor/src/executor/executor.ts +0 -233
- package/packages/compactor/src/executor/runtime.ts +0 -115
- package/packages/compactor/src/security/evaluator.ts +0 -89
- package/packages/compactor/src/security/policy.ts +0 -96
- package/packages/compactor/src/security/scanner.ts +0 -65
- package/packages/compactor/src/session/auto-inject.ts +0 -60
- package/packages/compactor/src/session/db.ts +0 -325
- package/packages/compactor/src/session/extract.ts +0 -144
- package/packages/compactor/src/session/resume-inject.ts +0 -64
- package/packages/compactor/src/session/snapshot.ts +0 -327
- package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
- package/packages/compactor/src/tools/ctx-execute.ts +0 -23
- package/packages/compactor/src/tools/ctx-stats.ts +0 -96
- package/packages/image/README.md +0 -99
- package/packages/image/package.json +0 -61
- package/packages/image/skills/image/SKILL.md +0 -76
- package/packages/image/src/generate.ts +0 -256
- package/packages/image/src/index.ts +0 -135
- package/packages/image/src/models.ts +0 -519
- package/packages/image/src/openai-images-api.ts +0 -282
- package/packages/image/src/register-providers.ts +0 -209
- package/packages/image/src/settings.ts +0 -173
- package/packages/image/src/tools.ts +0 -326
- package/packages/input-shortcuts/src/clipboard.ts +0 -119
- package/packages/kanboard/src/runner.ts +0 -817
- package/packages/skill-registry/skills/auto/SKILL.md +0 -465
- package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
- package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
- package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
- package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
- package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
- package/packages/skill-registry/skills/debug/SKILL.md +0 -228
- package/packages/skill-registry/skills/document/SKILL.md +0 -120
- package/packages/skill-registry/skills/fix/SKILL.md +0 -212
- package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
- package/packages/skill-registry/skills/plan/SKILL.md +0 -231
- package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
- package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
- package/packages/skill-registry/skills/research/SKILL.md +0 -251
- package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
- package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
- package/packages/skill-registry/skills/work/SKILL.md +0 -218
- package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
- package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
- package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
- package/packages/subagents/agents/delegate.md +0 -14
- package/packages/subagents/agents/oracle.md +0 -78
- package/packages/subagents/agents/researcher.md +0 -52
- package/packages/subagents/agents/reviewer.md +0 -80
- package/packages/subagents/agents/scout.md +0 -50
- package/packages/subagents/agents/worker.md +0 -59
- package/packages/subagents/prompts/council.md +0 -48
- package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
- package/packages/subagents/prompts/parallel-cleanup.md +0 -59
- package/packages/subagents/prompts/parallel-research.md +0 -50
- package/packages/subagents/prompts/parallel-review.md +0 -54
- package/packages/subagents/prompts/review-loop.md +0 -43
- package/packages/subagents/skills/council-mode/SKILL.md +0 -230
- package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
- package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
- package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
- package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
- package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
- package/packages/subagents/src/acceptance.ts +0 -331
- package/packages/subagents/src/agent-manager.ts +0 -405
- package/packages/subagents/src/agent-memory.ts +0 -300
- package/packages/subagents/src/agent-overrides.ts +0 -183
- package/packages/subagents/src/agent-runner.ts +0 -271
- package/packages/subagents/src/async-runner.ts +0 -422
- package/packages/subagents/src/authority-policy.ts +0 -47
- package/packages/subagents/src/budgets.ts +0 -285
- package/packages/subagents/src/child-safety.ts +0 -140
- package/packages/subagents/src/config.ts +0 -249
- package/packages/subagents/src/conversation-viewer.ts +0 -281
- package/packages/subagents/src/core-compat.ts +0 -94
- package/packages/subagents/src/custom-agents.ts +0 -343
- package/packages/subagents/src/file-system-retry.ts +0 -50
- package/packages/subagents/src/fleet-data.ts +0 -105
- package/packages/subagents/src/fleet-view.ts +0 -322
- package/packages/subagents/src/foreground-detach.ts +0 -59
- package/packages/subagents/src/fork-context.ts +0 -285
- package/packages/subagents/src/global.d.ts +0 -7
- package/packages/subagents/src/guide.ts +0 -129
- package/packages/subagents/src/mission-state.ts +0 -133
- package/packages/subagents/src/mission-store.ts +0 -436
- package/packages/subagents/src/output-limits.ts +0 -142
- package/packages/subagents/src/parity-types.ts +0 -315
- package/packages/subagents/src/pi-args.ts +0 -167
- package/packages/subagents/src/result-files.ts +0 -232
- package/packages/subagents/src/result-watcher.ts +0 -221
- package/packages/subagents/src/retained-children.ts +0 -176
- package/packages/subagents/src/run-fanout-budget.ts +0 -356
- package/packages/subagents/src/scheduled-runs.ts +0 -349
- package/packages/subagents/src/schemas.ts +0 -263
- package/packages/subagents/src/slash-commands.ts +0 -80
- package/packages/subagents/src/supervisor-channel.ts +0 -249
- package/packages/subagents/src/tool-handler.ts +0 -1144
- package/packages/subagents/src/types.ts +0 -238
- package/packages/subagents/src/widget.ts +0 -457
- package/packages/subagents/src/workflow-script.ts +0 -737
- package/packages/subagents/src/workflow-worker.ts +0 -384
- package/packages/subagents/src/worktree.ts +0 -614
- package/packages/utility/src/analytics/collector.ts +0 -293
- package/packages/utility/src/info-screen.ts +0 -80
- package/packages/utility/src/lifecycle/process.ts +0 -162
- package/packages/utility/src/prefix-cache.ts +0 -263
- package/packages/utility/src/skill-discovery.ts +0 -542
- package/packages/utility/src/tools/env.ts +0 -113
- package/packages/utility/src/tui/name-badge-state.ts +0 -315
- package/packages/utility/src/tui/name-badge.ts +0 -109
- /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
package/packages/memory/index.ts
CHANGED
|
@@ -7,20 +7,29 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
|
+
import * as os from "node:os";
|
|
10
11
|
import { dirname, join } from "node:path";
|
|
11
12
|
import { fileURLToPath } from "node:url";
|
|
12
13
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
14
14
|
import {
|
|
15
15
|
UNIPI_EVENTS,
|
|
16
16
|
MODULES,
|
|
17
17
|
emitEvent,
|
|
18
18
|
getPackageVersion,
|
|
19
|
+
type KitTheme,
|
|
20
|
+
formatTokens,
|
|
21
|
+
harnessMetadata,
|
|
19
22
|
} from "@pi-unipi/core";
|
|
20
23
|
|
|
21
24
|
import { createSessionBackend, type SessionBackend } from "./session.js";
|
|
22
|
-
import { registerMemoryTools, MEMORY_TOOLS, memoryCard } from "./tools.js";
|
|
25
|
+
import { registerMemoryTools, MEMORY_TOOLS, memoryCard, type RailRow } from "./tools.js";
|
|
23
26
|
import { registerMemoryCommands, type SessionOverrides } from "./commands.js";
|
|
27
|
+
import {
|
|
28
|
+
SAVE_CARD_TYPE,
|
|
29
|
+
shouldRunSideSave,
|
|
30
|
+
startMemorySave,
|
|
31
|
+
type MemorySaveRun,
|
|
32
|
+
} from "./save-session.js";
|
|
24
33
|
import { readMemoryConfig, agentHooksEnabled } from "./settings.js";
|
|
25
34
|
import { replayPending, pendingCount, type PendingOp } from "./pending.js";
|
|
26
35
|
import { adoptLooseFiles, needsMigration, runConversion, readConversionState } from "./convert.js";
|
|
@@ -45,6 +54,42 @@ const RECALL_CUSTOM_TYPE = "unipi-memory-recall-reminder";
|
|
|
45
54
|
const RETRO_CUSTOM_TYPE = "unipi-memory-retro-reminder";
|
|
46
55
|
const CARD_CUSTOM_TYPE = "unipi-memory-session-card";
|
|
47
56
|
|
|
57
|
+
/** Human-readable save-pass usage line (UI only — persisted usage stays raw). */
|
|
58
|
+
export function saveUsageLine(usage: {
|
|
59
|
+
input: number;
|
|
60
|
+
cacheRead: number;
|
|
61
|
+
cacheWrite: number;
|
|
62
|
+
output: number;
|
|
63
|
+
}): string {
|
|
64
|
+
return `save pass · input ${formatTokens(usage.input)} · cache read ${formatTokens(usage.cacheRead)} / write ${formatTokens(usage.cacheWrite)} · output ${formatTokens(usage.output)} tokens`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** ~/.unipi/logs/memory.log lines when UNIPI_DEBUG_MEMORY=1 (silent otherwise). */
|
|
68
|
+
export function memoryDebug(line: string): void {
|
|
69
|
+
if (process.env.UNIPI_DEBUG_MEMORY !== "1") return;
|
|
70
|
+
try {
|
|
71
|
+
const dir = join(os.homedir(), ".unipi", "logs");
|
|
72
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
73
|
+
fs.appendFileSync(join(dir, "memory.log"), `${new Date().toISOString()} ${line}\n`);
|
|
74
|
+
} catch {
|
|
75
|
+
// best-effort
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** True when the branch segment AFTER the last compaction already carries the
|
|
80
|
+
* recall reminder — a /reload or resume must not inject it twice. */
|
|
81
|
+
export function branchHasRecallReminder(
|
|
82
|
+
branch: Array<{ type: string; customType?: string }>,
|
|
83
|
+
): boolean {
|
|
84
|
+
let lastCompact = -1;
|
|
85
|
+
branch.forEach((entry, index) => {
|
|
86
|
+
if (entry.type === "compaction") lastCompact = index;
|
|
87
|
+
});
|
|
88
|
+
return branch
|
|
89
|
+
.slice(lastCompact + 1)
|
|
90
|
+
.some((entry) => entry.type === "custom_message" && entry.customType === RECALL_CUSTOM_TYPE);
|
|
91
|
+
}
|
|
92
|
+
|
|
48
93
|
export function buildMemoryRecallReminder(input: {
|
|
49
94
|
projectName: string;
|
|
50
95
|
memories: Array<{ title: string }>;
|
|
@@ -84,6 +129,23 @@ export function buildMemoryRecallReminder(input: {
|
|
|
84
129
|
return lines.join("\n");
|
|
85
130
|
}
|
|
86
131
|
|
|
132
|
+
/** One-queued-reminder guard (exported for tests): queue() latches until the
|
|
133
|
+
* reminder lands (clear on before_agent_start). */
|
|
134
|
+
export function retroReminderGuard(): { readonly queued: boolean; queue(): void; clear(): void } {
|
|
135
|
+
let queued = false;
|
|
136
|
+
return {
|
|
137
|
+
get queued() {
|
|
138
|
+
return queued;
|
|
139
|
+
},
|
|
140
|
+
queue() {
|
|
141
|
+
queued = true;
|
|
142
|
+
},
|
|
143
|
+
clear() {
|
|
144
|
+
queued = false;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
87
149
|
export default function (pi: ExtensionAPI) {
|
|
88
150
|
let backend: SessionBackend | null = null;
|
|
89
151
|
let recallDone = false;
|
|
@@ -121,15 +183,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
121
183
|
|
|
122
184
|
// Render the recall/end reminders as small badges (UI-only).
|
|
123
185
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
return new Text(`${b} ${theme.fg?.("dim", text.replace(/[*#🧠]/gu, "").trim()) ?? text}`, 0, 0);
|
|
186
|
+
// Same rail as every memory surface: `▌ Memory recall <first line>`.
|
|
187
|
+
const reminder = (label: string) =>
|
|
188
|
+
(message: { content?: unknown }, _options: unknown, theme: KitTheme) => {
|
|
189
|
+
const text = (String(message.content ?? "").split("\n")[0] ?? "").replace(/[*#🧠]/gu, "").trim();
|
|
190
|
+
return memoryCard(theme, [`${theme.bold(label)} ${theme.fg("dim", text)}`]);
|
|
130
191
|
};
|
|
131
|
-
pi.registerMessageRenderer?.(RECALL_CUSTOM_TYPE,
|
|
132
|
-
pi.registerMessageRenderer?.(RETRO_CUSTOM_TYPE,
|
|
192
|
+
pi.registerMessageRenderer?.(RECALL_CUSTOM_TYPE, reminder("Memory recall") as never);
|
|
193
|
+
pi.registerMessageRenderer?.(RETRO_CUSTOM_TYPE, reminder("Memory save?") as never);
|
|
133
194
|
} catch { /* renderer registration is UI-dependent */ }
|
|
134
195
|
|
|
135
196
|
// Session card (UI-only entry + renderer).
|
|
@@ -142,16 +203,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
142
203
|
const cfg = readMemoryConfig();
|
|
143
204
|
const recall = cfg.recallAtStart && overrides.recall !== false ? "on" : "off";
|
|
144
205
|
const write = cfg.write && overrides.write !== false ? "on" : "off";
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
`${
|
|
148
|
-
|
|
149
|
-
...(d.
|
|
206
|
+
const t = theme as unknown as KitTheme;
|
|
207
|
+
const rows: RailRow[] = [
|
|
208
|
+
{ left: `${t.bold("Memory palace")} ${d.project}`, right: t.fg("dim", `${String(d.count)} ${d.count === 1 ? "memory" : "memories"}`) },
|
|
209
|
+
t.fg("dim", `recall ${recall} · write ${write}${d.wakeUp ? " · wake-up ready (expand)" : ""}`),
|
|
210
|
+
...(d.issue ? [t.fg("warning", d.issue)] : []),
|
|
211
|
+
...(d.needsMigrate ? [t.fg("warning", "v2 memories found — run /unipi:memory migrate")] : []),
|
|
150
212
|
];
|
|
151
213
|
if ((_options as { expanded?: boolean } | undefined)?.expanded && d.wakeUp) {
|
|
152
|
-
|
|
214
|
+
rows.push("", ...d.wakeUp.split("\n").map((l) => t.fg("muted", l)));
|
|
153
215
|
}
|
|
154
|
-
return memoryCard(
|
|
216
|
+
return memoryCard(t, rows, d.issue ? "warning" : "memory");
|
|
155
217
|
},
|
|
156
218
|
);
|
|
157
219
|
} catch { /* UI-dependent */ }
|
|
@@ -159,6 +221,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
159
221
|
pi.on("session_start", async (_event, ctx) => {
|
|
160
222
|
recallDone = false;
|
|
161
223
|
storeDone = false;
|
|
224
|
+
// Reload/resume: the reminder is already in this branch's context tail —
|
|
225
|
+
// injecting again would just repeat it.
|
|
226
|
+
try {
|
|
227
|
+
if (branchHasRecallReminder(ctx.sessionManager.getBranch())) recallDone = true;
|
|
228
|
+
} catch {
|
|
229
|
+
// branch read is best-effort
|
|
230
|
+
}
|
|
162
231
|
overrides.recall = undefined;
|
|
163
232
|
overrides.write = undefined;
|
|
164
233
|
wakeUpPromise = null;
|
|
@@ -410,6 +479,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
410
479
|
])) ?? undefined;
|
|
411
480
|
}
|
|
412
481
|
|
|
482
|
+
// Injected once per session (and once after each compaction — the
|
|
483
|
+
// session_compact / COMPACTOR_COMPACTED handlers below reset the flag).
|
|
484
|
+
recallDone = true;
|
|
413
485
|
return {
|
|
414
486
|
message: {
|
|
415
487
|
customType: RECALL_CUSTOM_TYPE,
|
|
@@ -421,36 +493,142 @@ export default function (pi: ExtensionAPI) {
|
|
|
421
493
|
wakeUp: wake,
|
|
422
494
|
}),
|
|
423
495
|
display: true,
|
|
496
|
+
details: { unipiHarness: harnessMetadata({ source: "Memory", title: "Memory recall", synopsis: "Recalled at session start" }, "before_agent_start") },
|
|
424
497
|
},
|
|
425
498
|
};
|
|
426
499
|
});
|
|
427
500
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
"
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
501
|
+
// Save pass outcome card — UI-only entry, never model context.
|
|
502
|
+
try {
|
|
503
|
+
pi.registerEntryRenderer?.<{ titles?: string[]; usage?: { input: number; cacheRead: number; cacheWrite: number; output: number } }>(
|
|
504
|
+
SAVE_CARD_TYPE,
|
|
505
|
+
(entry, _options, theme) => {
|
|
506
|
+
const d = entry.data;
|
|
507
|
+
if (!d?.titles?.length) return undefined;
|
|
508
|
+
const t = theme as unknown as KitTheme;
|
|
509
|
+
const rows: RailRow[] = [
|
|
510
|
+
{ left: `${t.bold("Memory saved")} ${d.titles.join(", ")}` },
|
|
511
|
+
];
|
|
512
|
+
if (d.usage) {
|
|
513
|
+
rows.push(t.fg("dim", saveUsageLine(d.usage)));
|
|
514
|
+
}
|
|
515
|
+
return memoryCard(t, rows);
|
|
442
516
|
},
|
|
443
|
-
{ deliverAs: "nextTurn" },
|
|
444
517
|
);
|
|
518
|
+
} catch { /* UI-dependent */ }
|
|
519
|
+
|
|
520
|
+
// Two agent runs can end before the next user prompt (e.g. a background-task
|
|
521
|
+
// completion triggers a follow-up turn) — keep at most one queued reminder.
|
|
522
|
+
const retro = retroReminderGuard();
|
|
523
|
+
|
|
524
|
+
pi.on("before_agent_start", () => {
|
|
525
|
+
retro.clear();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// Per-run activity for the side save pass: reset on agent_start, counted on
|
|
529
|
+
// tool_call. The side session runs on its own AgentSession — its calls never
|
|
530
|
+
// reach these handlers.
|
|
531
|
+
let runToolCalls = 0;
|
|
532
|
+
let runHadWrite = false;
|
|
533
|
+
let runStoredMemory = false;
|
|
534
|
+
let saveRun: MemorySaveRun | null = null;
|
|
535
|
+
|
|
536
|
+
pi.on("agent_start", () => {
|
|
537
|
+
runToolCalls = 0;
|
|
538
|
+
runHadWrite = false;
|
|
539
|
+
runStoredMemory = false;
|
|
540
|
+
});
|
|
541
|
+
pi.on("tool_call", (event) => {
|
|
542
|
+
runToolCalls += 1;
|
|
543
|
+
if (event.toolName === "edit" || event.toolName === "write") runHadWrite = true;
|
|
544
|
+
if (event.toolName === MEMORY_TOOLS.STORE || event.toolName === MEMORY_TOOLS.DELETE) {
|
|
545
|
+
runStoredMemory = true;
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
550
|
+
const cfg = readMemoryConfig(ctx.cwd);
|
|
551
|
+
const mode = cfg.saveMode;
|
|
552
|
+
|
|
553
|
+
if (mode === "reminder") {
|
|
554
|
+
// Exactly the pre-side behaviour: one nextTurn nudge per finished run.
|
|
555
|
+
if (storeDone || !recallDone) return;
|
|
556
|
+
if (retro.queued) return;
|
|
557
|
+
if (!cfg.write || overrides.write === false) return;
|
|
558
|
+
if (!agentHooksEnabled()) return;
|
|
559
|
+
if (!pi.getActiveTools().includes(MEMORY_TOOLS.STORE)) return;
|
|
560
|
+
retro.queue();
|
|
561
|
+
pi.sendMessage(
|
|
562
|
+
{
|
|
563
|
+
customType: RETRO_CUSTOM_TYPE,
|
|
564
|
+
content: [
|
|
565
|
+
"**🧠 Memory reminder:** If you learned something non-obvious in this task,",
|
|
566
|
+
"call `memory_store` to save it as a memory for future sessions.",
|
|
567
|
+
"Update existing memories instead of creating duplicates.",
|
|
568
|
+
].join(" "),
|
|
569
|
+
display: true,
|
|
570
|
+
details: { unipiHarness: harnessMetadata({ source: "Memory", title: "Memory save?", synopsis: "Save reminder after a finished run" }, "nextTurn") },
|
|
571
|
+
},
|
|
572
|
+
{ deliverAs: "nextTurn" },
|
|
573
|
+
);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (mode !== "side") return; // "off"
|
|
578
|
+
if (
|
|
579
|
+
!shouldRunSideSave({
|
|
580
|
+
write: cfg.write,
|
|
581
|
+
writeOverride: overrides.write,
|
|
582
|
+
hooksEnabled: agentHooksEnabled(),
|
|
583
|
+
storeActive: pi.getActiveTools().includes(MEMORY_TOOLS.STORE),
|
|
584
|
+
runToolCalls,
|
|
585
|
+
runHadWrite,
|
|
586
|
+
runStoredMemory,
|
|
587
|
+
running: saveRun !== null,
|
|
588
|
+
})
|
|
589
|
+
) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const run = startMemorySave(pi, ctx, () => backend);
|
|
594
|
+
saveRun = run;
|
|
595
|
+
void run.finished
|
|
596
|
+
.then((outcome) => {
|
|
597
|
+
const usage = outcome.usage;
|
|
598
|
+
memoryDebug(
|
|
599
|
+
`save: stored=[${outcome.stored.join(", ")}]` +
|
|
600
|
+
(usage ? ` usage in=${usage.input} cacheRead=${usage.cacheRead} cacheWrite=${usage.cacheWrite} out=${usage.output}` : "") +
|
|
601
|
+
(outcome.error ? ` error=${outcome.error}` : ""),
|
|
602
|
+
);
|
|
603
|
+
if (outcome.stored.length > 0) {
|
|
604
|
+
// A store happened — a later reminder-mode switch shouldn't re-nag.
|
|
605
|
+
storeDone = true;
|
|
606
|
+
try {
|
|
607
|
+
pi.appendEntry(SAVE_CARD_TYPE, { titles: outcome.stored, usage });
|
|
608
|
+
} catch {
|
|
609
|
+
// card is cosmetic
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
})
|
|
613
|
+
.catch((error) => memoryDebug(`save: ${error instanceof Error ? error.message : String(error)}`))
|
|
614
|
+
.finally(() => {
|
|
615
|
+
if (saveRun === run) saveRun = null;
|
|
616
|
+
});
|
|
445
617
|
});
|
|
446
618
|
|
|
447
619
|
pi.on("session_compact", async () => {
|
|
448
620
|
recallDone = false;
|
|
449
621
|
});
|
|
622
|
+
// Boundary compactions (compactor's percentage trigger) skip session_compact.
|
|
623
|
+
pi.events?.on?.(UNIPI_EVENTS.COMPACTOR_COMPACTED, () => {
|
|
624
|
+
recallDone = false;
|
|
625
|
+
});
|
|
450
626
|
|
|
451
627
|
pi.on("session_shutdown", async () => {
|
|
452
628
|
if (pendingTimer) { clearInterval(pendingTimer); pendingTimer = null; }
|
|
453
629
|
replayKick = () => {};
|
|
630
|
+
saveRun?.abort();
|
|
631
|
+
saveRun = null;
|
|
454
632
|
backend?.close();
|
|
455
633
|
backend = null;
|
|
456
634
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/memory",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.21",
|
|
4
4
|
"description": "Persistent cross-session memory on MemPalace (auto-installed): daemon-routed writes, shared palace with other agents, for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"README.md"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@pi-unipi/core": "3.0.0-alpha.
|
|
46
|
-
"@pi-unipi/info-screen": "3.0.0-alpha.
|
|
45
|
+
"@pi-unipi/core": "3.0.0-alpha.21",
|
|
46
|
+
"@pi-unipi/info-screen": "3.0.0-alpha.21",
|
|
47
47
|
"js-yaml": "^4.1.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unipi/memory — background save session (saveMode "side")
|
|
3
|
+
*
|
|
4
|
+
* On agent_end a one-shot AgentSession is seeded from the main session's
|
|
5
|
+
* current branch (SessionManager.inMemory — the same path /unipi:btw uses)
|
|
6
|
+
* and asked to store anything durable. Nothing reaches the main context:
|
|
7
|
+
* no sendMessage, only a UI-only appendEntry card when something was stored.
|
|
8
|
+
*
|
|
9
|
+
* Prefix-cache parity is the point: same model, same thinking level, the
|
|
10
|
+
* verbatim system prompt (ctx.getSystemPrompt(), no appends, no stripping),
|
|
11
|
+
* and the same tool list — every ACTIVE tool is re-exposed as a customTools
|
|
12
|
+
* stub carrying the identical name/description/parameters from
|
|
13
|
+
* pi.getAllTools(). Non-memory stubs refuse; the memory tools call the real
|
|
14
|
+
* executors from tools.ts.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
createAgentSession,
|
|
19
|
+
createExtensionRuntime,
|
|
20
|
+
SessionManager,
|
|
21
|
+
type AgentSession,
|
|
22
|
+
type ExtensionAPI,
|
|
23
|
+
type ExtensionContext,
|
|
24
|
+
type FileEntry,
|
|
25
|
+
type ModelRuntime,
|
|
26
|
+
type ResourceLoader,
|
|
27
|
+
type ToolDefinition,
|
|
28
|
+
} from "@earendil-works/pi-coding-agent";
|
|
29
|
+
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
30
|
+
import type { SessionBackend } from "./session.js";
|
|
31
|
+
import { MEMORY_TOOLS, memoryExecutors } from "./tools.js";
|
|
32
|
+
|
|
33
|
+
export const SAVE_CARD_TYPE = "unipi-memory-save-card";
|
|
34
|
+
export const SAVE_PROMPT =
|
|
35
|
+
"Memory save pass. Review the task that just finished in this conversation. " +
|
|
36
|
+
"If you learned something non-obvious and durable (decision, pitfall + fix, project pattern, user preference), " +
|
|
37
|
+
"call memory_search to check for an existing memory, then memory_store (update rather than duplicate). " +
|
|
38
|
+
"If nothing qualifies, reply exactly NONE. Do not call any other tool.";
|
|
39
|
+
|
|
40
|
+
const MAX_SAVE_TOOL_CALLS = 8;
|
|
41
|
+
|
|
42
|
+
export interface SaveUsage {
|
|
43
|
+
input: number;
|
|
44
|
+
cacheRead: number;
|
|
45
|
+
cacheWrite: number;
|
|
46
|
+
output: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MemorySaveOutcome {
|
|
50
|
+
/** Titles stored or updated during the pass. */
|
|
51
|
+
stored: string[];
|
|
52
|
+
/** Summed usage across the side session's assistant messages. */
|
|
53
|
+
usage?: SaveUsage;
|
|
54
|
+
error?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface MemorySaveRun {
|
|
58
|
+
abort(): void;
|
|
59
|
+
finished: Promise<MemorySaveOutcome>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Whether a finished main-session run qualifies for the background save pass. */
|
|
63
|
+
export function shouldRunSideSave(input: {
|
|
64
|
+
write: boolean;
|
|
65
|
+
writeOverride?: boolean;
|
|
66
|
+
hooksEnabled: boolean;
|
|
67
|
+
storeActive: boolean;
|
|
68
|
+
/** Tool calls in the run that just ended (all tools). */
|
|
69
|
+
runToolCalls: number;
|
|
70
|
+
/** Any edit/write in the run that just ended. */
|
|
71
|
+
runHadWrite: boolean;
|
|
72
|
+
/** The main agent stored/deleted a memory itself during the run. */
|
|
73
|
+
runStoredMemory: boolean;
|
|
74
|
+
/** A save session is already in flight. */
|
|
75
|
+
running: boolean;
|
|
76
|
+
}): boolean {
|
|
77
|
+
if (!input.write || input.writeOverride === false) return false;
|
|
78
|
+
if (!input.hooksEnabled) return false;
|
|
79
|
+
if (!input.storeActive) return false;
|
|
80
|
+
if (input.runStoredMemory) return false;
|
|
81
|
+
if (input.runToolCalls < 3 && !input.runHadWrite) return false;
|
|
82
|
+
if (input.running) return false; // one at a time — skip, never queue
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Tool names that reach the real implementations inside the save session.
|
|
87
|
+
* memory_delete stays a stub: a background pass may add knowledge, never
|
|
88
|
+
* remove it. */
|
|
89
|
+
const REAL_MEMORY_EXECUTES = new Set<string>([
|
|
90
|
+
MEMORY_TOOLS.STORE,
|
|
91
|
+
MEMORY_TOOLS.SEARCH,
|
|
92
|
+
MEMORY_TOOLS.LIST,
|
|
93
|
+
MEMORY_TOOLS.GLOBAL_SEARCH,
|
|
94
|
+
MEMORY_TOOLS.GLOBAL_LIST,
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
const STUB_REFUSAL = "Not available in the memory save session.";
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* customTools mirroring the main session's active tools: identical names,
|
|
101
|
+
* descriptions and parameter schemas so the serialized request prefix matches
|
|
102
|
+
* what the main agent just sent. Order follows pi.getAllTools() (builtins keep
|
|
103
|
+
* their positions — a same-named customTool overrides in place).
|
|
104
|
+
*/
|
|
105
|
+
export function buildSaveTools(
|
|
106
|
+
pi: Pick<ExtensionAPI, "getAllTools" | "getActiveTools">,
|
|
107
|
+
backend: () => SessionBackend | null,
|
|
108
|
+
onStored?: (title: string) => void,
|
|
109
|
+
): ToolDefinition[] {
|
|
110
|
+
const active = new Set(pi.getActiveTools());
|
|
111
|
+
const ex = memoryExecutors(pi as never, backend);
|
|
112
|
+
const real = (name: string) => {
|
|
113
|
+
switch (name) {
|
|
114
|
+
case MEMORY_TOOLS.STORE:
|
|
115
|
+
return async (_id: string, params: { title: string; content: string; tags?: string[]; type?: string }, _s: unknown, _o: unknown, ctx: { cwd: string }) => {
|
|
116
|
+
const res = await ex.store(params, ctx);
|
|
117
|
+
const action = (res.details as { action?: string } | undefined)?.action;
|
|
118
|
+
if (action === "created" || action === "updated") onStored?.(params.title);
|
|
119
|
+
return res;
|
|
120
|
+
};
|
|
121
|
+
case MEMORY_TOOLS.SEARCH:
|
|
122
|
+
return async (_id: string, params: { query: string; limit?: number; scope?: string }) => ex.search(params);
|
|
123
|
+
case MEMORY_TOOLS.GLOBAL_SEARCH:
|
|
124
|
+
return async (_id: string, params: { query: string; limit?: number }) =>
|
|
125
|
+
ex.search({ query: params.query, limit: params.limit, scope: "all" });
|
|
126
|
+
case MEMORY_TOOLS.LIST:
|
|
127
|
+
return async () => ex.list();
|
|
128
|
+
case MEMORY_TOOLS.GLOBAL_LIST:
|
|
129
|
+
return async () => ex.globalList();
|
|
130
|
+
default:
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
return pi
|
|
135
|
+
.getAllTools()
|
|
136
|
+
.filter((info) => active.has(info.name))
|
|
137
|
+
.map(
|
|
138
|
+
(info): ToolDefinition => ({
|
|
139
|
+
name: info.name,
|
|
140
|
+
label: info.name,
|
|
141
|
+
description: info.description,
|
|
142
|
+
promptGuidelines: info.promptGuidelines,
|
|
143
|
+
parameters: info.parameters,
|
|
144
|
+
execute:
|
|
145
|
+
real(info.name) ??
|
|
146
|
+
(async () => ({
|
|
147
|
+
content: [{ type: "text" as const, text: STUB_REFUSAL }],
|
|
148
|
+
details: {},
|
|
149
|
+
})),
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Header + current branch, verbatim — the seeded side session sees exactly
|
|
155
|
+
* the context the main agent just worked in (max prefix overlap). */
|
|
156
|
+
export function buildSeedEntries(ctx: {
|
|
157
|
+
sessionManager: {
|
|
158
|
+
getHeader(): { type: string } | null;
|
|
159
|
+
getBranch(): Array<{ type: string; customType?: string }>;
|
|
160
|
+
};
|
|
161
|
+
}): FileEntry[] {
|
|
162
|
+
const header = ctx.sessionManager.getHeader();
|
|
163
|
+
const branch = ctx.sessionManager.getBranch();
|
|
164
|
+
return header
|
|
165
|
+
? [header as FileEntry, ...(branch as FileEntry[])]
|
|
166
|
+
: (branch as FileEntry[]);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Identical system prompt to the main session — no append, no stripping. */
|
|
170
|
+
function createSaveResourceLoader(ctx: ExtensionContext): ResourceLoader {
|
|
171
|
+
const extensionsResult = { extensions: [], errors: [], runtime: createExtensionRuntime() };
|
|
172
|
+
const systemPrompt = ctx.getSystemPrompt();
|
|
173
|
+
return {
|
|
174
|
+
getExtensions: () => extensionsResult,
|
|
175
|
+
getSkills: () => ({ skills: [], diagnostics: [] }),
|
|
176
|
+
getPrompts: () => ({ prompts: [], diagnostics: [] }),
|
|
177
|
+
getThemes: () => ({ themes: [], diagnostics: [] }),
|
|
178
|
+
getAgentsFiles: () => ({ agentsFiles: [] }),
|
|
179
|
+
getSystemPrompt: () => systemPrompt,
|
|
180
|
+
getSystemPromptSource: () => undefined,
|
|
181
|
+
getAppendSystemPrompt: () => [],
|
|
182
|
+
getAppendSystemPromptSources: () => [],
|
|
183
|
+
extendResources: () => {},
|
|
184
|
+
reload: async () => {},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function sumUsage(session: AgentSession): SaveUsage | undefined {
|
|
189
|
+
let usage: SaveUsage | undefined;
|
|
190
|
+
for (const message of session.state.messages) {
|
|
191
|
+
if (message.role !== "assistant") continue;
|
|
192
|
+
const u = (message as AssistantMessage).usage;
|
|
193
|
+
if (!u) continue;
|
|
194
|
+
usage ??= { input: 0, cacheRead: 0, cacheWrite: 0, output: 0 };
|
|
195
|
+
usage.input += u.input;
|
|
196
|
+
usage.cacheRead += u.cacheRead;
|
|
197
|
+
usage.cacheWrite += u.cacheWrite;
|
|
198
|
+
usage.output += u.output;
|
|
199
|
+
}
|
|
200
|
+
return usage;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Start the background save pass. Caller owns gating (shouldRunSideSave) and
|
|
205
|
+
* the one-at-a-time check — this just runs.
|
|
206
|
+
*/
|
|
207
|
+
export function startMemorySave(
|
|
208
|
+
pi: ExtensionAPI,
|
|
209
|
+
ctx: ExtensionContext,
|
|
210
|
+
backend: () => SessionBackend | null,
|
|
211
|
+
): MemorySaveRun {
|
|
212
|
+
let session: AgentSession | null = null;
|
|
213
|
+
const stored: string[] = [];
|
|
214
|
+
|
|
215
|
+
const finished = (async (): Promise<MemorySaveOutcome> => {
|
|
216
|
+
const model = ctx.model;
|
|
217
|
+
if (!model) return { stored, error: "no active model" };
|
|
218
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
219
|
+
if (!auth.ok || !auth.apiKey) {
|
|
220
|
+
return { stored, error: auth.ok ? "no credentials" : (auth.error ?? "no credentials") };
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
const seeded = await createAgentSession({
|
|
224
|
+
sessionManager: SessionManager.inMemory(ctx.cwd, undefined, buildSeedEntries(ctx)),
|
|
225
|
+
model,
|
|
226
|
+
modelRuntime: (ctx.modelRegistry as unknown as { runtime: ModelRuntime }).runtime,
|
|
227
|
+
thinkingLevel: ctx.thinkingLevel ?? pi.getThinkingLevel(),
|
|
228
|
+
customTools: buildSaveTools(pi, backend, (title) => stored.push(title)),
|
|
229
|
+
tools: pi.getActiveTools(),
|
|
230
|
+
resourceLoader: createSaveResourceLoader(ctx),
|
|
231
|
+
});
|
|
232
|
+
session = seeded.session;
|
|
233
|
+
let toolCalls = 0;
|
|
234
|
+
const unsubscribe = session.subscribe((event) => {
|
|
235
|
+
if (event.type === "tool_execution_start") {
|
|
236
|
+
toolCalls += 1;
|
|
237
|
+
if (toolCalls >= MAX_SAVE_TOOL_CALLS) void session?.abort().catch(() => {});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
try {
|
|
241
|
+
await session.prompt(SAVE_PROMPT, { source: "extension" });
|
|
242
|
+
} finally {
|
|
243
|
+
unsubscribe();
|
|
244
|
+
}
|
|
245
|
+
return { stored, usage: sumUsage(session) };
|
|
246
|
+
} catch (error) {
|
|
247
|
+
return { stored, error: error instanceof Error ? error.message : String(error) };
|
|
248
|
+
} finally {
|
|
249
|
+
try {
|
|
250
|
+
session?.dispose();
|
|
251
|
+
} catch {
|
|
252
|
+
/* already disposed */
|
|
253
|
+
}
|
|
254
|
+
session = null;
|
|
255
|
+
}
|
|
256
|
+
})();
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
abort() {
|
|
260
|
+
void session?.abort().catch(() => {});
|
|
261
|
+
},
|
|
262
|
+
finished,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -18,6 +18,9 @@ export interface MemoryConfig {
|
|
|
18
18
|
write: boolean;
|
|
19
19
|
/** Include `mempalace wake-up` output in the start reminder. */
|
|
20
20
|
wakeUp: boolean;
|
|
21
|
+
/** How the end-of-task save happens: a background side session, the inline
|
|
22
|
+
* nextTurn reminder, or nothing. */
|
|
23
|
+
saveMode: "side" | "reminder" | "off";
|
|
21
24
|
/** Start the MemPalace daemon automatically when it isn't running. */
|
|
22
25
|
autoStartDaemon: boolean;
|
|
23
26
|
/** Keep the MemPalace install current via a daily PyPI check + uv upgrade. */
|
|
@@ -28,6 +31,7 @@ export const DEFAULT_MEMORY_CONFIG: MemoryConfig = {
|
|
|
28
31
|
recallAtStart: true,
|
|
29
32
|
write: true,
|
|
30
33
|
wakeUp: true,
|
|
34
|
+
saveMode: "side",
|
|
31
35
|
autoStartDaemon: false,
|
|
32
36
|
mempalaceAutoUpdate: true,
|
|
33
37
|
};
|
|
@@ -58,6 +62,19 @@ registerSettings({
|
|
|
58
62
|
label: "Wake-up summary at start",
|
|
59
63
|
description: "Include `mempalace wake-up --wing <project>` output in the start reminder.",
|
|
60
64
|
},
|
|
65
|
+
{
|
|
66
|
+
key: "saveMode",
|
|
67
|
+
type: "enum",
|
|
68
|
+
label: "Save mode",
|
|
69
|
+
description:
|
|
70
|
+
"side: a background side session reviews the finished run and stores what's worth keeping (nothing enters the conversation). " +
|
|
71
|
+
"reminder: an end-of-task note asks the agent to save. off: no save pass at all.",
|
|
72
|
+
options: [
|
|
73
|
+
{ value: "side", label: "side agent" },
|
|
74
|
+
{ value: "reminder", label: "reminder" },
|
|
75
|
+
{ value: "off", label: "off" },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
61
78
|
{
|
|
62
79
|
key: "autoStartDaemon",
|
|
63
80
|
type: "boolean",
|