@code-yeongyu/senpi 2026.10.24-3 → 2026.10.25-1

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 (94) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/bundle/chunks/{args-6T5EQVY3.js → args-JXULI4EH.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-VUKNGMDT.js → chunk-3LNSLJ7N.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-MRN6IKOQ.js → chunk-3VOG6WFU.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-AVLYZET7.js → chunk-7WAJCESX.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-4TRKE3ZC.js → chunk-BMYNMUVZ.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-LR6Z6V54.js → chunk-BTNYLNWZ.js} +2 -2
  8. package/dist/bundle/chunks/{chunk-JAXZOEJE.js → chunk-BWA4FVFW.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-AGQSRWNN.js → chunk-EHQXLHRB.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-I5NIJZDY.js → chunk-EYBDBR7V.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-YJA2B7FQ.js → chunk-FDL4CF6F.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-HCF5M6NA.js → chunk-GXY5JEI5.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-CYG4LS4S.js → chunk-J5P2VL3Y.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-DYWHYQUD.js → chunk-PXKF2RHD.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-H6IG2ITJ.js → chunk-QH2HSCMX.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-CZYADQ2E.js → chunk-SGDOZT6B.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-BZ75DRZB.js → chunk-U67ML6CQ.js} +4 -2
  18. package/dist/bundle/chunks/{chunk-DITSGW4R.js → chunk-URBJS24R.js} +75 -75
  19. package/dist/bundle/chunks/{chunk-CZR7C6CY.js → chunk-VOBD6FQG.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-SGATKHO4.js → chunk-Y5OTHMXO.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-GOB5BLDV.js → chunk-YDCRA5ES.js} +2 -2
  22. package/dist/bundle/chunks/{cli-main-BOYWBHOB.js → cli-main-TABBP55A.js} +1 -1
  23. package/dist/bundle/chunks/{dispatch-KQEGIXQW.js → dispatch-DEP4PEDZ.js} +3 -2
  24. package/dist/bundle/chunks/{help-fast-path-6PVN7WI5.js → help-fast-path-ZHZGXAUG.js} +1 -1
  25. package/dist/bundle/chunks/{host-command-7MAXSZIH.js → host-command-OQR4RDTM.js} +1 -1
  26. package/dist/bundle/chunks/{host-session-command-L2UTORL5.js → host-session-command-IGB27ZPJ.js} +1 -1
  27. package/dist/bundle/chunks/{interactive-mode-NVBFOE5R.js → interactive-mode-DGQNSXUA.js} +1 -1
  28. package/dist/bundle/chunks/{list-tips-BO5RP34W.js → list-tips-CREX4IXZ.js} +1 -1
  29. package/dist/bundle/chunks/{multi-session-host-3TVASSQD.js → multi-session-host-M7HVUMRH.js} +2 -2
  30. package/dist/bundle/chunks/{package-manager-cli-5BHVDVJC.js → package-manager-cli-FSR5P6YL.js} +1 -1
  31. package/dist/bundle/chunks/{role-BHA5ROSF.js → role-M2MTVACJ.js} +1 -1
  32. package/dist/bundle/chunks/{rotation-stream-RXMCJJUL.js → rotation-stream-U7YQMC7H.js} +1 -1
  33. package/dist/bundle/chunks/{rpc-mode-LPP46I7H.js → rpc-mode-SZAJNKKL.js} +1 -1
  34. package/dist/bundle/chunks/{schedule-command-QW67J57R.js → schedule-command-XBW4EOG5.js} +1 -1
  35. package/dist/bundle/chunks/{session-control-endpoint-QQBBY5AT.js → session-control-endpoint-WEF3XTJR.js} +1 -1
  36. package/dist/bundle/chunks/{session-picker-HIC5ZTE6.js → session-picker-KQUFWDQC.js} +1 -1
  37. package/dist/bundle/chunks/session-worker.js +98 -95
  38. package/dist/bundle/cli.js +2 -2
  39. package/dist/bundle/index.js +1 -1
  40. package/dist/bundle/rpc-entry.js +1 -1
  41. package/dist/bundle/runtime-manifest.json +1 -1
  42. package/dist/cli/args.d.ts +2 -0
  43. package/dist/cli/args.js +27 -0
  44. package/dist/core/agent-session.js +1 -1
  45. package/dist/core/extensions/builtin/anthropic-subscription/index.js +4 -2
  46. package/dist/core/extensions/builtin/anthropic-subscription/options.js +2 -1
  47. package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.js +9 -2
  48. package/dist/core/extensions/builtin/anthropic-subscription/session-binding.d.ts +2 -0
  49. package/dist/core/extensions/builtin/anthropic-subscription/session-commit-boundary.d.ts +6 -1
  50. package/dist/core/extensions/builtin/anthropic-subscription/session-commit-boundary.js +18 -7
  51. package/dist/core/extensions/builtin/anthropic-subscription/session-commit-divergence.d.ts +23 -0
  52. package/dist/core/extensions/builtin/anthropic-subscription/session-commit-divergence.js +107 -0
  53. package/dist/core/extensions/builtin/anthropic-subscription/session-cwd.d.ts +3 -0
  54. package/dist/core/extensions/builtin/anthropic-subscription/session-cwd.js +9 -0
  55. package/dist/core/extensions/builtin/anthropic-subscription/session-entry-annotations.d.ts +1 -1
  56. package/dist/core/extensions/builtin/anthropic-subscription/session-entry-annotations.js +3 -1
  57. package/dist/core/extensions/builtin/anthropic-subscription/session-observability.d.ts +3 -0
  58. package/dist/core/extensions/builtin/anthropic-subscription/session-observability.js +4 -0
  59. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-wiring.js +12 -7
  60. package/dist/core/extensions/builtin/anthropic-subscription/session-registry.d.ts +3 -1
  61. package/dist/core/extensions/builtin/anthropic-subscription/session-registry.js +3 -2
  62. package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +2 -0
  63. package/dist/core/extensions/builtin/anthropic-subscription/settings.d.ts +1 -1
  64. package/dist/core/extensions/builtin/anthropic-subscription/settings.js +3 -1
  65. package/dist/core/extensions/builtin/anthropic-subscription/stream.js +4 -1
  66. package/dist/core/extensions/builtin/btw/index.js +4 -1
  67. package/dist/core/extensions/builtin/compaction/lane-policy.js +3 -1
  68. package/dist/core/extensions/builtin/look-at/runner.js +1 -0
  69. package/dist/core/extensions/builtin/mcp/connection-types.d.ts +2 -0
  70. package/dist/core/extensions/builtin/mcp/connection.js +3 -0
  71. package/dist/core/extensions/builtin/mcp/elicitation.d.ts +5 -2
  72. package/dist/core/extensions/builtin/mcp/elicitation.js +57 -11
  73. package/dist/core/extensions/builtin/mcp/index.js +1 -1
  74. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +6 -9
  75. package/dist/core/extensions/builtin/mcp/service-connection.d.ts +1 -0
  76. package/dist/core/extensions/builtin/mcp/service-connection.js +1 -0
  77. package/dist/core/extensions/builtin/mcp/service.js +1 -0
  78. package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +1 -0
  79. package/dist/core/extensions/builtin/mcp/transport-sdk.js +5 -5
  80. package/dist/core/extensions/builtin/mcp/transport.d.ts +1 -0
  81. package/dist/core/extensions/builtin/mcp/transport.js +1 -0
  82. package/dist/core/external-admission.d.ts +4 -1
  83. package/dist/core/external-admission.js +18 -2
  84. package/dist/core/lifecycle-session-binding.js +20 -0
  85. package/dist/core/sdk.js +1 -0
  86. package/dist/core/session-spawned-by.d.ts +4 -0
  87. package/dist/core/session-spawned-by.js +16 -0
  88. package/dist/core/skill-access.js +15 -3
  89. package/dist/core/skill-discovery.d.ts +2 -0
  90. package/dist/core/skill-discovery.js +1 -1
  91. package/dist/main.js +7 -2
  92. package/dist/modes/rpc/session-router-open-profile.js +2 -10
  93. package/docs/usage.md +14 -0
  94. package/package.json +6 -6
