@code-yeongyu/senpi 2026.8.9 → 2026.8.11
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 +84 -0
- package/dist/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +19 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin/cache-keepalive/index.d.ts +30 -0
- package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cache-keepalive/index.js +281 -0
- package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/index.js +9 -9
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +21 -8
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm.d.ts +11 -2
- package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm.js +16 -0
- package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +31 -1
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +2 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +115 -54
- package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/prompt.js +4 -4
- package/dist/core/extensions/builtin/goal/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/goal/store-changed-event.d.ts +8 -0
- package/dist/core/extensions/builtin/goal/store-changed-event.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal/store-changed-event.js +9 -0
- package/dist/core/extensions/builtin/goal/store-changed-event.js.map +1 -0
- package/dist/core/extensions/builtin/goal/wait-progress.js +5 -5
- package/dist/core/extensions/builtin/goal/wait-progress.js.map +1 -1
- package/dist/core/extensions/builtin/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/index.js +2 -0
- package/dist/core/extensions/builtin/index.js.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.d.ts +16 -0
- package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.js +15 -0
- package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +7 -8
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +3 -3
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +7 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +4 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +19 -2
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +50 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -4
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +0 -7
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +4 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +6 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +14 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/edit.d.ts +3 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +12 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/filesystem-policy.d.ts +10 -0
- package/dist/core/tools/filesystem-policy.d.ts.map +1 -0
- package/dist/core/tools/filesystem-policy.js +59 -0
- package/dist/core/tools/filesystem-policy.js.map +1 -0
- package/dist/core/tools/find.d.ts +3 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +15 -0
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +3 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +16 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts +3 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +11 -0
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +3 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +13 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +3 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +12 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/app-server/threads/goal-handlers.d.ts.map +1 -1
- package/dist/modes/app-server/threads/goal-handlers.js +7 -0
- package/dist/modes/app-server/threads/goal-handlers.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -6
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/extensions.md +76 -0
- package/docs/providers.md +23 -3
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +38 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/{resumption-channel.ts → wake-source-state.ts} +9 -9
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +7 -7
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +12 -11
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts +3 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +13 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +6 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +6 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.js +52 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/warm-prompt-cache.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/antml/recovery-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/antml/recovery-stream.js +120 -1
- package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/protocols/antml/recovery-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts +3 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js +17 -3
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-pty/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +6 -6
- package/dist/core/extensions/builtin/resumption-channel-event.d.ts +0 -13
- package/dist/core/extensions/builtin/resumption-channel-event.d.ts.map +0 -1
- package/dist/core/extensions/builtin/resumption-channel-event.js +0 -13
- package/dist/core/extensions/builtin/resumption-channel-event.js.map +0 -1
- package/dist/core/omo-native-detect.d.ts +0 -3
- package/dist/core/omo-native-detect.d.ts.map +0 -1
- package/dist/core/omo-native-detect.js +0 -58
- package/dist/core/omo-native-detect.js.map +0 -1
package/docs/extensions.md
CHANGED
|
@@ -1437,6 +1437,82 @@ pi.registerTool({
|
|
|
1437
1437
|
});
|
|
1438
1438
|
```
|
|
1439
1439
|
|
|
1440
|
+
### pi.registerFilesystemPolicy(policy)
|
|
1441
|
+
|
|
1442
|
+
Register a filesystem access policy for Senpi's built-in `read`, `write`,
|
|
1443
|
+
`edit`, `ls`, `find`, and `grep` tools. This is a **factory-time only** API: call
|
|
1444
|
+
it directly from the extension factory so the checker is compiled before the
|
|
1445
|
+
built-in tools are constructed.
|
|
1446
|
+
|
|
1447
|
+
The policy receives a canonical absolute target:
|
|
1448
|
+
|
|
1449
|
+
```typescript
|
|
1450
|
+
interface FilesystemPolicyRequest {
|
|
1451
|
+
operation: "read" | "enumerate" | "write";
|
|
1452
|
+
canonicalPath: string;
|
|
1453
|
+
toolName: string;
|
|
1454
|
+
}
|
|
1455
|
+
```
|
|
1456
|
+
|
|
1457
|
+
Operation mapping:
|
|
1458
|
+
|
|
1459
|
+
| Operation | Built-in tools |
|
|
1460
|
+
|---|---|
|
|
1461
|
+
| `read` | `read` |
|
|
1462
|
+
| `write` | `write`, `edit` |
|
|
1463
|
+
| `enumerate` | `ls`, `find`, `grep` |
|
|
1464
|
+
|
|
1465
|
+
Existing targets are resolved through filesystem symlinks. A missing write
|
|
1466
|
+
target is resolved through its nearest existing real parent, then its missing
|
|
1467
|
+
suffix is reattached. The policy runs after that resolution and immediately
|
|
1468
|
+
before the tool's target I/O. A denial is returned as a normal tool error with
|
|
1469
|
+
the policy's reason.
|
|
1470
|
+
|
|
1471
|
+
```typescript
|
|
1472
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
1473
|
+
import type { ExtensionAPI } from "@code-yeongyu/senpi";
|
|
1474
|
+
|
|
1475
|
+
function isContained(root: string, candidate: string): boolean {
|
|
1476
|
+
const pathFromRoot = relative(root, candidate);
|
|
1477
|
+
return pathFromRoot === "" || (
|
|
1478
|
+
pathFromRoot !== ".." &&
|
|
1479
|
+
!pathFromRoot.startsWith(`..${sep}`) &&
|
|
1480
|
+
!isAbsolute(pathFromRoot)
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
export default function (pi: ExtensionAPI) {
|
|
1485
|
+
const writableRoot = resolve(process.cwd());
|
|
1486
|
+
|
|
1487
|
+
pi.registerFilesystemPolicy({
|
|
1488
|
+
check({ operation, canonicalPath }) {
|
|
1489
|
+
if (operation !== "write" || isContained(writableRoot, canonicalPath)) {
|
|
1490
|
+
return { allow: true };
|
|
1491
|
+
}
|
|
1492
|
+
return {
|
|
1493
|
+
allow: false,
|
|
1494
|
+
reason: `Writes are limited to ${writableRoot}`,
|
|
1495
|
+
};
|
|
1496
|
+
},
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
```
|
|
1500
|
+
|
|
1501
|
+
Policies run in extension load and registration order. Every policy must allow a
|
|
1502
|
+
request; the first denial wins. A thrown policy error also stops the tool before
|
|
1503
|
+
its target I/O.
|
|
1504
|
+
|
|
1505
|
+
An optional `deniedRoots: readonly string[]` field exposes metadata for future
|
|
1506
|
+
inherited process sandbox support. Use canonical absolute roots. The current
|
|
1507
|
+
runtime does not interpret this metadata and does not apply filesystem policies
|
|
1508
|
+
to `bash` or descendants; command-string rewriting is not a filesystem security
|
|
1509
|
+
boundary.
|
|
1510
|
+
|
|
1511
|
+
This enforcement is separate from `tool_call` permission and approval hooks.
|
|
1512
|
+
An unrestricted approval mode, or a hook that returns `{ block: false }`, cannot
|
|
1513
|
+
bypass the built-in executor check. Custom extension tools are not covered
|
|
1514
|
+
automatically and must call their own policy if they access the filesystem.
|
|
1515
|
+
|
|
1440
1516
|
### pi.registerMcpServer(name, config)
|
|
1441
1517
|
|
|
1442
1518
|
Register an MCP server that the agent can use. This is a **factory-time only**
|
package/docs/providers.md
CHANGED
|
@@ -88,8 +88,18 @@ If your Claude Pro/Max subscription usage through `claude-sdk-oauth` feels unexp
|
|
|
88
88
|
1. **Upgrade to v2026.8.3 or later.** Resume-first session continuity (#634-637) landed on 2026-08-03. On older builds, every turn after a divergence (compaction, abort, model switch, restart, failover) re-sends the entire conversation, which is the dominant token-burn mechanism.
|
|
89
89
|
2. **Check which lane you are on.** The `ambient` lane (default) inherits the environment. `oauth-slots` and `config-dir` are managed lanes set via `SENPI_CLAUDE_SDK_OAUTH_TOKEN_INJECTION`. The `config-dir` lane keeps each account's credentials in its own `CLAUDE_CONFIG_DIR`; no official SDK API moves a transcript across roots, so account failover on that lane always flattens (re-sends the full history) — this is a declared residual, not a bug.
|
|
90
90
|
3. **Read the continuity observations.** Tail the session log and filter for `flatten` — each `flatten` line means the lane re-sent the whole conversation and lost prompt-cache hits. A healthy conversation shows one `bootstrap` followed by `delta` lines. Common flatten reasons: `transcript_missing`, `registry_miss`, `resume_initialization_failed`, `cross_root_unsupported` (config-dir only).
|
|
91
|
-
4. **Prompt-cache retention.**
|
|
92
|
-
|
|
91
|
+
4. **Prompt-cache retention.** Effective cache TTL depends on which lane you are on:
|
|
92
|
+
|
|
93
|
+
| Lane | Effective TTL | Who controls it | How to override |
|
|
94
|
+
| --- | --- | --- | --- |
|
|
95
|
+
| Claude SDK OAuth (subscription, `claude-sdk-oauth`) | 5 minutes | The Claude SDK owns `cache_control`; senpi cannot add breakpoints. senpi reports 300s for this lane so cache-aware budgets (tool waits, goal timing) size themselves correctly. | Not overridable |
|
|
96
|
+
| Direct Anthropic API (`api.anthropic.com`, API key or OAuth token) | 1 hour | senpi defaults retention to `long` on this lane. Note: Anthropic's own API default is 5 minutes; the 1h TTL is senpi's choice, and 1h cache writes cost 2x base input vs 1.25x for 5m ([Anthropic prompt caching](https://docs.claude.com/en/docs/build-with-claude/prompt-caching)). | `PI_CACHE_RETENTION`, `cacheRetention` |
|
|
97
|
+
| Anthropic-compatible providers (kimi-coding, fireworks, gateways) | 5 minutes | The 1h TTL is gated on the native `api.anthropic.com` base URL, so these lanes stay short. | `cacheRetention` |
|
|
98
|
+
|
|
99
|
+
Override precedence: `cacheRetention` in `models.json` / the model catalog wins over everything. `PI_CACHE_RETENTION=long` selects long; any other set value forces short; unset falls back to the lane default above.
|
|
100
|
+
5. **Goal-monitor timing.** The goal monitor's continuation backstop is derived from the model's cache-safe wait (TTL minus `promptCache.safetyBufferSeconds`, default 30), capped by `promptCache.goalBackstopMaxSeconds` (default 3570), instead of a fixed 4 minutes. On 5m lanes that means wakes every ~4m30s; on the 1h direct-API lane, up to 59m30s. Cache-warm notices show which warm iteration you are on.
|
|
101
|
+
6. **Wake sources that hold the goal backstop.** Anything that can wake a parked session publishes a `wake_source_state` event (`{source, activeCount}`): terminal monitors (`terminal-monitors`), background bash sessions including auto-detached and killed ones (`terminal-background-sessions`), detached `eval` cells (`senpi-codemode`), and omo-senpi background task children plus owned team members (`senpi-task`). The goal extension sums every source, so a goal waits inside the prompt-cache TTL while ANY of them is on duty instead of continuing immediately. The legacy `terminal_monitor_state` event is still emitted for external consumers and is folded onto the same `terminal-monitors` count.
|
|
102
|
+
7. **Directive-block deduplication.** As of v2026.8.4, the flatten serialization collapses repeated `<ultrawork-mode>` directive blocks to a single copy, preventing the issue-#494 scenario where duplicated ~17KB directive blocks consumed up to 73% of the re-sent prompt. The continuity observation reports how many were collapsed and the payload size.
|
|
93
103
|
|
|
94
104
|
### GitHub Copilot
|
|
95
105
|
|
|
@@ -108,6 +118,16 @@ If your Claude Pro/Max subscription usage through `claude-sdk-oauth` feels unexp
|
|
|
108
118
|
- On remote/headless machines (e.g. over SSH) the browser cannot reach the loopback callback; paste the final redirect URL (or the authorization code) into the login prompt instead
|
|
109
119
|
- `OPENROUTER_API_KEY` remains available through **Use an API key**
|
|
110
120
|
|
|
121
|
+
#### OpenRouter prompt caching and sticky routing
|
|
122
|
+
|
|
123
|
+
senpi pins every OpenRouter request to the same upstream from the first call by sending the session id both as the `x-session-id` header and as the request-body `session_id` field. OpenRouter's precedence is body > header > `prompt_cache_key`, with a 256-character limit on the value; specifying a manual `provider.order` disables sticky routing entirely ([OpenRouter prompt caching](https://openrouter.ai/docs/guides/best-practices/prompt-caching)).
|
|
124
|
+
|
|
125
|
+
Upstreams that require explicit cache breakpoints get `cache_control` blocks through OpenRouter: model ids starting with `anthropic/`, `qwen/`, or `google/` (catalog ids carrying one leading `~` are matched after stripping it). Other upstreams (OpenAI, DeepSeek, Moonshot, Groq, Z.AI, Grok) cache automatically and need no markers.
|
|
126
|
+
|
|
127
|
+
#### Moonshot / Kimi prompt caching
|
|
128
|
+
|
|
129
|
+
senpi sends `prompt_cache_key` (set to the session id) on Moonshot requests. Kimi documents the field as required for the Kimi Code Plan and recommended for any multi-turn agent ([Kimi context caching](https://platform.kimi.ai/docs/guide/use-context-caching-feature-of-kimi-api)). Kimi reports cache hits as a flat `usage.cached_tokens` field, which senpi parses as cache-read tokens.
|
|
130
|
+
|
|
111
131
|
### Radius
|
|
112
132
|
|
|
113
133
|
Radius is a dynamic `pi-messages` gateway. `/login radius` stores OAuth tokens in `auth.json`; the gateway catalog is refreshed independently and cached in `models-store.json`. Custom Radius gateways can be declared in `models.json` with `"oauth": "radius"` and a gateway `baseUrl`.
|
|
@@ -296,7 +316,7 @@ Also supports ECS task roles (`AWS_CONTAINER_CREDENTIALS_*`) and IRSA (`AWS_WEB_
|
|
|
296
316
|
pi --provider amazon-bedrock --model us.anthropic.claude-sonnet-4-20250514-v1:0
|
|
297
317
|
```
|
|
298
318
|
|
|
299
|
-
Prompt caching is enabled automatically for Claude models whose ID contains a recognizable model name (base models and system-defined inference profiles). For application inference profiles (whose ARNs don't contain the model name), set `AWS_BEDROCK_FORCE_CACHE=1` to enable cache points:
|
|
319
|
+
Prompt caching is enabled automatically for Claude models whose ID contains a recognizable model name (base models and system-defined inference profiles). With `PI_CACHE_RETENTION=long`, the 1-hour cache TTL is only requested for Claude Opus 4.5, Sonnet 4.5, and Haiku 4.5, the models AWS documents as supporting it ([Bedrock prompt caching](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html)); every other cacheable Bedrock Claude model uses 5 minutes on both the wire and the TTL estimate. For application inference profiles (whose ARNs don't contain the model name), set `AWS_BEDROCK_FORCE_CACHE=1` to enable cache points:
|
|
300
320
|
|
|
301
321
|
```bash
|
|
302
322
|
export AWS_BEDROCK_FORCE_CACHE=1
|
|
@@ -12,6 +12,44 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.8.11] - 2026-08-10
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
## [2026.8.10] - 2026-08-10
|
|
28
|
+
|
|
29
|
+
### Breaking Changes
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
## [2026.8.9-2] - 2026-08-09
|
|
40
|
+
|
|
41
|
+
### Breaking Changes
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Detached eval cells now emit the shared `wake_source_state` event under source `senpi-codemode` when they detach, complete, stop, or are disposed. The optional host event passthrough remains guarded, synchronous cells emit no lifecycle transition, and per-cell snapshot metadata is preserved.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
### Removed
|
|
52
|
+
|
|
15
53
|
## [2026.8.9] - 2026-08-09
|
|
16
54
|
|
|
17
55
|
### Breaking Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-codemode",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.11",
|
|
4
4
|
"description": "Source-only senpi extension package for codemode evaluation tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@babel/parser": "8.0.4",
|
|
33
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.
|
|
33
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.11",
|
|
34
34
|
"typebox": "1.3.8"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@code-yeongyu/senpi": "2026.8.
|
|
37
|
+
"@code-yeongyu/senpi": "2026.8.11"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@code-yeongyu/senpi": "2026.8.
|
|
40
|
+
"@code-yeongyu/senpi": "2026.8.11"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"senpi",
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Wake-source liveness contract shared with the goal builtin.
|
|
3
3
|
*
|
|
4
|
-
* The goal builtin delays its hidden continuation while any live
|
|
5
|
-
*
|
|
4
|
+
* The goal builtin delays its hidden continuation while any live wake source
|
|
5
|
+
* can still wake the session. Each emitter publishes a full per-source
|
|
6
6
|
* snapshot on every liveness transition and once on `session_start`.
|
|
7
7
|
*
|
|
8
8
|
* The event name is duplicated here on purpose: senpi-codemode is a separate
|
|
9
9
|
* package and must not import across the packages/coding-agent boundary, so
|
|
10
10
|
* the sentinel test pins this literal to the exact cross-package contract.
|
|
11
11
|
*/
|
|
12
|
-
export const
|
|
12
|
+
export const WAKE_SOURCE_STATE_EVENT = "wake_source_state";
|
|
13
13
|
|
|
14
14
|
/** Source key identifying detached eval cells in the per-source snapshot. */
|
|
15
|
-
export const
|
|
15
|
+
export const SENPI_CODEMODE_WAKE_SOURCE = "senpi-codemode";
|
|
16
16
|
|
|
17
|
-
/** One
|
|
18
|
-
export interface
|
|
17
|
+
/** One detached cell as broadcast on the wake-source state event. */
|
|
18
|
+
export interface WakeSourceStateItem {
|
|
19
19
|
readonly id: string;
|
|
20
20
|
readonly description: string;
|
|
21
21
|
/** Epoch milliseconds when the channel registered; lets consumers render their own elapsed labels. */
|
|
22
22
|
readonly startedAtMs: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export interface
|
|
25
|
+
export interface WakeSourceState {
|
|
26
26
|
readonly source: string;
|
|
27
27
|
readonly activeCount: number;
|
|
28
|
-
readonly
|
|
28
|
+
readonly items?: readonly WakeSourceStateItem[];
|
|
29
29
|
}
|
|
@@ -7,7 +7,6 @@ import { defaultCodemodeSettings } from "./config/settings.ts";
|
|
|
7
7
|
import { EvalNotifier } from "./extension/eval-notifier.ts";
|
|
8
8
|
import { EVAL_CELLS_STATUS_KEY } from "./extension/eval-status.ts";
|
|
9
9
|
import { EvalStatusTicker } from "./extension/eval-status-ticker.ts";
|
|
10
|
-
import { RESUMPTION_CHANNEL_STATE_EVENT, type ResumptionChannelState } from "./extension/resumption-channel.ts";
|
|
11
10
|
import {
|
|
12
11
|
createExecuteTool,
|
|
13
12
|
createRuntime,
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
} from "./extension/runtime-factory.ts";
|
|
17
16
|
import type { CodemodeSessionManager, CreateCodemodeSessionManagerOptions } from "./extension/session-manager.ts";
|
|
18
17
|
import { SessionManagerProxy } from "./extension/session-manager-proxy.ts";
|
|
18
|
+
import { WAKE_SOURCE_STATE_EVENT, type WakeSourceState } from "./extension/wake-source-state.ts";
|
|
19
19
|
import { EvalDetachedCellManager, type EvalDetachedCellStatusEntry } from "./tool/detached-cell-manager.ts";
|
|
20
20
|
import { createEvalTool } from "./tool/eval-tool.ts";
|
|
21
21
|
import { renderEvalCall, renderEvalResult } from "./tool/render.ts";
|
|
@@ -39,7 +39,7 @@ export interface CodemodeExtensionAPI {
|
|
|
39
39
|
getActiveTools(): string[];
|
|
40
40
|
getAllTools(): readonly EvalSchemaToolInfo[];
|
|
41
41
|
sendUserMessage(content: string, options?: { deliverAs?: "steer" | "followUp" }): void;
|
|
42
|
-
/** Optional host event bus; a host without one turns
|
|
42
|
+
/** Optional host event bus; a host without one turns wake-source emission into a harmless no-op. */
|
|
43
43
|
events?: { emit(name: string, data: unknown): void };
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -82,8 +82,8 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
|
|
|
82
82
|
const showDetachedCells = (entries: readonly EvalDetachedCellStatusEntry[]): void => {
|
|
83
83
|
statusTicker.sync(entries);
|
|
84
84
|
};
|
|
85
|
-
const
|
|
86
|
-
pi.events?.emit(
|
|
85
|
+
const emitWakeSourceState = (state: WakeSourceState): void => {
|
|
86
|
+
pi.events?.emit(WAKE_SOURCE_STATE_EVENT, state);
|
|
87
87
|
};
|
|
88
88
|
const registerEvalForRuntime = (
|
|
89
89
|
runtime: SessionRuntime,
|
|
@@ -132,7 +132,7 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
|
|
|
132
132
|
cellManager: new EvalDetachedCellManager({
|
|
133
133
|
notifier,
|
|
134
134
|
onStatusChange: showDetachedCells,
|
|
135
|
-
|
|
135
|
+
onWakeSourceState: emitWakeSourceState,
|
|
136
136
|
...(options.now === undefined ? {} : { now: options.now }),
|
|
137
137
|
}),
|
|
138
138
|
executionTracker: manager,
|
|
@@ -163,12 +163,12 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
|
|
|
163
163
|
artifactsDir: runtime.artifactsDir,
|
|
164
164
|
notifier,
|
|
165
165
|
onStatusChange: showDetachedCells,
|
|
166
|
-
|
|
166
|
+
onWakeSourceState: emitWakeSourceState,
|
|
167
167
|
...(options.now === undefined ? {} : { now: options.now }),
|
|
168
168
|
});
|
|
169
169
|
activeCells = cellManager;
|
|
170
170
|
// The goal builtin clears its per-session counts at session_start; re-publish our snapshot.
|
|
171
|
-
cellManager.
|
|
171
|
+
cellManager.publishWakeSourceState();
|
|
172
172
|
activeRuntime = runtime;
|
|
173
173
|
activeModelId = ctx.model?.id;
|
|
174
174
|
registerEvalForRuntime(runtime, activeModelId, cellManager);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentToolResult } from "@code-yeongyu/senpi";
|
|
2
|
-
import {
|
|
2
|
+
import { SENPI_CODEMODE_WAKE_SOURCE, type WakeSourceState } from "../extension/wake-source-state.ts";
|
|
3
3
|
import { detachedNotificationSpillPath } from "./detached-cell-notification.ts";
|
|
4
4
|
import { currentDetachedResult, detachedErrorResult, snapshotDetachedCell } from "./detached-cell-snapshot.ts";
|
|
5
5
|
import {
|
|
@@ -60,14 +60,14 @@ export interface EvalDetachedCellManagerOptions {
|
|
|
60
60
|
readonly notifier?: EvalDetachedCellNotifier;
|
|
61
61
|
readonly onStatusChange?: (entries: readonly EvalDetachedCellStatusEntry[]) => void;
|
|
62
62
|
/** Receives a full per-source liveness snapshot on every detached-cell transition; used by the goal builtin. */
|
|
63
|
-
readonly
|
|
63
|
+
readonly onWakeSourceState?: (state: WakeSourceState) => void;
|
|
64
64
|
readonly now?: () => number;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export class EvalDetachedCellManager {
|
|
68
68
|
readonly #artifactsDir: string | undefined;
|
|
69
69
|
readonly #onStatusChange: ((entries: readonly EvalDetachedCellStatusEntry[]) => void) | undefined;
|
|
70
|
-
readonly #
|
|
70
|
+
readonly #onWakeSourceState: ((state: WakeSourceState) => void) | undefined;
|
|
71
71
|
readonly #cells = new Map<string, ManagedCell>();
|
|
72
72
|
readonly #detachedByLanguage = new Map<EvalLanguage, ManagedCell>();
|
|
73
73
|
readonly #notificationQueue: DetachedNotificationQueue;
|
|
@@ -76,7 +76,7 @@ export class EvalDetachedCellManager {
|
|
|
76
76
|
constructor(options: EvalDetachedCellManagerOptions = {}) {
|
|
77
77
|
this.#artifactsDir = options.artifactsDir;
|
|
78
78
|
this.#onStatusChange = options.onStatusChange;
|
|
79
|
-
this.#
|
|
79
|
+
this.#onWakeSourceState = options.onWakeSourceState;
|
|
80
80
|
this.#notificationQueue = new DetachedNotificationQueue(options.notifier, options.artifactsDir);
|
|
81
81
|
this.#now = options.now ?? Date.now;
|
|
82
82
|
}
|
|
@@ -160,6 +160,7 @@ export class EvalDetachedCellManager {
|
|
|
160
160
|
await Promise.allSettled(
|
|
161
161
|
detached.map(async (cell) => await this.stop(cell.cellId, "Session ended; detached eval cell cancelled")),
|
|
162
162
|
);
|
|
163
|
+
if (detached.length === 0) this.#emitWakeSourceState([]);
|
|
163
164
|
await this.#notificationQueue.flush();
|
|
164
165
|
}
|
|
165
166
|
|
|
@@ -168,8 +169,8 @@ export class EvalDetachedCellManager {
|
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
/** Re-publish the current snapshot; consumers reset their per-source counts at session_start. */
|
|
171
|
-
|
|
172
|
-
this.#
|
|
172
|
+
publishWakeSourceState(): void {
|
|
173
|
+
this.#emitWakeSourceState([...this.#detachedByLanguage.values()]);
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
#settle(
|
|
@@ -207,14 +208,14 @@ export class EvalDetachedCellManager {
|
|
|
207
208
|
...(cell.input.summary === undefined ? {} : { summary: cell.input.summary }),
|
|
208
209
|
})),
|
|
209
210
|
);
|
|
210
|
-
this.#
|
|
211
|
+
this.#emitWakeSourceState(liveCells);
|
|
211
212
|
}
|
|
212
213
|
|
|
213
|
-
#
|
|
214
|
-
this.#
|
|
215
|
-
source:
|
|
214
|
+
#emitWakeSourceState(liveCells: readonly ManagedCell[]): void {
|
|
215
|
+
this.#onWakeSourceState?.({
|
|
216
|
+
source: SENPI_CODEMODE_WAKE_SOURCE,
|
|
216
217
|
activeCount: liveCells.length,
|
|
217
|
-
|
|
218
|
+
items: liveCells.map((cell) => ({
|
|
218
219
|
id: cell.cellId,
|
|
219
220
|
description:
|
|
220
221
|
cell.input.summary === undefined || cell.input.summary.length === 0 ? cell.cellId : cell.input.summary,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@earendil-works/pi-agent-core",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "2026.8.
|
|
4
|
+
"version": "2026.8.11",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"prepublishOnly": "npm run build"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.
|
|
33
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.11",
|
|
34
34
|
"diff": "9.0.0",
|
|
35
35
|
"ignore": "7.0.6",
|
|
36
36
|
"typebox": "1.3.8",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
-
import type {
|
|
2
|
+
import type { MessageCreateParamsNonStreaming } from "@anthropic-ai/sdk/resources/messages.js";
|
|
3
|
+
import type { Context, Model, SimpleStreamOptions, StreamFunction, StreamOptions } from "../types.ts";
|
|
3
4
|
export { getAnthropicCompat } from "../utils/prompt-cache-ttl.ts";
|
|
4
5
|
export type AnthropicEffort = "low" | "medium" | "high" | "xhigh" | "max";
|
|
5
6
|
export type AnthropicThinkingDisplay = "summarized" | "omitted";
|
|
@@ -69,4 +70,5 @@ export interface AnthropicOptions extends StreamOptions {
|
|
|
69
70
|
}
|
|
70
71
|
export declare const stream: StreamFunction<"anthropic-messages", AnthropicOptions>;
|
|
71
72
|
export declare const streamSimple: StreamFunction<"anthropic-messages", SimpleStreamOptions>;
|
|
73
|
+
export declare function buildAnthropicWarmPromptCacheParams(model: Model<"anthropic-messages">, context: Context, options?: AnthropicOptions): MessageCreateParamsNonStreaming;
|
|
72
74
|
//# sourceMappingURL=anthropic-messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAGX,+BAA+B,EAK/B,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAKX,OAAO,EAGP,KAAK,EAGL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAOb,MAAM,aAAa,CAAC;AAkCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA6JlE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AAoEhE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AA4zBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA6ZzE,CAAC;AA0FF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA2ClF,CAAC;AAsIF,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACxB,+BAA+B,CAkBjC"}
|
|
@@ -1438,6 +1438,19 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
1438
1438
|
});
|
|
1439
1439
|
return { client, isOAuthToken: false };
|
|
1440
1440
|
}
|
|
1441
|
+
export function buildAnthropicWarmPromptCacheParams(model, context, options) {
|
|
1442
|
+
const params = buildParams(model, context, false, {
|
|
1443
|
+
...options,
|
|
1444
|
+
maxTokens: 0,
|
|
1445
|
+
thinkingEnabled: undefined,
|
|
1446
|
+
toolChoice: undefined,
|
|
1447
|
+
});
|
|
1448
|
+
const { stream: _stream, thinking: _thinking, output_config: _outputConfig, tool_choice: _toolChoice, ...nonStreaming } = params;
|
|
1449
|
+
return sanitizeAnthropicToolPairs({
|
|
1450
|
+
...nonStreaming,
|
|
1451
|
+
max_tokens: 0,
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1441
1454
|
function buildParams(model, context, isOAuthToken, options, unsignedThinkingReplay = getAnthropicCompat(model).unsignedThinkingReplay) {
|
|
1442
1455
|
const compat = getAnthropicCompat(model);
|
|
1443
1456
|
const { cacheControl } = getCacheControl(model, options?.cacheRetention ?? model.cacheRetention, options?.env);
|