@linxiraos/pi-agent-core 1.0.1 → 1.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +28 -16
  2. package/package.json +76 -77
  3. package/src/agent-loop.ts +13 -4
  4. package/src/compaction/prompts/branch-summary-context.md +1 -1
  5. package/src/compaction/prompts/branch-summary-preamble.md +2 -2
  6. package/src/compaction/prompts/compaction-short-summary.md +3 -9
  7. package/src/compaction/prompts/compaction-summary-context.md +2 -1
  8. package/src/compaction/prompts/compaction-turn-prefix.md +4 -4
  9. package/src/compaction/prompts/compaction-update-summary.md +15 -16
  10. package/src/compaction/prompts/context-window-truncated-output.md +1 -1
  11. package/src/compaction/prompts/summarization-system.md +2 -2
  12. package/src/compaction/shake.ts +9 -3
  13. package/src/types.ts +11 -1
  14. package/dist/types/agent-loop.d.ts +0 -137
  15. package/dist/types/agent.d.ts +0 -465
  16. package/dist/types/append-only-context.d.ts +0 -131
  17. package/dist/types/compaction/branch-summarization.d.ts +0 -101
  18. package/dist/types/compaction/compaction-v2-streaming.d.ts +0 -85
  19. package/dist/types/compaction/compaction.d.ts +0 -307
  20. package/dist/types/compaction/entries.d.ts +0 -111
  21. package/dist/types/compaction/errors.d.ts +0 -38
  22. package/dist/types/compaction/index.d.ts +0 -13
  23. package/dist/types/compaction/message-cache.d.ts +0 -23
  24. package/dist/types/compaction/messages.d.ts +0 -79
  25. package/dist/types/compaction/openai.d.ts +0 -117
  26. package/dist/types/compaction/pruning.d.ts +0 -105
  27. package/dist/types/compaction/shake.d.ts +0 -97
  28. package/dist/types/compaction/tool-protection.d.ts +0 -19
  29. package/dist/types/compaction/utils.d.ts +0 -61
  30. package/dist/types/compaction.d.ts +0 -1
  31. package/dist/types/index.d.ts +0 -13
  32. package/dist/types/pause.d.ts +0 -46
  33. package/dist/types/proxy.d.ts +0 -91
  34. package/dist/types/replay-policy.d.ts +0 -5
  35. package/dist/types/run-collector.d.ts +0 -196
  36. package/dist/types/telemetry.d.ts +0 -608
  37. package/dist/types/thinking.d.ts +0 -18
  38. package/dist/types/tokenizer.d.ts +0 -2
  39. package/dist/types/types.d.ts +0 -781
  40. package/dist/types/utils/yield.d.ts +0 -71
package/CHANGELOG.md CHANGED
@@ -2,7 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.0.1] - 2026-08-14
5
+ ## [1.0.2] - 2026-08-15
6
+
7
+ ## [17.3.0] - 2026-08-13
8
+
9
+ ### Fixed
10
+
11
+ - Improved the manual `/shake` command to retain a small history of recent tool results, preventing the agent from losing its active working context.
12
+
13
+ ## [17.2.13] - 2026-08-11
14
+
15
+ ### Fixed
16
+
17
+ - Fixed Cursor sessions re-executing settled tools when an owned dialect projector rebuilds toolCall blocks: `snapshotAssistantContentBlock` now copies `kCursorExecResolved` explicitly so agent-loop still skips already-settled calls.
6
18
 
7
19
  ## [17.2.10] - 2026-08-06
8
20
 
@@ -332,7 +344,7 @@
332
344
 
333
345
  ### Added
334
346
 
335
- - Added `generateHandoffFromContext(context, model, options)` to `@oh-my-pi/pi-agent-core/compaction`: runs the handoff oneshot against a fully-built provider `Context` (system prompt, normalized tools, transformed history, trailing handoff prompt) with `streamOptions` mirroring the live turn's cache routing, so a host that owns the transform pipeline can make the handoff request share the prompt cache the main turn populated. `generateHandoff(messages, …)` is unchanged and now delegates to it.
347
+ - Added `generateHandoffFromContext(context, model, options)` to `@linxiraos/pi-agent-core/compaction`: runs the handoff oneshot against a fully-built provider `Context` (system prompt, normalized tools, transformed history, trailing handoff prompt) with `streamOptions` mirroring the live turn's cache routing, so a host that owns the transform pipeline can make the handoff request share the prompt cache the main turn populated. `generateHandoff(messages, …)` is unchanged and now delegates to it.
336
348
  - Added an optional `systemPrompt` argument to `Agent.buildSideRequestContext(llmMessages, systemPrompt?)`, defaulting to the live agent prompt; callers can pin a different prompt (e.g. handoff generation, which uses the base prompt rather than a per-turn `before_agent_start` hook override).