@@ -54,6 +54,13 @@ export class ExternalAdmission {
54
54
  this.emittedListeners.add(listener);
55
55
  return () => this.emittedListeners.delete(listener);
56
56
  }
57
+ /** Durable custody will replay this delivery: reserve its id before asynchronous recovery or gateway admission. */
58
+ restoreQueued(deliveryId, isOwned) {
59
+ if (this.pending.has(deliveryId) || this.emitted.has(deliveryId) || this.failed.has(deliveryId))
60
+ return;
61
+ // Recovery chooses the payload's queue lane; this reservation only tracks durable ownership.
62
+ this.pending.set(deliveryId, isOwned);
63
+ }
57
64
  /**
58
65
  * From here on every `admit` throws `reason` and changes nothing: the runtime is being handed to
59
66
  * another writer, so the delivery stays with its sender and is delivered there instead.
@@ -79,6 +86,7 @@ export class ExternalAdmission {
79
86
  throw new Error(this.closedReason);
80
87
  const turn_epoch = this.turnEpochValue;
81
88
  const id = input.delivery_id;
89
+ this.releaseRestored();
82
90
  if (this.pending.has(id) || this.emitted.has(id) || this.failed.has(id)) {
83
91
  return { kind: "already_admitted", turn_epoch };
84
92
  }
@@ -112,6 +120,7 @@ export class ExternalAdmission {
112
120
  return { kind: "queued", turn_epoch };
113
121
  }
114
122
  list() {
123
+ this.releaseRestored();
115
124
  const deliveries = { pending: [...this.pending.keys()], emitted: [...this.emitted] };
116
125
  if (this.failed.size === 0)
117
126
  return deliveries;
@@ -149,10 +158,17 @@ export class ExternalAdmission {
149
158
  if (this.fileTakesWrites)
150
159
  this.failed.clear();
151
160
  }
152
- /** The runtime's queues were cleared: queued deliveries are no longer held. */
161
+ /** Cleared carriers are no longer held; custody not yet enqueued for recovery still owns its delivery. */
153
162
  dropQueued() {
163
+ this.releaseRestored();
164
+ for (const [id, lane] of this.pending) {
165
+ if (lane !== "start" && typeof lane !== "function")
166
+ this.pending.delete(id);
167
+ }
168
+ }
169
+ releaseRestored() {
154
170
  for (const [id, lane] of this.pending) {
155
- if (lane !== "start")
171
+ if (typeof lane === "function" && !lane())
156
172
  this.pending.delete(id);
157
173
  }
158
174
  }
