volute 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activity-events-LQII6PZS.js → activity-events-TFAJBNBW.js} +2 -2
- package/dist/{ai-service-YW5SWCSB.js → ai-service-KEK2V3AP.js} +2 -2
- package/dist/{auth-KRDSM55Q.js → auth-Y27LTNZK.js} +3 -3
- package/dist/auto-upgrade-2MZF7NG3.js +46 -0
- package/dist/{avatar-image-52WGXLHV.js → avatar-image-SN576P45.js} +2 -2
- package/dist/{backup-QRNX6RK7.js → backup-GDCWH6MC.js} +2 -2
- package/dist/{channels-CKCY64TD.js → channels-AFJWGX7W.js} +15 -15
- package/dist/{chat-ZUGNL5GS.js → chat-HVPOZK3O.js} +3 -3
- package/dist/{chunk-QUSMYGNQ.js → chunk-2674KIGI.js} +1 -0
- package/dist/chunk-2GATCSZK.js +520 -0
- package/dist/{chunk-LKUGUQUW.js → chunk-3SFKY4GI.js} +4 -4
- package/dist/{chunk-CLCHOXLR.js → chunk-45QTYKQQ.js} +4 -4
- package/dist/{chunk-PPSN2MLC.js → chunk-4VAQKLMU.js} +7 -4
- package/dist/{chunk-TPX6E6PL.js → chunk-6HBGUPBA.js} +2 -2
- package/dist/{chunk-UFTDRTCP.js → chunk-7AA4T2RT.js} +2 -2
- package/dist/{chunk-56IT2ZXF.js → chunk-BP2X5AIK.js} +8 -14
- package/dist/{chunk-444RR3YF.js → chunk-CKOA4LWV.js} +11 -11
- package/dist/{chunk-HKGYZVWN.js → chunk-DUV4EMNW.js} +37 -3
- package/dist/{chunk-CV6BNGFK.js → chunk-FSXPPOPM.js} +3 -3
- package/dist/{chunk-3LHUXY3E.js → chunk-FZUPIS3T.js} +3 -3
- package/dist/{chunk-L7BKTZ5U.js → chunk-G3XAB4DK.js} +1 -1
- package/dist/{chunk-WHL6PKYP.js → chunk-HLWQ3P3R.js} +49 -45
- package/dist/{chunk-OAYY3WF3.js → chunk-HTSWDOJE.js} +0 -11
- package/dist/{chunk-OYF25B62.js → chunk-IR2FW4W7.js} +1 -1
- package/dist/{chunk-ICXY54ZC.js → chunk-IT4GEFDH.js} +1 -1
- package/dist/{chunk-F6D766VQ.js → chunk-KS62IHE2.js} +1 -1
- package/dist/{chunk-LNNI3MS4.js → chunk-MNKPAAQF.js} +9 -2
- package/dist/{chunk-LGXVGIWN.js → chunk-NCRG4SBR.js} +189 -55
- package/dist/{chunk-I7U5EEZC.js → chunk-NM5FDH4T.js} +1 -1
- package/dist/{chunk-7LT3YZUR.js → chunk-OFU6WJJZ.js} +1 -1
- package/dist/{chunk-OT5SXF7L.js → chunk-OUGD5L5U.js} +26 -12
- package/dist/{chunk-X67BDDZR.js → chunk-P3GXOT2X.js} +48 -2
- package/dist/{chunk-U54PJPTB.js → chunk-PRZUBJ4K.js} +1 -1
- package/dist/{chunk-OGPVJ53N.js → chunk-QVS3C4DK.js} +2 -2
- package/dist/{chunk-VDUCLVZI.js → chunk-TMTAHKYP.js} +27 -25
- package/dist/{chunk-RGHF3OK5.js → chunk-UEZC5MNB.js} +13 -13
- package/dist/{chunk-ATZF2RNJ.js → chunk-VTFXZPC3.js} +12 -12
- package/dist/{chunk-YSRYCS3N.js → chunk-W3OIPJPB.js} +1 -1
- package/dist/{chunk-5Y54AE3L.js → chunk-XAG53RWY.js} +1 -1
- package/dist/{chunk-HTRSSOHY.js → chunk-YU5EYQAE.js} +1 -1
- package/dist/{chunk-EGV6R6UF.js → chunk-Z7TNIIHW.js} +3 -1
- package/dist/cli.js +8 -8
- package/dist/{cloud-sync-52YNJRMR.js → cloud-sync-FM73SVFJ.js} +20 -20
- package/dist/{conversations-ZL6PHROD.js → conversations-FH3ODBL7.js} +3 -3
- package/dist/{create-G2RIKQYK.js → create-P75ZFTVN.js} +1 -1
- package/dist/{daemon-restart-QWHN5VMU.js → daemon-restart-JQMMMITM.js} +1 -1
- package/dist/daemon.js +187 -159
- package/dist/{db-I4EH5SLF.js → db-JH3NTERX.js} +17 -3
- package/dist/{delivery-manager-7R2YIQ2W.js → delivery-manager-H23Q23N7.js} +15 -15
- package/dist/{delivery-notices-RZQ6GONP.js → delivery-notices-S7TKOACE.js} +5 -5
- package/dist/{delivery-router-QS4NLJR6.js → delivery-router-KHBVEVVL.js} +2 -2
- package/dist/echo-text-ULEIFJJF.js +40 -0
- package/dist/{extensions-QT65JGV6.js → extensions-QD22GXZD.js} +24 -20
- package/dist/{files-PCKBQHAY.js → files-DUGRXN6V.js} +2 -2
- package/dist/{import-DB2KULRO.js → import-RDDUGGFI.js} +3 -3
- package/dist/message-delivery-LU4EP244.js +46 -0
- package/dist/migrate-init-infrastructure-Z72UJ5NT.js +44 -0
- package/dist/{migrate-name-placeholder-C7NCNLWF.js → migrate-name-placeholder-64AEAWPE.js} +1 -1
- package/dist/{migrate-thread-config-EQSEP2XG.js → migrate-thread-config-SVF2M3D3.js} +1 -1
- package/dist/{mind-CT6ANXK4.js → mind-BARCLI7O.js} +4 -4
- package/dist/{mind-activity-tracker-QF6DCX2X.js → mind-activity-tracker-UT23RGBF.js} +3 -3
- package/dist/mind-service-ZJCUBAUL.js +48 -0
- package/dist/{package-F6S5NHY2.js → package-AUDBDKAO.js} +10 -12
- package/dist/{prompts-JECJIFPQ.js → prompts-MOBF3TMO.js} +1 -1
- package/dist/{sandbox-NNWCTHRI.js → sandbox-VI7XWBIA.js} +2 -2
- package/dist/scheduler-GLINVC2B.js +27 -0
- package/dist/{seed-DRKAP3XV.js → seed-5JGR7ZRA.js} +1 -1
- package/dist/{seed-cmd-6IMTO3LG.js → seed-cmd-M6A7N7F3.js} +2 -2
- package/dist/{seed-create-VNLDYRFK.js → seed-create-TGFZW4IO.js} +3 -3
- package/dist/{seed-readiness-H5TTPAQH.js → seed-readiness-XBH2MUNA.js} +2 -2
- package/dist/{seed-sprout-PPGYGK4G.js → seed-sprout-3IC5ZVTI.js} +7 -7
- package/dist/{send-UXUQKO4N.js → send-DHYBAYK4.js} +2 -2
- package/dist/{setup-YUAQMDVZ.js → setup-6KAH3GRW.js} +1 -1
- package/dist/skills/volute-mind/SKILL.md +8 -0
- package/dist/{skills-GMQ5UIIW.js → skills-LKQCYFVP.js} +2 -2
- package/dist/sleep-manager-Q3ITFNOJ.js +42 -0
- package/dist/{spirit-Q5VGFWUI.js → spirit-FKXMGO3N.js} +5 -5
- package/dist/spirit-availability-5R2WYSWF.js +43 -0
- package/dist/{sprout-PMTRL7QN.js → sprout-VEW3CUNX.js} +1 -1
- package/dist/{src-H3UCOP6B.js → src-246GSX7A.js} +5 -2
- package/dist/src-LOUB3WPR.js +4189 -0
- package/dist/{system-events-Q7IVZ5T4.js → system-events-ZYOCYAO2.js} +5 -3
- package/dist/{tailscale-KHNZFC7L.js → tailscale-F2IMBILZ.js} +1 -1
- package/dist/{template-hash-YKWMVYMM.js → template-hash-RJRMF6DT.js} +2 -2
- package/dist/{template-staleness-PY2CULH6.js → template-staleness-IBD722PN.js} +4 -4
- package/dist/{turn-tracker-HZDPMSCC.js → turn-tracker-BE34OI6O.js} +7 -7
- package/dist/{up-XZDSJVMU.js → up-J47J5JRB.js} +1 -1
- package/dist/variant-cleanup-JGJZ5KB4.js +24 -0
- package/dist/{version-notify-VNVFX6UP.js → version-notify-KIXYNHJC.js} +11 -11
- package/dist/web-assets/assets/{index-CwtxGwe_.js → index-CoU1eUKE.js} +15 -15
- package/dist/web-assets/assets/index-hPn_fkz3.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +10 -12
- package/packages/extensions/intentions/dist/ui/assets/index-Bl5KEcli.js +67 -0
- package/packages/extensions/intentions/dist/ui/index.html +1 -1
- package/packages/extensions/pages/dist/ui/assets/index-BKxVQkA9.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-BMt6sStP.js +68 -0
- package/packages/extensions/pages/dist/ui/index.html +2 -2
- package/packages/extensions/pages/skills/pages/SKILL.md +123 -4
- package/templates/_base/.init/.config/prompts.json +0 -1
- package/templates/_base/.init/.local/hooks/pre-prompt/turn-context.ts +30 -0
- package/templates/_base/src/lib/codex-session-seed.ts +6 -55
- package/templates/_base/src/lib/pi-session-seed.ts +8 -57
- package/templates/_base/src/lib/seed-note.ts +4 -4
- package/templates/_base/src/lib/session-seed.ts +8 -53
- package/templates/_base/src/lib/startup.ts +0 -20
- package/templates/claude/package.json +2 -2
- package/templates/claude/src/agent.ts +34 -102
- package/templates/claude/src/lib/hooks/pre-compact.ts +6 -6
- package/templates/claude/src/server.ts +0 -1
- package/templates/codex/package.json +2 -2
- package/templates/codex/src/agent.ts +23 -89
- package/templates/pi/package.json +1 -1
- package/templates/pi/src/agent.ts +26 -93
- package/templates/pi/src/server.ts +0 -1
- package/dist/auto-upgrade-HEVZKPTF.js +0 -46
- package/dist/chunk-AAPNU7GA.js +0 -168
- package/dist/echo-text-73CYQRL7.js +0 -40
- package/dist/message-delivery-TRKT4T2P.js +0 -46
- package/dist/mind-service-7EFIJ3ZA.js +0 -48
- package/dist/scheduler-73Q4MQ7Y.js +0 -27
- package/dist/sleep-manager-KHI65FEX.js +0 -42
- package/dist/spirit-availability-G5FMWKRH.js +0 -43
- package/dist/src-3FIW7DLH.js +0 -2376
- package/dist/src-LTTQVN2Z.js +0 -832
- package/dist/variant-cleanup-QHGGY7ND.js +0 -24
- package/dist/web-assets/assets/index-DhM1yYRN.css +0 -1
- package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +0 -67
- package/packages/extensions/notes/dist/ui/assets/index-BBs8pyel.js +0 -67
- package/packages/extensions/notes/dist/ui/assets/index-D3cdui42.css +0 -1
- package/packages/extensions/notes/dist/ui/index.html +0 -14
- package/packages/extensions/notes/skills/notes/SKILL.md +0 -90
- package/packages/extensions/pages/dist/ui/assets/index-B589e8Xe.js +0 -2
- package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +0 -1
- /package/dist/{chunk-ZNHL5KQJ.js → chunk-OKM67ZWH.js} +0 -0
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/tokenizer": "^0.0.4",
|
|
13
|
-
"@openai/codex-sdk": "^0.
|
|
13
|
+
"@openai/codex-sdk": "^0.145.0",
|
|
14
14
|
"tsx": "^4.23.1"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@biomejs/biome": "2.5.
|
|
17
|
+
"@biomejs/biome": "2.5.5",
|
|
18
18
|
"@types/node": "^26.1.1",
|
|
19
19
|
"typescript": "^6.0.3"
|
|
20
20
|
}
|
|
@@ -3,8 +3,6 @@ import { resolve as resolvePath } from "node:path";
|
|
|
3
3
|
import { Codex } from "@openai/codex-sdk";
|
|
4
4
|
import { flushFileChanges, trackFileChange } from "./lib/auto-commit.js";
|
|
5
5
|
import {
|
|
6
|
-
type CodexSeedCut,
|
|
7
|
-
computeCodexSeedCut,
|
|
8
6
|
DEFAULT_SEED_TOKENS,
|
|
9
7
|
rotateCodexSession,
|
|
10
8
|
seedCodexSession,
|
|
@@ -21,17 +19,11 @@ import {
|
|
|
21
19
|
readSkillDescriptions,
|
|
22
20
|
} from "./lib/context-breakdown.js";
|
|
23
21
|
import { daemonEmit, daemonRestart, type EventType } from "./lib/daemon-client.js";
|
|
24
|
-
import { compactTimestamp } from "./lib/format-prefix.js";
|
|
25
22
|
import { runHooks } from "./lib/hook-loader.js";
|
|
26
23
|
import { log, warn } from "./lib/logger.js";
|
|
27
24
|
import { buildSeededNote, type SeedCause } from "./lib/seed-note.js";
|
|
28
25
|
import { createSessionStore } from "./lib/session-store.js";
|
|
29
|
-
import {
|
|
30
|
-
getStartupContext,
|
|
31
|
-
loadPrompts,
|
|
32
|
-
loadSystemPrompt,
|
|
33
|
-
renderCompactionWarning,
|
|
34
|
-
} from "./lib/startup.js";
|
|
26
|
+
import { getStartupContext, loadPrompts, loadSystemPrompt } from "./lib/startup.js";
|
|
35
27
|
import { filterEvent, loadTransparencyPreset } from "./lib/transparency.js";
|
|
36
28
|
import { turnContextFor } from "./lib/turn-context.js";
|
|
37
29
|
import type {
|
|
@@ -52,7 +44,7 @@ type CodexThread = {
|
|
|
52
44
|
): Promise<{ events: AsyncIterable<Record<string, any>> }>;
|
|
53
45
|
};
|
|
54
46
|
|
|
55
|
-
type QueuedMessage = { text: string; meta: HandlerMeta
|
|
47
|
+
type QueuedMessage = { text: string; meta: HandlerMeta };
|
|
56
48
|
|
|
57
49
|
type CodexSession = {
|
|
58
50
|
name: string;
|
|
@@ -81,14 +73,6 @@ type CodexSession = {
|
|
|
81
73
|
currentThreadId: string | null;
|
|
82
74
|
/** Why the tail is seeded — picks the boundary note's wording. Last cause wins. */
|
|
83
75
|
seededCause: SeedCause;
|
|
84
|
-
/**
|
|
85
|
-
* The cut point pinned at warn time (the first-kept turn the mind was told would
|
|
86
|
-
* survive verbatim). Honored at rotation so the promise in the warning holds even
|
|
87
|
-
* though the rollout grows after the warning. Null until a warning fires.
|
|
88
|
-
*/
|
|
89
|
-
rotationCut: CodexSeedCut | null;
|
|
90
|
-
/** A rotation warning turn is queued/in-flight — suppress re-triggering until it rotates. */
|
|
91
|
-
rotatePending: boolean;
|
|
92
76
|
/**
|
|
93
77
|
* Back-to-back rotations that did NOT bring context under the threshold (reset by any
|
|
94
78
|
* healthy turn). Guards against a runaway loop when the tail alone can't fit — e.g. a
|
|
@@ -139,12 +123,6 @@ export function createMind(options: {
|
|
|
139
123
|
const maxContextTokens = options.maxContextTokens;
|
|
140
124
|
const seedTokens = options.seedTokens ?? DEFAULT_SEED_TOKENS;
|
|
141
125
|
|
|
142
|
-
// The warning the mind gets when the context limit approaches: it authors summaries
|
|
143
|
-
// of the turns that will collapse, then the session rotates onto the verbatim tail.
|
|
144
|
-
// ${date}/${memory_size} are filled per-warning (the date rolls over and MEMORY.md
|
|
145
|
-
// changes as the mind edits it); ${cutoff} is filled with the pinned boundary.
|
|
146
|
-
const compactionMessage = () => renderCompactionWarning(prompts.compaction_warning);
|
|
147
|
-
|
|
148
126
|
if (maxContextTokens) {
|
|
149
127
|
log("mind", `compaction threshold: ${maxContextTokens} tokens`);
|
|
150
128
|
}
|
|
@@ -173,11 +151,11 @@ export function createMind(options: {
|
|
|
173
151
|
...(apiKey ? { apiKey } : {}),
|
|
174
152
|
config: {
|
|
175
153
|
model_instructions_file: promptPath,
|
|
176
|
-
// Rotation
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
154
|
+
// Rotation is the primary path: at maxContextTokens we silently rotate onto the
|
|
155
|
+
// verbatim tail between turns. The SDK's native auto-compaction is only an emergency
|
|
156
|
+
// backstop — set above the volute threshold (1.5×) so it fires solely when rotation
|
|
157
|
+
// can't relieve context (e.g. a system prompt too large to fit the tail under the
|
|
158
|
+
// threshold), after the runaway guard stops self-rotating.
|
|
181
159
|
model_auto_compact_token_limit: maxContextTokens
|
|
182
160
|
? Math.floor(maxContextTokens * 1.5)
|
|
183
161
|
: 999999999,
|
|
@@ -217,8 +195,6 @@ export function createMind(options: {
|
|
|
217
195
|
seededArchivedAt: null,
|
|
218
196
|
currentThreadId: null,
|
|
219
197
|
seededCause: "restored",
|
|
220
|
-
rotationCut: null,
|
|
221
|
-
rotatePending: false,
|
|
222
198
|
consecutiveRotations: 0,
|
|
223
199
|
};
|
|
224
200
|
sessions.set(name, session);
|
|
@@ -642,62 +618,22 @@ export function createMind(options: {
|
|
|
642
618
|
session.currentMessageId = undefined;
|
|
643
619
|
}
|
|
644
620
|
|
|
645
|
-
// --- Rotation (
|
|
621
|
+
// --- Rotation (silent, at turn end) ---
|
|
646
622
|
|
|
647
623
|
/**
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
652
|
-
* placeholder, and queues the warning as the mind's next (wrap-up) turn.
|
|
653
|
-
*/
|
|
654
|
-
function warnAndPinRotation(session: CodexSession) {
|
|
655
|
-
let cutoffLabel = "the cutoff";
|
|
656
|
-
session.rotationCut = null;
|
|
657
|
-
const threadId = session.currentThreadId;
|
|
658
|
-
if (threadId) {
|
|
659
|
-
try {
|
|
660
|
-
const path = findCodexSessionFile(threadId, options.mindDir);
|
|
661
|
-
const cut = path ? computeCodexSeedCut(readFileSync(path, "utf-8"), seedTokens) : null;
|
|
662
|
-
if (cut) {
|
|
663
|
-
session.rotationCut = cut;
|
|
664
|
-
const d = new Date(cut.boundaryTimestamp);
|
|
665
|
-
if (!Number.isNaN(d.getTime())) cutoffLabel = compactTimestamp(d);
|
|
666
|
-
}
|
|
667
|
-
} catch (err) {
|
|
668
|
-
log("mind", `session "${session.name}": failed to pin rotation cut:`, err);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
// biome-ignore lint/suspicious/noTemplateCurlyInString: literal ${cutoff} prompt placeholder
|
|
672
|
-
const warning = compactionMessage().replaceAll("${cutoff}", cutoffLabel);
|
|
673
|
-
session.rotatePending = true;
|
|
674
|
-
// Run the warning next, ahead of any queued messages, so the mind wraps up before
|
|
675
|
-
// more context piles on. Remaining queued messages then run on the rotated thread.
|
|
676
|
-
session.messageQueue.unshift({
|
|
677
|
-
text: warning,
|
|
678
|
-
meta: { messageId: `rotation-warning-${Date.now()}` },
|
|
679
|
-
isRotationWarning: true,
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* After the wrap-up warning turn completes, rotate the thread in place: build a seeded
|
|
685
|
-
* rollout from the pinned cut out of the live rollout, archive the rotated-out thread,
|
|
686
|
-
* and resume the new thread. On any failure we leave the old thread in place and let
|
|
687
|
-
* the SDK's native backstop handle it if context keeps climbing.
|
|
624
|
+
* Rotate the thread in place: build a seeded budget-tail rollout out of the live
|
|
625
|
+
* rollout, archive the rotated-out thread, and resume the new thread. On any failure
|
|
626
|
+
* we leave the old thread in place and let the SDK's native backstop handle it if
|
|
627
|
+
* context keeps climbing.
|
|
688
628
|
*/
|
|
689
629
|
function performRotation(session: CodexSession) {
|
|
690
|
-
session.rotatePending = false;
|
|
691
630
|
const oldThreadId = session.currentThreadId;
|
|
692
|
-
const cut = session.rotationCut;
|
|
693
|
-
session.rotationCut = null;
|
|
694
631
|
|
|
695
632
|
const newThreadId = oldThreadId
|
|
696
633
|
? rotateCodexSession({
|
|
697
634
|
mindDir: options.mindDir,
|
|
698
635
|
name: session.name,
|
|
699
636
|
oldThreadId,
|
|
700
|
-
cut,
|
|
701
637
|
seedTokens,
|
|
702
638
|
})
|
|
703
639
|
: null;
|
|
@@ -733,22 +669,24 @@ export function createMind(options: {
|
|
|
733
669
|
}
|
|
734
670
|
|
|
735
671
|
/**
|
|
736
|
-
* Decide, after a
|
|
737
|
-
*
|
|
738
|
-
*
|
|
672
|
+
* Decide, after a turn, whether to rotate in place: only when a threshold is
|
|
673
|
+
* configured, context is at/over it, and we're under the runaway cap (past the
|
|
674
|
+
* cap, the SDK's native backstop takes over). A turn that came back under the
|
|
675
|
+
* threshold ends any streak. Rotation is silent — the one-line rotation note
|
|
676
|
+
* arms via seededCause and lands on the next turn.
|
|
739
677
|
*/
|
|
740
|
-
function
|
|
678
|
+
function maybeRotate(session: CodexSession) {
|
|
741
679
|
if (!maxContextTokens) return;
|
|
742
680
|
if (session.cumulativeInputTokens < maxContextTokens) {
|
|
743
681
|
session.consecutiveRotations = 0; // healthy turn — streak over
|
|
744
682
|
return;
|
|
745
683
|
}
|
|
746
|
-
if (session.
|
|
684
|
+
if (session.consecutiveRotations >= MAX_CONSECUTIVE_ROTATIONS) return;
|
|
747
685
|
log(
|
|
748
686
|
"mind",
|
|
749
|
-
`session "${session.name}": ${session.cumulativeInputTokens} tokens >= ${maxContextTokens} —
|
|
687
|
+
`session "${session.name}": ${session.cumulativeInputTokens} tokens >= ${maxContextTokens} — rotating`,
|
|
750
688
|
);
|
|
751
|
-
|
|
689
|
+
performRotation(session);
|
|
752
690
|
}
|
|
753
691
|
|
|
754
692
|
// --- Message queue processing ---
|
|
@@ -761,12 +699,8 @@ export function createMind(options: {
|
|
|
761
699
|
const next = session.messageQueue.shift()!;
|
|
762
700
|
session.currentMessageId = next.meta.messageId;
|
|
763
701
|
await runTurn(session, next.text, next.meta);
|
|
764
|
-
if
|
|
765
|
-
|
|
766
|
-
performRotation(session);
|
|
767
|
-
} else {
|
|
768
|
-
maybeWarnRotation(session);
|
|
769
|
-
}
|
|
702
|
+
// Post-turn is between-turns for the queue, so rotate here if we're over.
|
|
703
|
+
maybeRotate(session);
|
|
770
704
|
}
|
|
771
705
|
|
|
772
706
|
session.processing = false;
|
|
@@ -24,25 +24,13 @@ import {
|
|
|
24
24
|
import { daemonEmit } from "./lib/daemon-client.js";
|
|
25
25
|
import { dispatchPrompt } from "./lib/dispatch.js";
|
|
26
26
|
import { createEventHandler, emit } from "./lib/event-handler.js";
|
|
27
|
-
import { compactTimestamp } from "./lib/format-prefix.js";
|
|
28
27
|
import { runHooks } from "./lib/hook-loader.js";
|
|
29
28
|
import { log } from "./lib/logger.js";
|
|
30
|
-
import {
|
|
31
|
-
computePiSeedCut,
|
|
32
|
-
DEFAULT_SEED_TOKENS,
|
|
33
|
-
type PiSeedCut,
|
|
34
|
-
rotatePiSession,
|
|
35
|
-
seedPiSession,
|
|
36
|
-
} from "./lib/pi-session-seed.js";
|
|
29
|
+
import { DEFAULT_SEED_TOKENS, rotatePiSession, seedPiSession } from "./lib/pi-session-seed.js";
|
|
37
30
|
import { createReplyInstructionsExtension } from "./lib/reply-instructions-extension.js";
|
|
38
31
|
import { resolveModel } from "./lib/resolve-model.js";
|
|
39
32
|
import { buildSeededNote, type SeedCause } from "./lib/seed-note.js";
|
|
40
|
-
import {
|
|
41
|
-
getStartupContext,
|
|
42
|
-
loadPrompts,
|
|
43
|
-
renderCompactionWarning,
|
|
44
|
-
type SubagentConfig,
|
|
45
|
-
} from "./lib/startup.js";
|
|
33
|
+
import { getStartupContext, loadPrompts, type SubagentConfig } from "./lib/startup.js";
|
|
46
34
|
import { createSubagentExtension, type SubagentDefinition } from "./lib/subagents.js";
|
|
47
35
|
import type {
|
|
48
36
|
HandlerMeta,
|
|
@@ -81,12 +69,6 @@ type PiSession = {
|
|
|
81
69
|
* note (which fires only on the first turn of a freshly created agent session).
|
|
82
70
|
*/
|
|
83
71
|
rotationNotePending?: boolean;
|
|
84
|
-
/**
|
|
85
|
-
* The cut point pinned at warn time (the first-kept turn the mind was told would
|
|
86
|
-
* survive verbatim). Honored at rotation so the promise in the warning holds even
|
|
87
|
-
* though the transcript grows after the warning. Null until a warning fires.
|
|
88
|
-
*/
|
|
89
|
-
rotationCut?: PiSeedCut | null;
|
|
90
72
|
/**
|
|
91
73
|
* Back-to-back rotations that did NOT bring context under the threshold (reset by
|
|
92
74
|
* any healthy turn). Guards against a runaway loop when the tail alone can't fit —
|
|
@@ -107,7 +89,6 @@ export function createMind(options: {
|
|
|
107
89
|
sessionsDir: string;
|
|
108
90
|
model?: string;
|
|
109
91
|
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
110
|
-
compactionMessage?: string;
|
|
111
92
|
maxContextTokens?: number;
|
|
112
93
|
/** Estimated-token budget for seeding a fresh persistent session. 0 disables. Default 30000. */
|
|
113
94
|
seedTokens?: number;
|
|
@@ -119,8 +100,6 @@ export function createMind(options: {
|
|
|
119
100
|
} {
|
|
120
101
|
const sessions = new Map<string, PiSession>();
|
|
121
102
|
const prompts = loadPrompts();
|
|
122
|
-
const compactionMessage = () =>
|
|
123
|
-
options.compactionMessage ?? renderCompactionWarning(prompts.compaction_warning);
|
|
124
103
|
const compactionInstructions = prompts.compaction_instructions;
|
|
125
104
|
const maxContextTokens = options.maxContextTokens;
|
|
126
105
|
const seedTokens = options.seedTokens ?? DEFAULT_SEED_TOKENS;
|
|
@@ -387,62 +366,26 @@ export function createMind(options: {
|
|
|
387
366
|
|
|
388
367
|
log("mind", `session "${session.name}": ${isEphemeral ? "ephemeral" : "persistent"}`);
|
|
389
368
|
|
|
390
|
-
// Compaction
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
// (or, past the runaway cap, the native compact() backstop)
|
|
369
|
+
// Compaction is rotation (not SDK /compact), and it's silent: crossing the
|
|
370
|
+
// threshold (onContextTokens) or a native PreCompact just sets rotatePending; the
|
|
371
|
+
// session rotates in place at the end of the turn that crossed it — no warning, no
|
|
372
|
+
// wrap-up turn. The auto summarizer's turn summaries are the record of collapsed
|
|
373
|
+
// turns, and the one-line rotation note marks the boundary on the next turn. Past
|
|
374
|
+
// the runaway cap, onTurnEnd defers to the native compact() backstop instead.
|
|
397
375
|
let compactBlocked = false;
|
|
398
376
|
let manualCompactPending = false;
|
|
399
|
-
let
|
|
400
|
-
let compactOnNextTurnEnd = false;
|
|
377
|
+
let rotatePending = false;
|
|
401
378
|
let compactionInProgress = false;
|
|
402
379
|
|
|
403
380
|
function resetCompactionState() {
|
|
404
|
-
|
|
405
|
-
compactOnNextTurnEnd = false;
|
|
381
|
+
rotatePending = false;
|
|
406
382
|
compactionInProgress = false;
|
|
407
383
|
}
|
|
408
384
|
|
|
409
|
-
/**
|
|
410
|
-
* Warn the mind that the session will rotate at the context limit and pin the cut
|
|
411
|
-
* point it's told about. Computes which whole turns fit in `seedTokens` from the
|
|
412
|
-
* live transcript, pins the first-kept turn (so rotation honors the same boundary
|
|
413
|
-
* even as the transcript grows), interpolates its local time into the `${cutoff}`
|
|
414
|
-
* placeholder, and sends the warning as the wrap-up prompt. Shared by the
|
|
415
|
-
* threshold and native-PreCompact paths.
|
|
416
|
-
*/
|
|
417
|
-
function warnAndPinRotation() {
|
|
418
|
-
let cutoffLabel = "the cutoff";
|
|
419
|
-
session.rotationCut = null;
|
|
420
|
-
const sourcePath = session.agentSession?.sessionManager.getSessionFile();
|
|
421
|
-
if (sourcePath) {
|
|
422
|
-
try {
|
|
423
|
-
const cut = computePiSeedCut(readFileSync(sourcePath, "utf-8"), seedTokens);
|
|
424
|
-
if (cut) {
|
|
425
|
-
session.rotationCut = cut;
|
|
426
|
-
if (cut.boundaryTimestamp) {
|
|
427
|
-
const d = new Date(cut.boundaryTimestamp);
|
|
428
|
-
if (!Number.isNaN(d.getTime())) cutoffLabel = compactTimestamp(d);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
} catch (err) {
|
|
432
|
-
log("mind", `session "${session.name}": failed to pin rotation cut:`, err);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
// biome-ignore lint/suspicious/noTemplateCurlyInString: literal ${cutoff} prompt placeholder
|
|
436
|
-
const warning = compactionMessage().replaceAll("${cutoff}", cutoffLabel);
|
|
437
|
-
compactionTriggered = true;
|
|
438
|
-
session.messageIds.push(undefined);
|
|
439
|
-
session.agentSession?.prompt(warning, { streamingBehavior: "followUp" });
|
|
440
|
-
}
|
|
441
|
-
|
|
442
385
|
/**
|
|
443
386
|
* Rotate the session in place onto a synthetic session holding the verbatim recent
|
|
444
|
-
* tail (
|
|
445
|
-
* false if rotation can't proceed (session not ready, or the file build failed) so
|
|
387
|
+
* tail (the seedTokens-budget tail), then switch the running SessionManager to it.
|
|
388
|
+
* Returns false if rotation can't proceed (session not ready, or the file build failed) so
|
|
446
389
|
* the caller can fall back to a fresh session. The setSessionFile + state.messages
|
|
447
390
|
* re-sync is the load-bearing adoption step: the agent caches context in
|
|
448
391
|
* state.messages, so swapping the file alone wouldn't change what the mind sees
|
|
@@ -457,10 +400,8 @@ export function createMind(options: {
|
|
|
457
400
|
sessionsDir: options.sessionsDir,
|
|
458
401
|
name: session.name,
|
|
459
402
|
sourcePath,
|
|
460
|
-
cut: session.rotationCut ?? null,
|
|
461
403
|
seedTokens,
|
|
462
404
|
});
|
|
463
|
-
session.rotationCut = null;
|
|
464
405
|
if (!newPath) return false;
|
|
465
406
|
as.sessionManager.setSessionFile(newPath);
|
|
466
407
|
as.state.messages = as.sessionManager.buildSessionContext().messages;
|
|
@@ -487,7 +428,6 @@ export function createMind(options: {
|
|
|
487
428
|
as.sessionManager.newSession();
|
|
488
429
|
as.state.messages = [];
|
|
489
430
|
}
|
|
490
|
-
session.rotationCut = null;
|
|
491
431
|
session.consecutiveRotations = 0;
|
|
492
432
|
compactBlocked = false;
|
|
493
433
|
log("mind", `session "${session.name}": rotation failed, starting fresh`);
|
|
@@ -520,23 +460,22 @@ export function createMind(options: {
|
|
|
520
460
|
}
|
|
521
461
|
|
|
522
462
|
// The SDK's native auto-compaction wants to fire. Converge it onto rotation:
|
|
523
|
-
// the first pass
|
|
524
|
-
// already did, or the runaway cap is hit) and blocks the native
|
|
525
|
-
// the second pass allows native compaction as the emergency
|
|
526
|
-
// reachable if rotation never brought context under control).
|
|
463
|
+
// the first pass marks the session for rotation at turn end (unless the
|
|
464
|
+
// threshold path already did, or the runaway cap is hit) and blocks the native
|
|
465
|
+
// compaction; the second pass allows native compaction as the emergency
|
|
466
|
+
// backstop (only reachable if rotation never brought context under control).
|
|
527
467
|
if (!compactBlocked) {
|
|
528
468
|
compactBlocked = true;
|
|
529
469
|
if (
|
|
530
|
-
!
|
|
531
|
-
!compactOnNextTurnEnd &&
|
|
470
|
+
!rotatePending &&
|
|
532
471
|
!compactionInProgress &&
|
|
533
472
|
(session.consecutiveRotations ?? 0) < MAX_CONSECUTIVE_ROTATIONS
|
|
534
473
|
) {
|
|
535
474
|
log(
|
|
536
475
|
"mind",
|
|
537
|
-
`session "${session.name}": native compaction —
|
|
476
|
+
`session "${session.name}": native compaction — rotation pending at turn end`,
|
|
538
477
|
);
|
|
539
|
-
|
|
478
|
+
rotatePending = true;
|
|
540
479
|
} else {
|
|
541
480
|
log("mind", `session "${session.name}": blocking native compaction (rotation pending)`);
|
|
542
481
|
}
|
|
@@ -595,8 +534,7 @@ export function createMind(options: {
|
|
|
595
534
|
if (
|
|
596
535
|
maxContextTokens &&
|
|
597
536
|
tokens >= maxContextTokens &&
|
|
598
|
-
!
|
|
599
|
-
!compactOnNextTurnEnd &&
|
|
537
|
+
!rotatePending &&
|
|
600
538
|
!compactionInProgress &&
|
|
601
539
|
(session.consecutiveRotations ?? 0) < MAX_CONSECUTIVE_ROTATIONS
|
|
602
540
|
) {
|
|
@@ -609,18 +547,18 @@ export function createMind(options: {
|
|
|
609
547
|
}
|
|
610
548
|
log(
|
|
611
549
|
"mind",
|
|
612
|
-
`session "${session.name}": ${tokens} tokens >= ${maxContextTokens} —
|
|
550
|
+
`session "${session.name}": ${tokens} tokens >= ${maxContextTokens} — rotation pending at turn end`,
|
|
613
551
|
);
|
|
614
|
-
|
|
552
|
+
rotatePending = true;
|
|
615
553
|
}
|
|
616
554
|
},
|
|
617
555
|
onTurnEnd: maxContextTokens
|
|
618
556
|
? () => {
|
|
619
557
|
try {
|
|
620
|
-
// The
|
|
621
|
-
// place onto the verbatim tail
|
|
622
|
-
if (
|
|
623
|
-
|
|
558
|
+
// The turn that crossed the threshold is done — rotate the session in
|
|
559
|
+
// place onto the verbatim recent tail. Silent: no warning, no wrap-up.
|
|
560
|
+
if (rotatePending) {
|
|
561
|
+
rotatePending = false;
|
|
624
562
|
if ((session.consecutiveRotations ?? 0) >= MAX_CONSECUTIVE_ROTATIONS) {
|
|
625
563
|
// Runaway guard: rotation isn't relieving context (system prompt too
|
|
626
564
|
// large to fit the tail under the threshold) — defer to the SDK.
|
|
@@ -632,11 +570,6 @@ export function createMind(options: {
|
|
|
632
570
|
}
|
|
633
571
|
return;
|
|
634
572
|
}
|
|
635
|
-
if (compactionTriggered) {
|
|
636
|
-
compactionTriggered = false;
|
|
637
|
-
compactOnNextTurnEnd = true;
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
573
|
// A healthy turn (context under the threshold) — the rotation streak,
|
|
641
574
|
// if any, is over, so re-arm the self-rotation cap.
|
|
642
575
|
session.consecutiveRotations = 0;
|
|
@@ -31,7 +31,6 @@ const mind = createMind({
|
|
|
31
31
|
sessionsDir: resolve(".mind/pi-sessions"),
|
|
32
32
|
model: config.model,
|
|
33
33
|
thinkingLevel: config.thinkingLevel,
|
|
34
|
-
compactionMessage: config.compactionMessage,
|
|
35
34
|
maxContextTokens: config.compaction?.maxContextTokens,
|
|
36
35
|
seedTokens: config.continuity?.seedTokens,
|
|
37
36
|
subagents: config.subagents,
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
autoUpgradeOne,
|
|
4
|
-
getUpgradeBlocked,
|
|
5
|
-
pruneBlocked,
|
|
6
|
-
runAutoUpgrades,
|
|
7
|
-
selectEligible
|
|
8
|
-
} from "./chunk-OT5SXF7L.js";
|
|
9
|
-
import "./chunk-CV6BNGFK.js";
|
|
10
|
-
import "./chunk-HTRSSOHY.js";
|
|
11
|
-
import "./chunk-OGPVJ53N.js";
|
|
12
|
-
import "./chunk-LGXVGIWN.js";
|
|
13
|
-
import "./chunk-RGHF3OK5.js";
|
|
14
|
-
import "./chunk-X67BDDZR.js";
|
|
15
|
-
import "./chunk-444RR3YF.js";
|
|
16
|
-
import "./chunk-VDUCLVZI.js";
|
|
17
|
-
import "./chunk-WHL6PKYP.js";
|
|
18
|
-
import "./chunk-YSRYCS3N.js";
|
|
19
|
-
import "./chunk-56IT2ZXF.js";
|
|
20
|
-
import "./chunk-PPSN2MLC.js";
|
|
21
|
-
import "./chunk-7LT3YZUR.js";
|
|
22
|
-
import "./chunk-HKGYZVWN.js";
|
|
23
|
-
import "./chunk-LNNI3MS4.js";
|
|
24
|
-
import "./chunk-OAYY3WF3.js";
|
|
25
|
-
import "./chunk-5Y54AE3L.js";
|
|
26
|
-
import "./chunk-TPX6E6PL.js";
|
|
27
|
-
import "./chunk-UFTDRTCP.js";
|
|
28
|
-
import "./chunk-U54PJPTB.js";
|
|
29
|
-
import "./chunk-ICXY54ZC.js";
|
|
30
|
-
import "./chunk-DW4TRDOZ.js";
|
|
31
|
-
import "./chunk-WCFSBH33.js";
|
|
32
|
-
import "./chunk-OIOW3FPT.js";
|
|
33
|
-
import "./chunk-GLM7TVWP.js";
|
|
34
|
-
import "./chunk-EGV6R6UF.js";
|
|
35
|
-
import "./chunk-QUSMYGNQ.js";
|
|
36
|
-
import "./chunk-LGNUFVMR.js";
|
|
37
|
-
import "./chunk-H6X6CKDN.js";
|
|
38
|
-
import "./chunk-2RNMBXD2.js";
|
|
39
|
-
import "./chunk-K3NQKI34.js";
|
|
40
|
-
export {
|
|
41
|
-
autoUpgradeOne,
|
|
42
|
-
getUpgradeBlocked,
|
|
43
|
-
pruneBlocked,
|
|
44
|
-
runAutoUpgrades,
|
|
45
|
-
selectEligible
|
|
46
|
-
};
|