337
349
 
338
350
  ### Changed
@@ -427,7 +439,7 @@
427
439
  ### Fixed
428
440
 
429
441
  - Fixed transient provider errors after streamed tool-call arguments so incomplete tool calls are marked as interrupted output instead of eligible for automatic retry ([#2683](https://github.com/can1357/oh-my-pi/issues/2683)).
430
- - Fixed `@oh-my-pi/pi-agent-core` telemetry content capture crashing every chat turn with `TypeError: systemPrompt.map is not a function` when `captureMessageContent` is enabled (`OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`). `ChatRequestSnapshot.systemPrompt` now accepts `string | readonly string[]` and the telemetry serializers normalize a bare string to a single-element array — previously the full-system serializer called `.map` on a string (the `.length` guard passed, so it threw) and the request-message serializer iterated the string into one `system` message per character.
442
+ - Fixed `@linxiraos/pi-agent-core` telemetry content capture crashing every chat turn with `TypeError: systemPrompt.map is not a function` when `captureMessageContent` is enabled (`OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`). `ChatRequestSnapshot.systemPrompt` now accepts `string | readonly string[]` and the telemetry serializers normalize a bare string to a single-element array — previously the full-system serializer called `.map` on a string (the `.length` guard passed, so it threw) and the request-message serializer iterated the string into one `system` message per character.
431
443
 
432
444
  ## [16.0.0] - 2026-06-15
433
445
 
@@ -462,7 +474,7 @@
462
474
 
463
475
  ### Breaking Changes
464
476
 
465
- - Removed `harmony-leak` exports from the `@oh-my-pi/pi-agent-core` package entrypoint
477
+ - Removed `harmony-leak` exports from the `@linxiraos/pi-agent-core` package entrypoint
466
478
  - Replaced the experimental `promptToolCalls` agent/loop option with `toolCallSyntax`, selecting an explicit in-band tool-call grammar instead of a boolean GLM-only mode.
467
479
 
468
480
  ### Added
@@ -554,7 +566,7 @@
554
566
 
555
567
  ### Breaking Changes
556
568
 
557
- - Removed `compaction/index.ts` re-export of snapcompact helpers, so snapcompact utilities are no longer available from the agent compaction barrel and should be imported from `@oh-my-pi/snapcompact`
569
+ - Removed `compaction/index.ts` re-export of snapcompact helpers, so snapcompact utilities are no longer available from the agent compaction barrel and should be imported from `@linxiraos/pi-snapcompact`
558
570
  - Removed the `convertToLlm` alias export from `compaction/messages` — it duplicated `defaultConvertToLlm` under a second name. Import `defaultConvertToLlm` (array form) or the new `convertMessageToLlm` (single-message form) instead
559
571
 
560
572
  ### Added
@@ -562,7 +574,7 @@
562
574
  - Added `convertMessageToLlm()`: the single-message core transformer behind `defaultConvertToLlm()`. Embedders with app-specific message roles should handle their own roles and delegate every core role (`user`/`developer`/`assistant`/`toolResult`/`custom`/`hookMessage`/`branchSummary`/`compactionSummary`) to it instead of duplicating the conversion — a duplicated `compactionSummary` case is how snapcompact frames once silently dropped off provider requests
563
575
  - Added `pruneSupersededToolResults()` and the opt-in `PruneConfig.supersedeKey` hook so harnesses can prune stale tool results superseded by a newer read of the same file; superseded results are pruned ahead of age-based victims during overflow pruning and replaced with a `[Superseded by a newer read of this file]` placeholder. Without the new config, `pruneToolOutputs()` behavior is unchanged.
564
576
  - Added `readToolSupersedeKey()` implementing the read-tool path/selector grammar (selector-free reads supersede range reads of the same file; URL-scheme paths exempt). Pruning honors prompt-cache economics: per-turn prunes only fire when the post-candidate suffix is small or the cache is cold (idle gap).
565
- - Added the `snapcompact` compaction strategy via `@oh-my-pi/snapcompact`: instead of an LLM summary, discarded history is printed onto dense bitmap frames and re-attached to the compaction summary message as image blocks. `CompactionSummaryMessage` gains an optional `images` field, `estimateTokens()` charges per attached frame, and frames persist under `preserveData.snapcompact` with an 8-frame middle-out eviction budget.
577
+ - Added the `snapcompact` compaction strategy via `@linxiraos/pi-snapcompact`: instead of an LLM summary, discarded history is printed onto dense bitmap frames and re-attached to the compaction summary message as image blocks. `CompactionSummaryMessage` gains an optional `images` field, `estimateTokens()` charges per attached frame, and frames persist under `preserveData.snapcompact` with an 8-frame middle-out eviction budget.
566
578
  - Snapcompact frames are now rendered in a provider-aware shape (`SNAPCOMPACT_SHAPES` + `resolveSnapcompactShape(api)`), following the snapcompact 200k-token monolithic evals: Anthropic-family and unknown APIs get `8x8r-bw` (unscii-8 square cells, black ink, every line printed twice with the copy on a pale highlight band — read at F1 parity with raw text at ~2x lower cost and the most refusal-robust), Google gets `8x8r-sent` (sentence-hue ink, ~2.9x cheaper), and OpenAI gets `6x6u-sent` (unscii Lanczos-stretched to 6x6 cells — OpenAI bills a flat ~2.9k tokens per image, so frame count is the only cost lever) with `detail: "original"` on the frame images. `snapcompactCompact()` accepts `model`/`shape` options, frames persist their shape metadata, mixed-shape archives (provider switches, legacy 5x8 frames) are flagged in the reading instructions, and `snapcompactGeometry()`/`renderSnapcompactFrame()` now take a shape
567
579
 
568
580
  ### Changed
@@ -591,7 +603,7 @@
591
603
  ### Changed
592
604
 
593
605
  - Editorial pass over the compaction prompts: fixed garbled grammar and missing articles, RFC-keyed prohibitions, deduped restated instructions; parsed markers (`<read-files>`/`<modified-files>`/`<previous-summary>`) and all output-format headings left byte-identical
594
- - Catalog imports moved to the new `@oh-my-pi/pi-catalog` package: subpath imports (`calculateCost`, Codex wire constants) plus catalog values previously taken from the `@oh-my-pi/pi-ai` root (`getBundledModel`, `clampThinkingLevelForModel`), which pi-ai no longer re-exports; type-only `Model`/`Api`/`Effort` imports from pi-ai are unchanged
606
+ - Catalog imports moved to the new `@linxiraos/pi-catalog` package: subpath imports (`calculateCost`, Codex wire constants) plus catalog values previously taken from the `@linxiraos/pi-ai` root (`getBundledModel`, `clampThinkingLevelForModel`), which pi-ai no longer re-exports; type-only `Model`/`Api`/`Effort` imports from pi-ai are unchanged
595
607
 
596
608
  ## [15.10.8] - 2026-06-09
597
609
 
@@ -675,8 +687,8 @@
675
687
 
676
688
  ### Added
677
689
 
678
- - Added `getReadToolPath(context)` to `@oh-my-pi/pi-agent-core/compaction/tool-protection` to extract a paired `read` tool call's `path` for embedders building read-targeted protection matchers
679
- - Added `getReadToolPath(context)` to `@oh-my-pi/pi-agent-core/compaction/tool-protection`: the shared primitive that extracts a paired `read` tool call's `path` argument, so embedders can build their own read-targeted compaction protection matchers (e.g. plan-file reads) the same way `isSkillReadToolResult` does.
690
+ - Added `getReadToolPath(context)` to `@linxiraos/pi-agent-core/compaction/tool-protection` to extract a paired `read` tool call's `path` for embedders building read-targeted protection matchers
691
+ - Added `getReadToolPath(context)` to `@linxiraos/pi-agent-core/compaction/tool-protection`: the shared primitive that extracts a paired `read` tool call's `path` argument, so embedders can build their own read-targeted compaction protection matchers (e.g. plan-file reads) the same way `isSkillReadToolResult` does.
680
692
 
681
693
  ## [15.8.2] - 2026-06-03
682
694
 
@@ -704,7 +716,7 @@
704
716
 
705
717
  ### Added
706
718
 
707
- - Added `shake` compaction primitives (`collectShakeRegions`, `applyShakeRegion`, `applyShakeRegions`, `summarizeShakeRegions`, `DEFAULT_SHAKE_CONFIG`, `AGGRESSIVE_SHAKE_CONFIG`, plus the `ShakeRegion`/`ShakeConfig`/`ShakeSummaryItem`/`ShakeSummaryComplete`/`ProtectedToolMatcher` types) under `@oh-my-pi/pi-agent-core/compaction`. These detect heavy context regions — whole tool-call results plus large fenced/XML blocks — and either elide them with placeholders or extractively compress them through an injected completion backend (no LLM summary cut-point). The compressor is provider-agnostic: callers wire it to a local on-device model. Pure detection/mutation; no I/O.
719
+ - Added `shake` compaction primitives (`collectShakeRegions`, `applyShakeRegion`, `applyShakeRegions`, `summarizeShakeRegions`, `DEFAULT_SHAKE_CONFIG`, `AGGRESSIVE_SHAKE_CONFIG`, plus the `ShakeRegion`/`ShakeConfig`/`ShakeSummaryItem`/`ShakeSummaryComplete`/`ProtectedToolMatcher` types) under `@linxiraos/pi-agent-core/compaction`. These detect heavy context regions — whole tool-call results plus large fenced/XML blocks — and either elide them with placeholders or extractively compress them through an injected completion backend (no LLM summary cut-point). The compressor is provider-agnostic: callers wire it to a local on-device model. Pure detection/mutation; no I/O.
708
720
 
709
721
  ### Fixed
710
722
 
@@ -730,7 +742,7 @@
730
742
 
731
743
  ### Fixed
732
744
 
733
- - Fixed compaction summarizer throws losing the provider's HTTP status. `generateSummary`, `generateHandoff`, `generateShortSummary`, and `generateTurnPrefixSummary` now route their `stopReason === "error"` throws through a `createSummarizationError` helper that copies `AssistantMessage.errorStatus` onto the thrown `Error` as `.status`, letting downstream consumers (e.g. `AgentSession.#isCompactionAuthFailure` in `@oh-my-pi/pi-coding-agent`) branch on real provider 401/403s without regex-scraping the message body.
745
+ - Fixed compaction summarizer throws losing the provider's HTTP status. `generateSummary`, `generateHandoff`, `generateShortSummary`, and `generateTurnPrefixSummary` now route their `stopReason === "error"` throws through a `createSummarizationError` helper that copies `AssistantMessage.errorStatus` onto the thrown `Error` as `.status`, letting downstream consumers (e.g. `AgentSession.#isCompactionAuthFailure` in `@linxiraos/zeta`) branch on real provider 401/403s without regex-scraping the message body.
734
746
 
735
747
  ## [15.5.0] - 2026-05-26
736
748
 
@@ -776,12 +788,12 @@
776
788
 
777
789
  ### Breaking Changes
778
790
 
779
- - Removed the `@oh-my-pi/pi-agent-core/compaction/handoff` exports from the package surface, including `extractHandoffDocument`, `createHandoffContext`, and `createHandoffFileName`
791
+ - Removed the `@linxiraos/pi-agent-core/compaction/handoff` exports from the package surface, including `extractHandoffDocument`, `createHandoffContext`, and `createHandoffFileName`
780
792
  - Removed legacy telemetry constants from the public enum surface (including `AGGREGATE_ATTR`, `GenAIAttr.System`, and old `gen_ai.*` extension keys such as `gen_ai.request.service_tier`/cost/tool status/handoff fields) and replaced them with `OpenAIAttr`, `PiGenAIAttr`, and `PiGenAIAggregateAttr`
781
793
 
782
794
  ### Added
783
795
 
784
- - Added `generateHandoff(messages, model, apiKey, options)` to `@oh-my-pi/pi-agent-core/compaction` to generate a handoff document by calling the model directly, using live system/tool context and optional metadata
796
+ - Added `generateHandoff(messages, model, apiKey, options)` to `@linxiraos/pi-agent-core/compaction` to generate a handoff document by calling the model directly, using live system/tool context and optional metadata
785
797
  - Added generation filtering so the returned handoff document now includes only text content blocks from the model output
786
798
  - Added support for defining `AgentTool` schemas with Zod, with legacy TypeBox schemas still supported when generating tool schemas for model calls
787
799
  - Added `OpenAIAttr`, `PiGenAIAttr`, and `PiGenAIAggregateAttr` exports so consumers can reference the new `openai.*` and `pi.gen_ai.*` telemetry attribute keys directly
@@ -798,7 +810,7 @@
798
810
  - Added `AgentTelemetryConfig` hooks (`onSpanStart`, `onSpanEnd`, `costEstimator`), `agent` identity, `attributes` envelope merged onto every span, `captureMessageContent` toggle (defaults to the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` env var) emitting OTEL-shaped `gen_ai.input.messages` / `gen_ai.output.messages` / `gen_ai.system_instructions` / `gen_ai.tool.call.arguments` / `gen_ai.tool.call.result`, and tracer/tracerName override surfaces.
799
811
  - Added `Agent#setTelemetry(config)` so consumers can swap or disable instrumentation between invocations.
800
812
  - Added `@opentelemetry/api` as a runtime dependency; SDK setup (exporters, samplers, processors) remains the host's responsibility per standard OTEL conventions. When no SDK is registered, helpers fall through to no-op spans with zero overhead.
801
- - Added compaction APIs under `@oh-my-pi/pi-agent-core/compaction`, including context compaction, branch summarization, handoff prompt/context helpers, pruning, token budgeting, prompt templates, and OpenAI `/responses/compact` helpers.
813
+ - Added compaction APIs under `@linxiraos/pi-agent-core/compaction`, including context compaction, branch summarization, handoff prompt/context helpers, pruning, token budgeting, prompt templates, and OpenAI `/responses/compact` helpers.
802
814
 
803
815
  ### Changed
804
816
 
@@ -1145,7 +1157,7 @@
1145
1157
 
1146
1158
  ### Changed
1147
1159
 
1148
- - Switched from local `@oh-my-pi/pi-ai` to upstream `@mariozechner/pi-ai` package
1160
+ - Switched from local `@linxiraos/pi-ai` to upstream `@mariozechner/pi-ai` package
1149
1161
 
1150
1162
  ### Added
1151
1163
 
@@ -1252,7 +1264,7 @@ Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mon
1252
1264
  - **`AppMessage` renamed to `AgentMessage`**: All references to `AppMessage` have been renamed to `AgentMessage` for consistency.
1253
1265
  - **`CustomMessages` renamed to `CustomAgentMessages`**: The declaration merging interface has been renamed.
1254
1266
  - **`UserMessageWithAttachments` and `Attachment` types removed**: Attachment handling is now the responsibility of the `convertToLlm` function.
1255
- - **Agent loop moved from `@oh-my-pi/pi-ai`**: The `agentLoop`, `agentLoopContinue`, and related types have moved to this package. Import from `@oh-my-pi/pi-agent` instead.
1267
+ - **Agent loop moved from `@linxiraos/pi-ai`**: The `agentLoop`, `agentLoopContinue`, and related types have moved to this package. Import from `@oh-my-pi/pi-agent` instead.
1256
1268
 
1257
1269
  ### Added
1258
1270
 
package/package.json CHANGED
@@ -1,79 +1,78 @@
1
1
  {
2
- "type": "module",
3
- "name": "@linxiraos/pi-agent-core",
4
- "version": "1.0.1",
5
- "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
- "homepage": "https://linxira-os.github.io",
7
- "author": "Can Boluk",
8
- "contributors": [
9
- "Mario Zechner"
10
- ],
11
- "license": "MIT",
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
15
- "directory": "packages/agent"
16
- },
17
- "bugs": {
18
- "url": "https://github.com/can1357/oh-my-pi/issues"
19
- },
20
- "keywords": [
21
- "ai",
22
- "agent",
23
- "llm",
24
- "transport",
25
- "state-management"
26
- ],
27
- "main": "./src/index.ts",
28
- "types": "./dist/types/index.d.ts",
29
- "scripts": {
30
- "check": "biome check . && bun run check:types",
31
- "check:types": "tsgo -p tsconfig.json --noEmit",
32
- "lint": "biome lint .",
33
- "test": "bun test --parallel",
34
- "fix": "biome check --write --unsafe .",
35
- "fmt": "biome format --write ."
36
- },
37
- "dependencies": {
38
- "@opentelemetry/api": "^1.9.1",
39
- "@linxiraos/pi-ai": "1.0.1",
40
- "@linxiraos/pi-catalog": "1.0.1",
41
- "@linxiraos/pi-natives": "1.0.1",
42
- "@linxiraos/pi-utils": "1.0.1",
43
- "@linxiraos/pi-wire": "1.0.1",
44
- "@linxiraos/pi-snapcompact": "1.0.1"
45
- },
46
- "devDependencies": {
47
- "@opentelemetry/context-async-hooks": "^2.9.0",
48
- "@opentelemetry/sdk-trace-base": "^2.9.0",
49
- "@types/bun": "^1.3.14",
50
- "@linxiraos/pi-omptype": "1.0.1"
51
- },
52
- "engines": {
53
- "bun": ">=1.3.14"
54
- },
55
- "files": [
56
- "src",
57
- "README.md",
58
- "CHANGELOG.md",
59
- "dist/types"
60
- ],
61
- "exports": {
62
- ".": {
63
- "types": "./dist/types/index.d.ts",
64
- "import": "./src/index.ts"
65
- },
66
- "./compaction": {
67
- "types": "./dist/types/compaction.d.ts",
68
- "import": "./src/compaction.ts"
69
- },
70
- "./compaction/*": {
71
- "types": "./dist/types/compaction/*.d.ts",
72
- "import": "./src/compaction/*.ts"
73
- },
74
- "./*": {
75
- "types": "./dist/types/*.d.ts",
76
- "import": "./src/*.ts"
77
- }
78
- }
2
+ "type": "module",
3
+ "name": "@linxiraos/pi-agent-core",
4
+ "version": "1.0.2",
5
+ "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
+ "homepage": "https://linxira-os.github.io",
7
+ "author": "Can Boluk",
8
+ "contributors": [
9
+ "Mario Zechner"
10
+ ],
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
15
+ "directory": "packages/agent"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/can1357/oh-my-pi/issues"
19
+ },
20
+ "keywords": [
21
+ "ai",
22
+ "agent",
23
+ "llm",
24
+ "transport",
25
+ "state-management"
26
+ ],
27
+ "main": "./src/index.ts",
28
+ "types": "./src/index.ts",
29
+ "scripts": {
30
+ "check": "biome check . && bun run check:types",
31
+ "check:types": "tsgo -p tsconfig.json --noEmit",
32
+ "lint": "biome lint .",
33
+ "test": "bun test --parallel",
34
+ "fix": "biome check --write --unsafe .",
35
+ "fmt": "biome format --write ."
36
+ },
37
+ "dependencies": {
38
+ "@linxiraos/pi-ai": "catalog:",
39
+ "@linxiraos/pi-catalog": "catalog:",
40
+ "@linxiraos/pi-natives": "catalog:",
41
+ "@linxiraos/pi-utils": "catalog:",
42
+ "@linxiraos/pi-wire": "catalog:",
43
+ "@linxiraos/pi-snapcompact": "catalog:",
44
+ "@opentelemetry/api": "catalog:"
45
+ },
46
+ "devDependencies": {
47
+ "@linxiraos/pi-omptype": "catalog:",
48
+ "@opentelemetry/context-async-hooks": "catalog:",
49
+ "@opentelemetry/sdk-trace-base": "catalog:",
50
+ "@types/bun": "catalog:"
51
+ },
52
+ "engines": {
53
+ "bun": ">=1.3.14"
54
+ },
55
+ "files": [
56
+ "src",
57
+ "README.md",
58
+ "CHANGELOG.md"
59
+ ],
60
+ "exports": {
61
+ ".": {
62
+ "types": "./src/index.ts",
63
+ "import": "./src/index.ts"
64
+ },
65
+ "./compaction": {
66
+ "types": "./src/compaction.ts",
67
+ "import": "./src/compaction.ts"
68
+ },
69
+ "./compaction/*": {
70
+ "types": "./src/compaction/*.ts",
71
+ "import": "./src/compaction/*.ts"
72
+ },
73
+ "./*": {
74
+ "types": "./src/*.ts",
75
+ "import": "./src/*.ts"
76
+ }
77
+ }
79
78
  }
package/src/agent-loop.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  * Agent loop that works with AgentMessage throughout.
3
3
  * Transforms to Message[] only at the LLM call boundary.
4
4
  */
5
-
6
5
  import {
7
6
  type AssistantMessage,
8
7
  type AssistantMessageEvent,
@@ -32,7 +31,11 @@ import {
32
31
  wrapInbandToolStream,
33
32
  } from "@linxiraos/pi-ai/dialect";
34
33
  import * as AIError from "@linxiraos/pi-ai/error";
35
- import { type CursorExecResolvedCarrier, kCursorExecResolved } from "@linxiraos/pi-ai/utils/block-symbols";
34
+ import {
35
+ type CursorExecResolvedCarrier,
36
+ copyCursorExecResolved,
37
+ kCursorExecResolved,
38
+ } from "@linxiraos/pi-ai/utils/block-symbols";
36
39
  import {
37
40
  createHarmonyAuditEvent,
38
41
  detectHarmonyLeakInAssistantMessage,
@@ -357,12 +360,18 @@ function snapshotAssistantContentBlock(block: AssistantContentBlock): AssistantC
357
360
  return { ...block, block: structuredCloneJSON(block.block) };
358
361
  case "fallback":
359
362
  return { ...block, from: { ...block.from }, to: { ...block.to } };
360
- case "toolCall":
361
- return {
363
+ case "toolCall": {
364
+ const snap = {
362
365
  ...block,
363
366
  arguments: structuredCloneJSON(block.arguments),
364
367
  providerMetadata: snapshotToolCallProviderMetadata(block.providerMetadata),
365
368
  };
369
+ // Object spread copies enumerable symbols in Bun, but the Cursor
370
+ // exec-resolved marker is load-bearing for skip-on-dispatch — copy
371
+ // it explicitly so a projector/snapshot path cannot drop it.
372
+ copyCursorExecResolved(snap, block);
373
+ return snap;
374
+ }
366
375
  }
367
376
  }
368
377
 
@@ -1,4 +1,4 @@
1
- The following is a summary of a branch that this conversation came back from:
1
+ Branch-return summary:
2
2
 
3
3
  <summary>
4
4
  {{summary}}
@@ -1,2 +1,2 @@
1
- The user explored a different conversation branch before returning here.
2
- Summary of that exploration:
1
+ User explored another conversation branch, then returned here.
2
+ Exploration summary:
@@ -1,9 +1,3 @@
1
- You MUST summarize what was done in this conversation, written like a pull request description.
2
-
3
- Rules:
4
- - MUST be 2-3 sentences max
5
- - MUST describe the changes made, not the process
6
- - NEVER mention running tests, builds, or other validation steps
7
- - NEVER explain what the user asked for
8
- - MUST write in first person (I added…, I fixed…)
9
- - NEVER ask questions
1
+ Summarize conversation changes as a pull request description.
2
+ MUST 2–3 sentences; first person (`I added…`, `I fixed…`); describe changes, not process.
3
+ NEVER mention tests, builds, or other validation steps; explain user request; ask questions.
@@ -1,4 +1,5 @@
1
- Another language model started to solve this problem and produced a summary of its thinking process. You also have access to the state of the tools that model used. You MUST build on the work already done and NEVER duplicate it. Here is that summary:
1
+ Prior model work/tool state available.
2
+ MUST build on prior work; NEVER duplicate prior work.
2
3
 
3
4
  <summary>
4
5
  {{summary}}
@@ -1,6 +1,6 @@
1
- This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.
1
+ Turn prefix too large; recent-work suffix retained.
2
2
 
3
- You MUST summarize the prefix to provide context for the retained suffix:
3
+ MUST summarize prefix for retained suffix:
4
4
 
5
5
  ## Original Request
6
6
 
@@ -12,6 +12,6 @@ You MUST summarize the prefix to provide context for the retained suffix:
12
12
  ## Context for Suffix
13
13
  - [Information needed to understand the retained recent work]
14
14
 
15
- You MUST output only the structured summary. You NEVER include extra text.
15
+ MUST output only the structured summary; NEVER extra text.
16
16
 
17
- You MUST be concise. You MUST preserve exact file paths, function names, error messages, and relevant tool outputs or command results if they appear. You MUST focus on what's needed to understand the kept suffix.
17
+ MUST concise. MUST preserve exact file paths, function names, error messages, relevant tool outputs, and command results if present. MUST focus on information needed to understand the retained suffix.
@@ -1,15 +1,18 @@
1
- You MUST incorporate the new messages above into the existing handoff summary in <previous-summary> tags, used by another LLM to resume the task.
2
- RULES:
3
- - MUST preserve all information from the previous summary
4
- - MUST add new progress, decisions, and context from new messages
5
- - MUST update Progress: move items from "In Progress" to "Done" when completed
6
- - MUST update "Next Steps" based on what was accomplished
7
- - MUST preserve exact file paths, function names, and error messages
8
- - You MAY remove anything no longer relevant
9
-
10
- IMPORTANT: If the new messages end with an unanswered question or request to the user, you MUST add it to Critical Context (replacing any previous pending question if answered).
11
-
12
- You MUST use this format (omit sections if not applicable):
1
+ Update existing handoff summary in <previous-summary> tags from new messages above for another LLM to resume.
2
+
3
+ MUST:
4
+ - preserve all previous-summary information; add new progress, decisions, context.
5
+ - Progress: move completed "In Progress" items to "Done".
6
+ - update "Next Steps" for completed work.
7
+ - preserve exact file paths, function names, error messages.
8
+ - MAY remove irrelevant content.
9
+ - If new messages end with an unanswered user question/request: add it to Critical Context; replace any previous pending question if answered.
10
+ - output only the structured summary; NEVER extra text.
11
+ - keep sections concise.
12
+ - preserve relevant tool outputs/command results.
13
+ - include mentioned repository state changes (branch, uncommitted changes).
14
+
15
+ Format (omit inapplicable sections):
13
16
 
14
17
  ## Goal
15
18
  [Preserve existing goals; add new ones if task expanded]
@@ -39,7 +42,3 @@ You MUST use this format (omit sections if not applicable):
39
42
 
40
43
  ## Additional Notes
41
44
  [Other important info not fitting above]
42
-
43
- You MUST output only the structured summary; you NEVER include extra text.
44
-
45
- Sections MUST be kept concise. You MUST preserve relevant tool outputs/command results. You MUST include repository state changes (branch, uncommitted changes) if mentioned.
@@ -1 +1 @@
1
- Output exceeded the available model context and was truncated
1
+ Output: exceeded available model context → truncated.
@@ -1,3 +1,3 @@
1
- Summarize conversations between users and AI coding assistants. Produce structured summaries in the exact specified format.
1
+ Summarize user–AI coding-assistant conversations in the exact specified structured format.
2
2
 
3
- NEVER continue the conversation. NEVER respond to questions in it. Output ONLY the structured summary.
3
+ NEVER continue the conversation or answer its questions. Output ONLY the structured summary.
@@ -52,11 +52,13 @@ export const DEFAULT_SHAKE_CONFIG: ShakeConfig = {
52
52
  };
53
53
 
54
54
  /**
55
- * Manual `/shake`: aggressive — drops every eligible region across history,
56
- * artifact recovery reads included (the user's full escape hatch).
55
+ * Manual `/shake`: aggressive — no savings threshold and drops eligible
56
+ * regions across history, artifact recovery reads included (the user's full
57
+ * escape hatch). Still keeps a small recent tail so it cannot strip the tool
58
+ * results the agent is currently working from (#7776).
57
59
  */
58
60
  export const AGGRESSIVE_SHAKE_CONFIG: ShakeConfig = {
59
- protectTokens: 0,
61
+ protectTokens: 4_000,
60
62
  minSavings: 0,
61
63
  protectedTools: ["skill", isSkillReadToolResult],
62
64
  fenceMinTokens: 400,
@@ -65,6 +67,10 @@ export const AGGRESSIVE_SHAKE_CONFIG: ShakeConfig = {
65
67
  /** Compaction dead-end rescue: aggressive reach, but artifact recovery reads stay protected. */
66
68
  export const RESCUE_SHAKE_CONFIG: ShakeConfig = {
67
69
  ...AGGRESSIVE_SHAKE_CONFIG,
70
+ // Rescue must be able to elide the newest oversized result even inside the
71
+ // manual preset's recent-tail window (#7776) — a dead-end recovery that
72
+ // cannot drop its blocker is not a recovery.
73
+ protectTokens: 0,
68
74
  protectedTools: [...AGGRESSIVE_SHAKE_CONFIG.protectedTools, isArtifactRecoveryToolResult],
69
75
  };
70
76
 
package/src/types.ts CHANGED
@@ -728,7 +728,17 @@ export type ToolLoadMode = "essential" | "discoverable";
728
728
  */
729
729
  export type ToolApprovalDecision =
730
730
  | ToolTier
731
- | { tier: ToolTier; reason?: string; override?: boolean; policy?: "allow" | "deny" | "prompt" };
731
+ | {
732
+ tier: ToolTier;
733
+ reason?: string;
734
+ override?: boolean;
735
+ policy?: "allow" | "deny" | "prompt";
736
+ /** User-policy key for this decision. When set, `tools.approval.<policyKey>`
737
+ * is consulted instead of `tools.approval.<tool.name>`. Lets a dispatcher
738
+ * tool (e.g. `write` for an `xd://` device call) scope user allow/deny/
739
+ * prompt policies to the tool it dispatches into. */
740
+ policyKey?: string;
741
+ };
732
742
  export type ToolApproval = ToolApprovalDecision | ((args: unknown) => ToolApprovalDecision);
733
743
 
734
744
  /**