@@ -2,6 +2,7 @@ import { collectGoalLifecycleFacts, onGoalLifecycleChange } from "./extensions/b
2
2
  import { goalStoreRef } from "./extensions/builtin/goal/store-ref.js";
3
3
  import { collectTurnEndLivenessInput } from "./extensions/builtin/goal/turn-end-liveness-report.js";
4
4
  import { StaleExtensionContextError } from "./extensions/stale-context-error.js";
5
+ import { deliveryIdOf } from "./external-admission.js";
5
6
  import { consumeLifecycleDelivery, lifecycleDeliveryEntries, messageTicket } from "./lifecycle-delivery.js";
6
7
  import { readLifecycleGoal } from "./lifecycle-goal.js";
7
8
  import { lifecycleHistory } from "./lifecycle-history.js";
@@ -41,6 +42,25 @@ export class LifecycleSessionBinding {
41
42
  },
42
43
  readRuntime: () => readLifecycleRuntimeFacts(session),
43
44
  });
45
+ // A crashed receiver may have durable custody but no transcript entry yet. Recovery owns that
46
+ // delivery already; register it before session_start can let the gateway enqueue a second carrier.
47
+ const deliveryEntries = session.sessionManager.reduceHistory(lifecycleDeliveryEntries);
48
+ for (const record of this.controller.obligations.active) {
49
+ if (record.state !== "queued" || !record.payload || !record.ticketId)
50
+ continue;
51
+ const ticketId = record.ticketId;
52
+ const message = { role: "custom", ...record.payload.message, timestamp: 0 };
53
+ const deliveryId = deliveryIdOf(message);
54
+ if (deliveryId === undefined)
55
+ continue;
56
+ if (deliveryEntries.has(ticketId))
57
+ session.externalAdmission.observePersisted(message);
58
+ else
59
+ session.externalAdmission.restoreQueued(deliveryId, () =>
60
+ // Consumption writes the entry before message_end observes it in external admission.
61
+ session.sessionManager.reduceHistory(lifecycleDeliveryEntries).has(ticketId) ||
62
+ this.controller.obligations.active.some((current) => current.ticketId === ticketId && current.state === "queued"));
63
+ }
44
64
  this.unsubscribeHuman = bindLifecycleHumanInput(session, (event) => this.controller.humanInput(event.at));
45
65
  this.unsubscribeAdmission = session.externalAdmission.onInputsSettled(() => this.controller.admissionSettled());
46
66
  this.unsubscribeGoal = onGoalLifecycleChange(session.sessionManager, (goal) => {
package/dist/core/sdk.js CHANGED
@@ -315,6 +315,7 @@ export async function createAgentSession(options = {}) {
315
315
  const serviceTier = serviceTierForProvider(model.provider, options?.serviceTier ?? resolveRequestServiceTier(model));
316
316
  return modelRuntime.streamSimple(model, context, {
317
317
  ...options,
318
+ cwd,
318
319
  serviceTier,
319
320
  timeoutMs,
320
321
  websocketConnectTimeoutMs,
@@ -0,0 +1,4 @@
1
+ import type { SpawnedBy } from "./session-lifecycle-types.ts";
2
+ /** The shared CLI / open_session boundary contract; IDs are opaque, non-empty strings. */
3
+ export declare function isSpawnedBy(value: unknown): value is SpawnedBy;
4
+ //# sourceMappingURL=session-spawned-by.d.ts.map
@@ -0,0 +1,16 @@
1
+ /** The shared CLI / open_session boundary contract; IDs are opaque, non-empty strings. */
2
+ export function isSpawnedBy(value) {
3
+ return (typeof value === "object" &&
4
+ value !== null &&
5
+ !Array.isArray(value) &&
6
+ "parentSessionId" in value &&
7
+ typeof value.parentSessionId === "string" &&
8
+ value.parentSessionId.length > 0 &&
9
+ "rootSessionId" in value &&
10
+ typeof value.rootSessionId === "string" &&
11
+ value.rootSessionId.length > 0 &&
12
+ "taskId" in value &&
13
+ typeof value.taskId === "string" &&
14
+ value.taskId.length > 0);
15
+ }
16
+ //# sourceMappingURL=session-spawned-by.js.map
@@ -22,15 +22,27 @@ export function insideSkillRoot(path, root) {
22
22
  export function buildSkillAllowedSet(sources) {
23
23
  const roots = new Set();
24
24
  const files = new Set();
25
- for (const source of sources) {
25
+ // Package resolution retains the selected root on every discovered skill.
26
+ // Walk each spelling once, without collapsing differently ignored aliases.
27
+ const realpaths = new Map();
28
+ const realpath = (path) => {
29
+ let canonical = realpaths.get(path);
30
+ if (canonical === undefined) {
31
+ canonical = realpathSync.native(path);
32
+ realpaths.set(path, canonical);
33
+ }
34
+ return canonical;
35
+ };
36
+ for (const source of new Set(sources)) {
26
37
  try {
27
- const canonical = realpathSync.native(source);
38
+ const canonical = realpath(source);
28
39
  if (statSync(canonical).isFile()) {
29
40
  files.add(canonical);
30
41
  continue;
31
42
  }
32
43
  collectSkillEntries(source, "pi", undefined, undefined, {
33
- onDirectory: (path) => roots.add(realpathSync.native(path)),
44
+ realpath,
45
+ onDirectory: (path) => roots.add(realpath(path)),
34
46
  });
35
47
  }
36
48
  catch {
@@ -9,6 +9,8 @@ export interface SkillWalkOptions {
9
9
  readonly allowedSet?: SkillAllowedSet;
10
10
  readonly ancestry?: ReadonlySet<string>;
11
11
  readonly onDirectory?: (path: string) => void;
12
+ /** Optional cache owned by a single discovery pass, never retained on a Skill. */
13
+ readonly realpath?: (path: string) => string;
12
14
  }
13
15
  export declare function shouldSkipSkillWalkDirectoryName(name: string): boolean;
14
16
  /**
@@ -117,7 +117,7 @@ export function collectSkillEntries(dir, mode, ignoreMatcher, rootDir, options =
117
117
  let canonical;
118
118
  try {
119
119
  authorizeSkillPath(dir, options.provenance, options.allowedSet, true);
120
- canonical = realpathSync.native(dir);
120
+ canonical = (options.realpath ?? realpathSync.native)(dir);
121
121
  }
122
122
  catch (error) {
123
123
  // Leave a blocked root as a candidate so the resource loader retains its
package/dist/main.js CHANGED
@@ -401,8 +401,11 @@ async function sessionCwdOrUndefined(sessionFile) {
401
401
  }
402
402
  }
403
403
  export async function createSessionManager(parsed, cwd, sessionDir, settingsManager, appMode) {
404
+ if (parsed.spawnedBy && (parsed.session || parsed.resume || parsed.continue || parsed.fork || parsed.rebind)) {
405
+ console.error(chalk.yellow("Warning: --spawned-by is ignored when resuming or forking a session."));
406
+ }
404
407
  if (parsed.noSession || parsed.help || parsed.listModels !== undefined || parsed.listTips) {
405
- return SessionManager.inMemory(cwd, parsed.sessionId !== undefined ? { id: parsed.sessionId } : undefined);
408
+ return SessionManager.inMemory(cwd, { id: parsed.sessionId, spawnedBy: parsed.spawnedBy });
406
409
  }
407
410
  if (parsed.fork) {
408
411
  if (parsed.sessionId) {
@@ -533,11 +536,13 @@ export async function createSessionManager(parsed, cwd, sessionDir, settingsMana
533
536
  if (parsed.sessionId) {
534
537
  const existingSession = await findLocalSessionByExactId(parsed.sessionId, cwd, sessionDir);
535
538
  if (existingSession) {
539
+ if (parsed.spawnedBy)
540
+ console.error(chalk.yellow("Warning: --spawned-by is ignored when resuming an existing session."));
536
541
  return SessionManager.open(existingSession.path, sessionDir);
537
542
  }
538
543
  console.error(chalk.yellow(`Warning: No project session found with id '${parsed.sessionId}'; creating a new session with that id.`));
539
544
  }
540
- return SessionManager.create(cwd, sessionDir, { id: parsed.sessionId });
545
+ return SessionManager.create(cwd, sessionDir, { id: parsed.sessionId, spawnedBy: parsed.spawnedBy });
541
546
  }
542
547
  function buildSessionOptions(parsed, scopedModels, hasExistingSession, modelRuntime, settingsManager) {
543
548
  const options = {};
@@ -1,3 +1,4 @@
1
+ import { isSpawnedBy } from "../../core/session-spawned-by.js";
1
2
  import { HOST_DAEMON_DIR_ENV } from "./host-daemon-paths.js";
2
3
  import { hostInstanceId } from "./protocol-identity.js";
3
4
  import { sessionAutoTitleError, sessionBrowserEngineError, sessionContextError, sessionKindError, sessionPromptSurfaceError, sessionRetryFallbackError, } from "./rpc-input-validation.js";
@@ -6,16 +7,7 @@ import { error } from "./session-router-errors.js";
6
7
  /** `open_session.spawnedBy` must name the parent, root and task when present; the open answers `invalid_spawned_by` otherwise. */
7
8
  export function invalidSpawnedBy(command) {
8
9
  const ancestry = command.spawnedBy;
9
- return (ancestry !== undefined &&
10
- (typeof ancestry !== "object" ||
11
- ancestry === null ||
12
- Array.isArray(ancestry) ||
13
- typeof ancestry.parentSessionId !== "string" ||
14
- ancestry.parentSessionId.length === 0 ||
15
- typeof ancestry.rootSessionId !== "string" ||
16
- ancestry.rootSessionId.length === 0 ||
17
- typeof ancestry.taskId !== "string" ||
18
- ancestry.taskId.length === 0));
10
+ return ancestry !== undefined && !isSpawnedBy(ancestry);
19
11
  }
20
12
  export function validatedOpenProfile(host, command, options) {
21
13
  // A parked session's reopen replays what its park recorded; a field it left empty stays empty
package/docs/usage.md CHANGED
@@ -232,12 +232,26 @@ cat README.md | senpi -p "Summarize this text"
232
232
  | `-c`, `--continue` | Continue the most recent session |
233
233
  | `-r`, `--resume` | Browse and select a session |
234
234
  | `--session <path\|id>` | Use a specific session file or partial UUID |
235
+ | `--spawned-by <json>` | Set parent, root, and task ancestry on a fresh standalone session |
235
236
  | `--fork <path\|id>` | Fork a session file or partial UUID into a new session |
236
237
  | `--rebind <path\|id>` | Move a session of this repository, recorded at another path (moved or re-cloned), into this directory and continue it |
237
238
  | `--session-dir <dir>` | Custom session storage directory |
238
239
  | `--no-session` | Ephemeral mode; do not save |
239
240
  | `--name <name>`, `-n <name>` | Set session display name at startup |
240
241
 
242
+ To start a standalone child session, pass one JSON object:
243
+
244
+ ```bash
245
+ senpi --mode rpc --spawned-by '{"parentSessionId":"parent","rootSessionId":"root","taskId":"task"}'
246
+ ```
247
+
248
+ All three fields must be non-empty strings; malformed input exits before runtime creation.
249
+ The ancestry is stored in the session header, so `get_state.lifecycle.role` is `child` before
250
+ the first prompt. A fresh session created with `--session-id` also accepts it. Resuming an
251
+ existing ID preserves its header and warns that the flag is ignored. The flag is likewise
252
+ ignored with a warning with `--session`, `--resume`, `--continue`, `--rebind`, or `--fork`.
253
+ Without the flag, new sessions and ordinary forks start as roots.
254
+
241
255
  ### Tool Options
242
256
 
243
257
  | Option | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi",
3
- "version": "2026.10.24-3",
3
+ "version": "2026.10.25-1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -71,9 +71,9 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@earendil-works/chord": "0.99.1",
74
- "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.24-3",
75
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.24-3",
76
- "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.24-3",
74
+ "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.25-1",
75
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.25-1",
76
+ "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.25-1",
77
77
  "@silvia-odwyer/photon-node": "0.3.4",
78
78
  "chalk": "6.0.0",
79
79
  "cross-spawn": "7.0.6",
@@ -92,8 +92,8 @@
92
92
  "yaml": "2.9.1",
93
93
  "@anthropic-ai/claude-agent-sdk": "0.3.306",
94
94
  "@anthropic-ai/sdk": "0.127.0",
95
- "@code-yeongyu/senpi-codemode": "2026.10.24-3",
96
- "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.24-3",
95
+ "@code-yeongyu/senpi-codemode": "2026.10.25-1",
96
+ "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.25-1",
97
97
  "@modelcontextprotocol/sdk": "1.32.1",
98
98
  "@mozilla/readability": "0.6.0",
99
99
  "linkedom": "0.18.13",