@code-yeongyu/senpi 2026.10.16-3 → 2026.10.16-4

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 (99) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/bundle/chunks/{chunk-I5ZJEXTM.js → chunk-2NO5ZOYE.js} +6 -6
  3. package/dist/bundle/chunks/{chunk-4R4PRTF5.js → chunk-CJRE5LLR.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-X5VCHHXR.js → chunk-GY5O7NIQ.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-AVALXFDW.js → chunk-JD4CV3GH.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-SSEGIXRL.js → chunk-KUQ3K33L.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-FMYLLJBQ.js → chunk-KWC3MCLP.js} +1 -1
  8. package/dist/bundle/chunks/chunk-MLL7AGVM.js +3 -0
  9. package/dist/bundle/chunks/chunk-PAJKEG2L.js +8 -0
  10. package/dist/bundle/chunks/{chunk-DPRV3FR7.js → chunk-PWHAOWNH.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-HJPX7PBL.js → chunk-XCRGEMY2.js} +125 -118
  12. package/dist/bundle/chunks/{cli-main-JVPFJGP5.js → cli-main-I6JF2GPS.js} +1 -1
  13. package/dist/bundle/chunks/{help-fast-path-SVCMHCYG.js → help-fast-path-APSHUDY6.js} +1 -1
  14. package/dist/bundle/chunks/{host-session-command-46PTZGIB.js → host-session-command-BUJUJHDA.js} +1 -1
  15. package/dist/bundle/chunks/{interactive-mode-BROZRY34.js → interactive-mode-4Q3TIXOZ.js} +1 -1
  16. package/dist/bundle/chunks/{multi-session-host-SMQR5SCJ.js → multi-session-host-XEZB2XVR.js} +5 -4
  17. package/dist/bundle/chunks/{package-manager-cli-2QFJY2AS.js → package-manager-cli-YWZHOZOY.js} +1 -1
  18. package/dist/bundle/chunks/{rotation-stream-QX7YSKDU.js → rotation-stream-HY3JPQCX.js} +1 -1
  19. package/dist/bundle/chunks/{rpc-mode-Z45T3NZO.js → rpc-mode-SLWXVXXE.js} +1 -1
  20. package/dist/bundle/chunks/{schedule-command-2RHSWBQO.js → schedule-command-JRWZGEDU.js} +1 -1
  21. package/dist/bundle/chunks/{session-control-endpoint-H7FFWF2A.js → session-control-endpoint-E2ZSEWXW.js} +1 -1
  22. package/dist/bundle/chunks/session-picker-5IZY7E3G.js +2 -0
  23. package/dist/bundle/chunks/session-worker.js +243 -235
  24. package/dist/bundle/cli.js +2 -2
  25. package/dist/bundle/index.js +1 -1
  26. package/dist/bundle/rpc-entry.js +1 -1
  27. package/dist/bundle/runtime-manifest.json +1 -1
  28. package/dist/core/agent-session-runtime.d.ts +15 -0
  29. package/dist/core/agent-session-runtime.js +53 -2
  30. package/dist/core/agent-session.d.ts +12 -0
  31. package/dist/core/agent-session.js +42 -1
  32. package/dist/core/context-files.d.ts +21 -0
  33. package/dist/core/context-files.js +175 -0
  34. package/dist/core/diagnostics.d.ts +1 -0
  35. package/dist/core/extensions/builtin/anthropic-subscription/account-command.js +26 -3
  36. package/dist/core/extensions/builtin/anthropic-subscription/account-management.d.ts +5 -0
  37. package/dist/core/extensions/builtin/anthropic-subscription/account-management.js +40 -0
  38. package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
  39. package/dist/core/extensions/builtin/anthropic-subscription/accounts.js +1 -1
  40. package/dist/core/extensions/builtin/anthropic-subscription/affinity.js +1 -1
  41. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +1 -0
  42. package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +10 -5
  43. package/dist/core/extensions/builtin/anthropic-subscription/failover.js +158 -17
  44. package/dist/core/extensions/builtin/anthropic-subscription/usage-limit-block.d.ts +13 -0
  45. package/dist/core/extensions/builtin/anthropic-subscription/usage-limit-block.js +89 -0
  46. package/dist/core/extensions/builtin/permission-system/always-scope.d.ts +34 -0
  47. package/dist/core/extensions/builtin/permission-system/always-scope.js +77 -0
  48. package/dist/core/extensions/builtin/permission-system/index.js +3 -1
  49. package/dist/core/extensions/builtin/permission-system/parsers.d.ts +1 -1
  50. package/dist/core/extensions/builtin/permission-system/parsers.js +24 -3
  51. package/dist/core/extensions/builtin/permission-system/prompt.js +5 -1
  52. package/dist/core/extensions/builtin/permission-system/service.js +1 -1
  53. package/dist/core/extensions/builtin/permission-system/types.d.ts +7 -0
  54. package/dist/core/package-manager.js +4 -32
  55. package/dist/core/project-trust.d.ts +8 -0
  56. package/dist/core/project-trust.js +12 -0
  57. package/dist/core/resource-loader.d.ts +5 -7
  58. package/dist/core/resource-loader.js +11 -84
  59. package/dist/core/retry-fallback/circuit-probes.d.ts +1 -0
  60. package/dist/core/retry-fallback/circuit-probes.js +3 -0
  61. package/dist/core/retry-fallback/circuit.d.ts +5 -0
  62. package/dist/core/retry-fallback/circuit.js +27 -4
  63. package/dist/core/retry-fallback/controller.d.ts +9 -4
  64. package/dist/core/retry-fallback/controller.js +21 -4
  65. package/dist/core/settings-manager.d.ts +15 -0
  66. package/dist/core/settings-manager.js +21 -1
  67. package/dist/core/skills-prompt.d.ts +18 -0
  68. package/dist/core/skills-prompt.js +131 -0
  69. package/dist/core/skills.d.ts +1 -9
  70. package/dist/core/skills.js +1 -59
  71. package/dist/core/trust-manager.d.ts +13 -4
  72. package/dist/core/trust-manager.js +52 -21
  73. package/dist/main.js +23 -4
  74. package/dist/modes/interactive/interactive-mode.js +10 -3
  75. package/dist/modes/rpc/abort-grace.d.ts +9 -0
  76. package/dist/modes/rpc/abort-grace.js +11 -0
  77. package/dist/modes/rpc/effect-commands.d.ts +0 -2
  78. package/dist/modes/rpc/effect-commands.js +2 -3
  79. package/dist/modes/rpc/host-handover-parked.d.ts +6 -0
  80. package/dist/modes/rpc/host-handover-parked.js +1 -0
  81. package/dist/modes/rpc/host-handover-reopen.js +11 -2
  82. package/dist/modes/rpc/rpc-client.d.ts +28 -2
  83. package/dist/modes/rpc/rpc-client.js +24 -11
  84. package/dist/modes/rpc/rpc-request-deadline.d.ts +12 -0
  85. package/dist/modes/rpc/rpc-request-deadline.js +15 -0
  86. package/dist/modes/rpc/session-command-router.js +7 -4
  87. package/dist/modes/rpc/session-registry-attach.js +6 -1
  88. package/dist/modes/rpc/session-registry-claims.d.ts +22 -2
  89. package/dist/modes/rpc/session-registry-claims.js +68 -0
  90. package/dist/modes/rpc/session-registry-opening.d.ts +23 -0
  91. package/dist/modes/rpc/session-registry-opening.js +57 -0
  92. package/dist/modes/rpc/session-registry.d.ts +1 -0
  93. package/dist/modes/rpc/session-registry.js +48 -60
  94. package/docs/rpc.md +6 -2
  95. package/docs/settings.md +13 -0
  96. package/package.json +6 -6
  97. package/dist/bundle/chunks/chunk-6MEETY7N.js +0 -8
  98. package/dist/bundle/chunks/chunk-TEGEPQIA.js +0 -3
  99. package/dist/bundle/chunks/session-picker-2O2BNBVE.js +0 -2
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The "opening" entry an `openSession` records once its path is reserved and claimed. Built in one
3
+ * synchronous step so the registry can give the reservation back if building it throws, instead of
4
+ * leaving a reserved path no entry backs (senpi#3485).
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { ProviderScope } from "@earendil-works/pi-ai/node/provider-scope";
8
+ import { createEntrySwitchSession } from "./session-registry-switch.js";
9
+ import { frozenProfile, sessionIdentity, } from "./session-registry-types.js";
10
+ export function buildOpeningEntry(input) {
11
+ const { profile, sessionPath, requestedDurableId, options } = input;
12
+ // Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):
13
+ // Create-only launch semantics mirror classic startup flags. A resumed
14
+ // session restores its persisted model and thinking level instead of being
15
+ // overridden by the new open_session request.
16
+ const isResume = sessionPath !== undefined && existsSync(sessionPath);
17
+ // Re-opening an existing session file is a resume, exactly like interactive
18
+ // /resume (AgentSessionRuntime.switchSession). Without the event the session
19
+ // starts with reason "startup" and every extension that only rebuilds state
20
+ // on a resume - the ask-user dangling-question hook - stays unreachable from
21
+ // the RPC restart path. A session created by this open stays "startup".
22
+ const sessionStartEvent = isResume
23
+ ? { type: "session_start", reason: "resume" }
24
+ : undefined;
25
+ const storedProfile = frozenProfile({ ...profile, ...(sessionPath ? { sessionPath } : {}) });
26
+ const runtimeProfile = isResume
27
+ ? frozenProfile({ ...storedProfile, creationModel: undefined, initialThinkingLevel: undefined })
28
+ : storedProfile;
29
+ let settleOpen;
30
+ const openCompletion = new Promise((resolve, reject) => {
31
+ settleOpen = { resolve, reject };
32
+ });
33
+ // A concurrent explicit open for the same path waits on this to attach, never to time it out.
34
+ openCompletion.catch(() => { });
35
+ const entry = {
36
+ state: "opening",
37
+ scope: new ProviderScope(),
38
+ profile: storedProfile,
39
+ ...sessionIdentity(storedProfile),
40
+ sessionPath,
41
+ reservationKey: sessionPath,
42
+ cwd: storedProfile.cwd,
43
+ attachments: 1,
44
+ retainOnDisconnect: options?.retainOnDisconnect === true,
45
+ lastCommandAt: input.now,
46
+ lifecycleMutex: Promise.resolve(),
47
+ openCompletion,
48
+ };
49
+ entry.switchSession = createEntrySwitchSession(entry, input.createRuntime, input.sync);
50
+ // Recorded before the first await so the synchronous collision guard sees an open that is still
51
+ // being built. `manager.getSessionId()` overwrites it later with the authoritative value, which on
52
+ // a resume is the header's id, not the requested one.
53
+ if (requestedDurableId !== undefined)
54
+ entry.durableSessionId = requestedDurableId;
55
+ return { entry, storedProfile, runtimeProfile, sessionStartEvent, settleOpen };
56
+ }
57
+ //# sourceMappingURL=session-registry-opening.js.map
@@ -5,6 +5,7 @@ export { frozenProfile, type OpenRpcSession, type RpcSessionEntry, type RpcSessi
5
5
  export declare class RpcSessionRegistry {
6
6
  private readonly entries;
7
7
  private readonly reservations;
8
+ private readonly pendingClaims;
8
9
  private readonly teardownHost;
9
10
  private nextHandle;
10
11
  private readonly options;
@@ -1,21 +1,19 @@
1
- import { existsSync } from "node:fs";
2
1
  import { isAbsolute } from "node:path";
3
- import { ProviderScope, runWithProviderScope } from "@earendil-works/pi-ai/node/provider-scope";
2
+ import { runWithProviderScope } from "@earendil-works/pi-ai/node/provider-scope";
4
3
  import { createAgentSessionRuntime } from "../../core/agent-session-runtime.js";
5
4
  import { assertValidSessionId, SessionManager } from "../../core/session-manager.js";
6
5
  import { ClientAdmissions } from "./client-admissions.js";
7
6
  import { EffectLedger } from "./effect-ledger.js";
8
- import { SESSION_PATH_RETRY_AFTER_MS } from "./host-reservations.js";
9
7
  import { createRegistryWarm } from "./host-warm.js";
10
8
  import { assertRequestedModelResolved } from "./open-session-model.js";
11
9
  import { refreshesSessionActivity } from "./session-command-activity.js";
12
10
  import { publishedSessionContext } from "./session-context-patch.js";
13
11
  import { canonicalSessionPath } from "./session-path-key.js";
14
12
  import { attachToOpenSession } from "./session-registry-attach.js";
15
- import { settleClosingReservation, syncRuntimeMetadata } from "./session-registry-claims.js";
13
+ import { settleClosingReservation, settlePendingClaim, syncRuntimeMetadata, takeCrossGenerationClaim, } from "./session-registry-claims.js";
16
14
  import { resolveMovedProfile } from "./session-registry-moved-path.js";
17
- import { createEntrySwitchSession } from "./session-registry-switch.js";
18
- import { frozenProfile, RpcSessionRegistryError, sessionIdentity, } from "./session-registry-types.js";
15
+ import { buildOpeningEntry } from "./session-registry-opening.js";
16
+ import { RpcSessionRegistryError, } from "./session-registry-types.js";
19
17
  import { beginSessionClose, closeMarkedSession, closeSession, releaseWithinGrace, } from "./session-teardown.js";
20
18
  export { frozenProfile, RpcSessionRegistryError, sessionIdentity, } from "./session-registry-types.js";
21
19
  /** Process-local lifecycle owner for multi-session RPC runtimes. */
@@ -23,6 +21,7 @@ export class RpcSessionRegistry {
23
21
  constructor(options) {
24
22
  this.entries = new Map();
25
23
  this.reservations = new Set();
24
+ this.pendingClaims = new Map();
26
25
  this.nextHandle = 0;
27
26
  if (options.createRuntime.prepare)
28
27
  this.warm = createRegistryWarm(options.createRuntime.prepare, {
@@ -85,8 +84,14 @@ export class RpcSessionRegistry {
85
84
  throw new RpcSessionRegistryError("session_id_in_use");
86
85
  }
87
86
  }
88
- if (sessionPath)
87
+ if (sessionPath) {
89
88
  await settleClosingReservation(this.entries.values(), sessionPath, this.closeGraceMs);
89
+ // Re-checked right before the reservation decision, with no await in between: a waiter woken
90
+ // by a FAILED claim must see the next open's claim, not a bare reservation (senpi#3485 review).
91
+ const deadline = Date.now() + this.closeGraceMs;
92
+ while (this.pendingClaims.has(sessionPath))
93
+ await settlePendingClaim(this.pendingClaims, sessionPath, Math.max(0, deadline - Date.now()));
94
+ }
90
95
  if (sessionPath && this.reservations.has(sessionPath)) {
91
96
  return attachToOpenSession(this.entries, sessionPath, profile, options, this.options.pathReservations, this.now());
92
97
  }
@@ -95,6 +100,7 @@ export class RpcSessionRegistry {
95
100
  reason: "session_not_held",
96
101
  action: "rediscover_holder",
97
102
  });
103
+ let claimSettled;
98
104
  if (sessionPath) {
99
105
  // Taken SYNCHRONOUSLY, before any await: a concurrent open for the same path must find the
100
106
  // reservation already held, not a window between the decision and the record of it.
@@ -104,62 +110,44 @@ export class RpcSessionRegistry {
104
110
  // Only AWAIT when there is a cross-generation claim to take: `await undefined` still costs a
105
111
  // microtask, and an embedded registry (no second generation) must reach the "opening" entry
106
112
  // in the same tick a caller that raced it would look, exactly as it did before generations.
107
- const holder = this.options.pathReservations
108
- ? await this.options.pathReservations.claim(sessionPath)
109
- : undefined;
110
- if (holder) {
111
- this.reservations.delete(sessionPath);
112
- throw new RpcSessionRegistryError("session_path_in_use", undefined, {
113
- owner: holder,
114
- retry_after_ms: SESSION_PATH_RETRY_AFTER_MS,
115
- });
116
- }
113
+ // A concurrent open of this path waits on `claimSettled` instead of finding a reservation no
114
+ // entry backs (senpi#3485); it is settled once the entry is recorded or the reservation is back.
115
+ if (this.options.pathReservations)
116
+ claimSettled = await takeCrossGenerationClaim(this.reservations, this.pendingClaims, this.options.pathReservations, sessionPath);
117
117
  }
118
- // Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):
119
- // Create-only launch semantics mirror classic startup flags. A resumed
120
- // session restores its persisted model and thinking level instead of being
121
- // overridden by the new open_session request.
122
- const isResume = sessionPath !== undefined && existsSync(sessionPath);
123
- // Re-opening an existing session file is a resume, exactly like interactive
124
- // /resume (AgentSessionRuntime.switchSession). Without the event the session
125
- // starts with reason "startup" and every extension that only rebuilds state
126
- // on a resume - the ask-user dangling-question hook - stays unreachable from
127
- // the RPC restart path. A session created by this open stays "startup".
128
- const sessionStartEvent = isResume
129
- ? { type: "session_start", reason: "resume" }
130
- : undefined;
131
- const storedProfile = frozenProfile({ ...profile, ...(sessionPath ? { sessionPath } : {}) });
132
- const runtimeProfile = isResume
133
- ? frozenProfile({ ...storedProfile, creationModel: undefined, initialThinkingLevel: undefined })
134
- : storedProfile;
135
118
  const handle = `rpc-${++this.nextHandle}`;
136
- let settleOpen;
137
- const openCompletion = new Promise((resolve, reject) => {
138
- settleOpen = { resolve, reject };
139
- });
140
- // A concurrent explicit open for the same path waits on this to attach, never to time it out.
141
- openCompletion.catch(() => { });
142
- const entry = {
143
- state: "opening",
144
- scope: new ProviderScope(),
145
- profile: storedProfile,
146
- ...sessionIdentity(storedProfile),
147
- sessionPath,
148
- reservationKey: sessionPath,
149
- cwd: storedProfile.cwd,
150
- attachments: 1,
151
- retainOnDisconnect: options?.retainOnDisconnect === true,
152
- lastCommandAt: this.now(),
153
- lifecycleMutex: Promise.resolve(),
154
- openCompletion,
155
- };
156
- entry.switchSession = createEntrySwitchSession(entry, this.options.createRuntime, () => this.syncRuntimeMetadata());
157
- // Recorded before the first await so the synchronous collision guard above sees an open
158
- // that is still being built. `manager.getSessionId()` overwrites it below with the
159
- // authoritative value, which on a resume is the header's id, not the requested one.
160
- if (requestedDurableId !== undefined)
161
- entry.durableSessionId = requestedDurableId;
119
+ let opening;
120
+ try {
121
+ opening = buildOpeningEntry({
122
+ profile,
123
+ sessionPath,
124
+ requestedDurableId,
125
+ options,
126
+ now: this.now(),
127
+ createRuntime: this.options.createRuntime,
128
+ sync: () => this.syncRuntimeMetadata(),
129
+ });
130
+ }
131
+ catch (cause) {
132
+ // Give the path back before waking waiters, or they find a reservation no entry backs. A failed
133
+ // claim release is reported, never allowed to strand the waiters or replace the build error.
134
+ try {
135
+ if (sessionPath) {
136
+ this.reservations.delete(sessionPath);
137
+ await this.options.pathReservations?.release(sessionPath);
138
+ }
139
+ }
140
+ catch (releaseCause) {
141
+ process.stderr.write(`senpi rpc open of ${sessionPath} could not release its claim: ${String(releaseCause)}\n`);
142
+ }
143
+ finally {
144
+ claimSettled?.();
145
+ }
146
+ throw cause;
147
+ }
148
+ const { entry, storedProfile, runtimeProfile, sessionStartEvent, settleOpen } = opening;
162
149
  this.entries.set(handle, entry);
150
+ claimSettled?.();
163
151
  this.options.onSizeChange?.(this.entries.size);
164
152
  try {
165
153
  const newSessionOptions = requestedDurableId !== undefined ? { id: requestedDurableId } : undefined;
package/docs/rpc.md CHANGED
@@ -1585,7 +1585,9 @@ What the host does enforce are lifecycle windows, and they only ever return memo
1585
1585
  reaches zero attachments and closes the session, host shutdown closes it, and the idle-eviction window above still
1586
1586
  parks it (or the completed-detached-worker rule above parks it sooner) — announced as
1587
1587
  `session_parked { sessionId, sessionPath }`, after which the file reopens by path like any
1588
- evicted session, and the parked session counts as gone for the empty-host exit below. It is also bounded by the empty-host exit
1588
+ evicted session, and the parked session counts as gone for the empty-host exit below. The park is also recorded
1589
+ for the NEXT generation, which reopens it on startup; the host that parked it never reopens it on its own, so a
1590
+ client reopening the session right after `session_parked` is the only open of that path (senpi#3485). It is also bounded by the empty-host exit
1589
1591
  and the supervisor's idle-exit window below: retention survives a client, not the host. Any attach may turn retention
1590
1592
  on for a live session; no attach turns it off for clients that already rely on it. Omitting the flag is byte-identical
1591
1593
  to the previous behavior — the session is closed with its last connection. Probe `retain_on_disconnect` in
@@ -2066,7 +2068,7 @@ codes or code prefixes in `error`:
2066
2068
  - **Mid-turn policy:** a turn admitted before the foreign holder appeared finishes and persists its results; subsequent writing RPCs are refused. Read/control requests, including abort, queue clearing, `extension_ui_response` and `extension_ui_progress`, remain available.
2067
2069
  - **Conservative admission:** `new_session`, `fork`, `clone`, and settings or account commands carrying this session's routing handle are also refused while its current file is held. Close the handle and open another session to leave a held file.
2068
2070
  - Holder records are admission evidence, not an exclusive filesystem lock. SessionManager's append API is synchronous and fresh process-identity checks are asynchronous; changing append to asynchronous admission would change the SDK contract and could discard results of already-executed tools.
2069
- - `session_path_in_use` (path held by an opening or quarantined owner; a fully-open current owner is attached instead, an owner whose teardown is already in flight is waited out on the in-process runtime, and a path a live owner has superseded is released rather than held). A path held by ANOTHER GENERATION of the daemon carries `errorData { owner: { instanceId, pid, processStartTime, sessionPath, current }, retry_after_ms: 2000 }`: that generation is still writing the file and is parking it, so the open is a retry, not a failure. Only a claim whose owner serves the socket (`current: true`) or still has a client attached refuses an open at all - a superseded, attachment-less claim is reclaimed instead
2071
+ - `session_path_in_use` (path held by an opening or quarantined owner; a fully-open current owner is attached instead, an owner whose teardown is already in flight is waited out on the in-process runtime, and a path a live owner has superseded is released rather than held). A path held by ANOTHER GENERATION of the daemon carries `errorData { owner: { instanceId, pid, processStartTime, sessionPath, current }, retry_after_ms: 2000 }`: that generation is still writing the file and is parking it, so the open is a retry, not a failure. An open that arrives while ANOTHER open of the same path is still taking that cross-generation claim waits for it - bounded by the close grace window - and then attaches to the session that open is building; a claim that does not settle in that window answers `errorData { reason: "claim_in_progress", retry_after_ms: 2000 }`. When the claim it waited on is refused, the next waiting open takes the claim itself and later ones wait on that, so no racing open answers a bare refusal; a path still reserved by a session past its teardown grace answers `errorData { reason: "path_still_held", retry_after_ms: 2000 }` (senpi#3485). Only a claim whose owner serves the socket (`current: true`) or still has a client attached refuses an open at all - a superseded, attachment-less claim is reclaimed instead
2070
2072
  - `session_reservation_limit` (this worker already holds 64 live session paths; the open or session replacement was refused without disturbing the existing session)
2071
2073
  - `missing_session_id` (session-scoped command without `sessionId` in multi mode)
2072
2074
  - `multi_session_disabled` (`open_session` in classic mode)
@@ -2421,6 +2423,8 @@ Quiesce and queue options (both optional, combinable with `effectId`):
2421
2423
  {"type": "response", "command": "abort", "success": true, "data": {"effectId": "stop-2", "state": "done", "effect": {"effectId": "stop-2", "kind": "abort", "state": "done", "durableSessionId": "session-id"}, "stopped": true, "idle": true, "cleared": 1}}
2422
2424
  ```
2423
2425
 
2426
+ The typed `RpcClient` sends these with `abort({ effectId, awaitIdle, clearQueue, graceMs })` and returns the answer; `abort()` with no options keeps the old fire-and-forget contract. `getEffectStatus(effectId, { sessionPath? })` reads a receipt.
2427
+
2424
2428
  An aborted turn's `agent_end` carries `aborted: true`, `abortSource` and, when an RPC client aborted it,
2425
2429
  `abortedBy: { connectionId, effectId? }`; a `session_abort` caused by a client carries the same `abortedBy`. `interrupt` records its caller the same way. An abort that reached no turn is forgotten once it settles, and a new turn starts with none, so a later system or extension abort never inherits an old client's `abortedBy`.
2426
2430
  Every attached client sees it. `abortSource` is `user` for a client or user abort, `provider` when the
package/docs/settings.md CHANGED
@@ -199,6 +199,19 @@ Set `PI_SKIP_VERSION_CHECK=1` to disable the senpi version update check. Use `--
199
199
  }
200
200
  ```
201
201
 
202
+ ### Context Files and Skills
203
+
204
+ | Setting | Type | Default | Description |
205
+ |---------|------|---------|-------------|
206
+ | `contextFiles.maxBytesPerFile` | number | `65536` | Maximum UTF-8 bytes loaded from each AGENTS.md / CLAUDE.md, including the truncation note and any closing code fence |
207
+ | `contextFiles.maxTotalBytes` | number | `131072` | Maximum combined UTF-8 bytes of loaded context files, including truncation notes and closing code fences |
208
+
209
+ Files receive budget nearest-first: the current directory, its ancestors, then the global file. A cut prefers a newline in the last quarter of the available content budget; otherwise it uses a UTF-8 character boundary. An open backtick or tilde fence is closed with the same marker before the truncation note; the closing marker's bytes count against the budget. Files with less than 1024 bytes of budget left are skipped when they would need truncation, with a warning distinguishing an undersized per-file limit from budget used by nearer files. Both limits accept positive numbers (rounded down); invalid values use the defaults.
210
+
211
+ The skills section has a fixed 48 KiB rendered UTF-8 byte cap, including its roots table and markup. Above 100 visible skills or 64 KiB of descriptions, descriptions become one line of at most 160 characters. If needed to meet the section cap, smaller catalogs also use compact descriptions, then all descriptions are dropped. If names and locations still exceed the cap, only the first skills that fit are listed, with the omitted count inside the section. Skills remain available for explicit invocation.
212
+
213
+ Context cuts, skipped files, and compacted or omitted skills share the "Context size limits" resource notice in interactive mode and appear once on stderr at startup in print, JSON, and RPC modes, with the affected path when present. Ordinary skill validation warnings keep their existing diagnostic behavior and are not printed to stderr by this size check.
214
+
202
215
  ### Compaction
203
216
 
204
217
  | Setting | Type | Default | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi",
3
- "version": "2026.10.16-3",
3
+ "version": "2026.10.16-4",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -60,9 +60,9 @@
60
60
  },
61
61
  "dependencies": {
62
62
  "@earendil-works/chord": "0.99.1",
63
- "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.16-3",
64
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.16-3",
65
- "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.16-3",
63
+ "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.16-4",
64
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.16-4",
65
+ "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.16-4",
66
66
  "@silvia-odwyer/photon-node": "0.3.4",
67
67
  "chalk": "6.0.0",
68
68
  "cross-spawn": "7.0.6",
@@ -81,8 +81,8 @@
81
81
  "yaml": "2.9.1",
82
82
  "@anthropic-ai/claude-agent-sdk": "0.3.296",
83
83
  "@anthropic-ai/sdk": "0.127.0",
84
- "@code-yeongyu/senpi-codemode": "2026.10.16-3",
85
- "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.16-3",
84
+ "@code-yeongyu/senpi-codemode": "2026.10.16-4",
85
+ "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.16-4",
86
86
  "@modelcontextprotocol/sdk": "1.32.1",
87
87
  "@mozilla/readability": "0.6.0",
88
88
  "linkedom": "0.18.13",
@@ -1,8 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
2
- import{fuzzyFilter}from"./chunk-UUMKAA7S.js";import{clientMessageIdentity}from"./chunk-IRKWZZKD.js";import{DETACH_CONNECTION_CAPABILITY,socketNeedsHandshake}from"./chunk-EL4NYWZT.js";import{ORIGIN_HOST_INSTANCE_FIELD,attachJsonlLineReader,originHostInstance,readSocketSecret,resolveSocketTransportAddress,sendSocketHandshake,serializeJsonLine,socketSecretPath}from"./chunk-BPOEQRHX.js";var COMMAND_TOKEN=/^\/[A-Za-z][A-Za-z0-9:_-]*$/,MAX_SUGGESTIONS=3,UnknownCommandError=class extends Error{constructor(command,suggestions,reason){super(formatUnknownCommandMessage(command,suggestions,reason)),this.name="UnknownCommandError",this.command=command,this.suggestions=suggestions,this.reason=reason}};function formatUnknownCommandMessage(command,suggestions,reason){if(reason==="interactive_only")return`/${command} is an interactive command and cannot be sent as a prompt.`;let hint=suggestions.length===0?"":` Did you mean ${suggestions.map(name=>`/${name}`).join(", ")}?`;return`Unknown command /${command}.${hint}`}var UNKNOWN_COMMAND_CONFIRM_HINT='Resend with "unknownCommandAsText": true to send it as text.';function unknownCommandErrorFromWire(data){if(typeof data!="object"||data===null)return;let command=Reflect.get(data,"command"),suggestions=Reflect.get(data,"suggestions"),reason=Reflect.get(data,"reason");if(typeof command!="string"||!Array.isArray(suggestions)||reason!=="unknown"&&reason!=="interactive_only")return;let names=suggestions.filter(name=>typeof name=="string");return new UnknownCommandError(command,names,reason)}function commandShapedName(text){let token=text.trim().split(/\s/,1)[0]??"";return COMMAND_TOKEN.test(token)?token.slice(1):void 0}function findUnknownCommand(text,catalog){let command=commandShapedName(text);if(command===void 0||catalog.promptCommands.has(command))return;if(catalog.interactiveCommands.has(command))return new UnknownCommandError(command,[],"interactive_only");let known=[...new Set([...catalog.promptCommands,...catalog.interactiveCommands])],suggestions=fuzzyFilter(known,command,name=>name).slice(0,MAX_SUGGESTIONS);return new UnknownCommandError(command,suggestions,"unknown")}import{existsSync}from"node:fs";function getMissingSessionCwdIssue(sessionManager,fallbackCwd){let sessionFile=sessionManager.getSessionFile();if(!sessionFile)return;let sessionCwd=sessionManager.getCwd();if(!(!sessionCwd||existsSync(sessionCwd)))return{sessionFile,sessionCwd,fallbackCwd}}function formatMissingSessionCwdError(issue){let sessionFile=issue.sessionFile?`
3
- Session file: ${issue.sessionFile}`:"";return`Stored session working directory does not exist: ${issue.sessionCwd}${sessionFile}
4
- Current working directory: ${issue.fallbackCwd}`}function formatMissingSessionCwdPrompt(issue){return`cwd from session file does not exist
5
- ${issue.sessionCwd}
6
-
7
- continue in current cwd
8
- ${issue.fallbackCwd}`}var MissingSessionCwdError=class extends Error{constructor(issue){super(formatMissingSessionCwdError(issue)),this.name="MissingSessionCwdError",this.issue=issue}};function assertSessionCwdExists(sessionManager,fallbackCwd){let issue=getMissingSessionCwdIssue(sessionManager,fallbackCwd);if(issue)throw new MissingSessionCwdError(issue)}import{spawn}from"node:child_process";import{createConnection}from"node:net";var StreamIdleTimeoutError=class extends Error{constructor(idleTimeoutMs){super(`Summarization stream stalled: no provider events for ${idleTimeoutMs}ms; treating the request as dead`),this.name="StreamIdleTimeoutError",this.idleTimeoutMs=idleTimeoutMs}},StreamDurationBudgetError=class extends Error{constructor(maxDurationMs){super(`Summarization stream exceeded its ${maxDurationMs}ms wall-clock budget; treating the request as too slow to keep the session waiting`),this.name="StreamDurationBudgetError",this.maxDurationMs=maxDurationMs}},DEFAULT_SUMMARIZATION_IDLE_TIMEOUT_MS=3e5,DEFAULT_SUMMARIZATION_MAX_DURATION_MS=12e4,SUMMARIZATION_MAX_DURATION_PER_TOKEN_MS=2,SUMMARIZATION_MAX_DURATION_CAP_MS=18e5,SUMMARIZATION_TOTAL_BUDGET_MS=9e5;function summarizationTotalBudgetMs(attemptOverrideMs){let override=attemptOverrideMs!==void 0&&Number.isFinite(attemptOverrideMs)&&attemptOverrideMs>0?Math.min(SUMMARIZATION_MAX_DURATION_CAP_MS,attemptOverrideMs):0;return Math.max(SUMMARIZATION_TOTAL_BUDGET_MS,override)}var SummarizationTotalBudgetError=class extends Error{constructor(totalBudgetMs){super(`Compaction exceeded its ${totalBudgetMs}ms total wall-clock budget across every summarization attempt and retry`),this.name="SummarizationTotalBudgetError",this.totalBudgetMs=totalBudgetMs}};function createSummarizationDeadline(totalBudgetMs,now=Date.now){let startedMs=now(),remainingMs=()=>Math.max(0,totalBudgetMs-(now()-startedMs));return{totalBudgetMs,remainingMs,attemptBudgetMs:requestedMs=>{let remaining=remainingMs();if(remaining<=0)throw new SummarizationTotalBudgetError(totalBudgetMs);return Math.min(requestedMs,remaining)}}}function summarizationMaxDurationMs(estimatedInputTokens,overrideMs){if(overrideMs!==void 0&&Number.isFinite(overrideMs)&&overrideMs>0)return Math.min(SUMMARIZATION_MAX_DURATION_CAP_MS,overrideMs);let scaled=estimatedInputTokens*SUMMARIZATION_MAX_DURATION_PER_TOKEN_MS;return Math.min(SUMMARIZATION_MAX_DURATION_CAP_MS,Math.max(DEFAULT_SUMMARIZATION_MAX_DURATION_MS,scaled))}var IDLE_TRIP="idle-trip",BUDGET_TRIP="budget-trip",CALLER_ABORTED="caller-aborted";async function consumeStreamWithIdleTimeout(stream,options){let{idleTimeoutMs,maxDurationMs,abort,onEvent,signal,settle}=options,iterator,removeAbortListener,callerAbortPromise,budgetPromise,budgetTimer,budgetMs=0;if(maxDurationMs!==void 0){budgetMs=maxDurationMs;let{promise,resolve}=Promise.withResolvers();budgetTimer=setTimeout(()=>resolve(BUDGET_TRIP),budgetMs),budgetTimer.unref?.(),budgetPromise=promise}if(signal!==void 0&&!signal.aborted){let{promise,resolve}=Promise.withResolvers(),onAbort=()=>resolve(CALLER_ABORTED);signal.addEventListener("abort",onAbort,{once:!0}),removeAbortListener=()=>signal.removeEventListener("abort",onAbort),callerAbortPromise=promise}let settleUnderWatchdogs=async()=>{if(!settle)return;let{promise:idlePromise,resolve:resolveIdle}=Promise.withResolvers(),timer=setTimeout(()=>resolveIdle(IDLE_TRIP),idleTimeoutMs);timer.unref?.();let contenders=[settle().then(value=>({settled:value})),idlePromise];budgetPromise&&contenders.push(budgetPromise);let outcome;try{outcome=await Promise.race(contenders)}finally{clearTimeout(timer)}if(outcome===IDLE_TRIP)throw abort(),new StreamIdleTimeoutError(idleTimeoutMs);if(outcome===BUDGET_TRIP)throw abort(),new StreamDurationBudgetError(budgetMs);return outcome.settled};try{if(signal?.aborted)return await settleUnderWatchdogs();let resolvedStream;if(Symbol.asyncIterator in stream)resolvedStream=stream;else{let streamContenders=[Promise.resolve(stream)];callerAbortPromise&&streamContenders.push(callerAbortPromise),budgetPromise&&streamContenders.push(budgetPromise);let resolution=await Promise.race(streamContenders);if(resolution===BUDGET_TRIP)throw abort(),new StreamDurationBudgetError(budgetMs);if(resolution===CALLER_ABORTED)return await settleUnderWatchdogs();resolvedStream=resolution}for(iterator=resolvedStream[Symbol.asyncIterator]();;){let{promise:idlePromise,resolve:resolveIdle}=Promise.withResolvers(),timer=setTimeout(()=>resolveIdle(IDLE_TRIP),idleTimeoutMs);timer.unref?.();let contenders=[iterator.next(),idlePromise];callerAbortPromise&&contenders.push(callerAbortPromise),budgetPromise&&contenders.push(budgetPromise);let result;try{result=await Promise.race(contenders)}finally{clearTimeout(timer)}if(result===IDLE_TRIP)throw abort(),iterator?.return?.(),new StreamIdleTimeoutError(idleTimeoutMs);if(result===BUDGET_TRIP)throw abort(),iterator?.return?.(),new StreamDurationBudgetError(budgetMs);if(result===CALLER_ABORTED)return iterator?.return?.(),await settleUnderWatchdogs();if(result.done)return await settleUnderWatchdogs();onEvent?.(result.value)}}finally{removeAbortListener?.(),budgetTimer!==void 0&&clearTimeout(budgetTimer)}}var REQUEST_DEADLINE_MS=3e4,PROMPT_COMPACTION_DEADLINE_MS=SUMMARIZATION_TOTAL_BUDGET_MS+SUMMARIZATION_MAX_DURATION_CAP_MS+REQUEST_DEADLINE_MS,PROMPT_ACK_MAX_WAIT_MS=PROMPT_COMPACTION_DEADLINE_MS+REQUEST_DEADLINE_MS,OPEN_AFTER_QUEUED_DEADLINE_MS=10*6e4,PROMPT_AFTER_QUEUED_DEADLINE_MS=5*6e4;function promptStalledMessage(){return`prompt was received by the host but not accepted within ${PROMPT_AFTER_QUEUED_DEADLINE_MS/6e4} minutes`}function openStalledMessage(position){let queued=typeof position=="number"?` at queue position ${position}`:"",minutes=OPEN_AFTER_QUEUED_DEADLINE_MS/6e4;return`open_session was accepted by the host${queued} but not answered within ${minutes} minutes`}var RpcRequestTimeoutError=class extends Error{constructor(message){super(message),this.code="rpc_request_timeout",this.name="RpcRequestTimeoutError"}};function armRequestDeadline(ms,message,expire){let timer=setTimeout(()=>expire(new RpcRequestTimeoutError(message())),ms);return{extend(nextMs,nextMessage){clearTimeout(timer),timer=setTimeout(()=>expire(new RpcRequestTimeoutError(nextMessage())),nextMs)},clear(){clearTimeout(timer)}}}var RPC_ERROR_UNKNOWN_SESSION="unknown_session",RPC_ERROR_SESSION_NOT_ATTACHED="session_not_attached",RPC_ERROR_SESSION_CLOSING="session_closing";var RPC_ERROR_SESSION_HELD="session_held";var RPC_ERROR_MISSING_SESSION_ID="missing_session_id";var RPC_ERROR_INVALID_PATH="invalid_path",RPC_ERROR_INVALID_ARGUMENT="invalid_argument",RPC_ERROR_OPEN_FAILED="open_failed",RPC_ERROR_OWNER_CHANGED="owner_changed";var RPC_ERROR_MEDIA_NOT_FOUND="media_not_found",RPC_ERROR_INVALID_SESSION_CONTEXT="invalid_session_context",RPC_ERROR_INVALID_SUBSCRIPTION="invalid_subscription",RPC_ERROR_UNKNOWN_SUBSCRIPTION="unknown_subscription",RPC_ERROR_SUBSCRIPTION_LIMIT="subscription_limit",RPC_ERROR_UNSUPPORTED_TRANSPORT="unsupported_transport",RPC_ERROR_INVALID_SESSION_KIND="invalid_session_kind",RPC_ERROR_INVALID_LAUNCH_PROFILE="invalid_launch_profile";var RPC_ERROR_WARM_FAILED="warm_failed";var RPC_ERROR_ENTRY_NOT_FOUND="not_found",RPC_ERROR_MODEL_NOT_FOUND="model_not_found",RPC_ERROR_EFFECT_ID_CONFLICT="effect_id_conflict",RPC_ERROR_TRANSCRIPT_UNREADABLE="transcript_unreadable",RPC_ERROR_PROVIDER_UNAVAILABLE="provider_unavailable",RPC_ERROR_CONTEXT_BUDGET="context_budget",RPC_ERROR_EVAL_CELL_NOT_FOUND="eval_cell_not_found",RPC_ERROR_INVALID_EVAL_CELL_REQUEST="invalid_eval_cell_request",RPC_ERROR_EVAL_CELLS_UNAVAILABLE="eval_cells_unavailable";var RPC_ERROR_UNKNOWN_COMMAND="unknown_command",RPC_ERROR_TURN_ACTIVE="turn_active",RPC_ERROR_SESSION_BUSY="session_busy",RPC_ERROR_ATTACHED="attached",RPC_ERROR_INVALID_RELEASE_REASON="invalid_release_reason",RPC_ERROR_RELEASE_UNSUPPORTED="release_unsupported",RPC_ERROR_RELEASE_FAILED="release_failed",RPC_ERROR_HOST_DRAINING="host_draining";var STEER_WHEN_IDLE_CAPABILITY="steer_when_idle";var RpcCommandError=class extends Error{constructor(message,errorCode,errorData){super(message),this.name="RpcCommandError",this.errorCode=errorCode,this.errorData=errorData}};function isProviderAccountEvent(event){return event.type==="auth_accounts_changed"||event.type==="account_failover"}var RpcTransportGoneError=class extends Error{constructor(message="Shared RPC host is unavailable"){super(message),this.code="rpc_transport_gone",this.name="RpcTransportGoneError"}};function isTransportGoneError(error){return error instanceof RpcTransportGoneError||error instanceof Error&&(error.message==="Client not started"||error.message.startsWith("RPC transport is not writable.")||error.message==="RPC socket closed")}var RpcClientOpenInFlightError=class extends Error{constructor(){super("An open_session request is already in flight"),this.code="open_session_in_flight",this.name="RpcClientOpenInFlightError"}},MAX_PENDING_SESSION_EVENTS=512,MAX_PENDING_SESSION_EVENT_BYTES=1024*1024,RpcClient=class{constructor(options={}){this.process=null,this.socket=null,this.stopReadingStdout=null,this.eventListeners=[],this.pendingRequests=new Map,this.requestId=0,this.compactions=new Map,this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.pendingSessionEventBytes=0,this.stderr="",this.exitError=null,this.disconnectNotified=!1,this.stopping=!1,this.originHost=originHostInstance(),this.options=options,this.onEvent(event=>{switch(event.type){case"compaction_start":{let key=event.requestId??"";if(this.compactions.has(key))return;this.compactions.set(key,Date.now());break}case"compaction_end":if(!this.compactions.delete(event.requestId??""))return;break;case"session_replaced":this.compactions.clear();break;default:return}for(let pending of this.pendingRequests.values())pending.sessionId===this.sessionId&&pending.onCompaction?.(this.isCompacting())})}async start(){if(this.process||this.socket)throw new Error("Client already started");if(this.exitError=null,this.compactions.clear(),this.stopping=!1,this.disconnectNotified=!1,this.options.socketPath){await this.startSocket(this.options.socketPath);return}let cliPath=this.options.cliPath??"dist/cli.js",args=["--mode","rpc"];this.options.provider&&this.options.model&&args.push("--provider",this.options.provider),this.options.model&&args.push("--model",this.options.model),this.options.args&&args.push(...this.options.args);let childProcess=spawn("node",[cliPath,...args],{cwd:this.options.cwd,env:{...process.env,SENPI_RUNTIME:"node",...this.options.env},stdio:["pipe","pipe","pipe"],windowsHide:!0});if(this.process=childProcess,childProcess.stderr?.on("data",data=>{this.stderr+=data.toString(),process.stderr.write(data)}),childProcess.once("exit",(code,signal)=>{if(this.process!==childProcess)return;let error=this.createProcessExitError(code,signal);this.exitError=error,this.rejectPendingRequests(error)}),childProcess.once("error",error=>{if(this.process!==childProcess)return;let processError=new Error(`Agent process error: ${error.message}. Stderr: ${this.stderr}`);this.exitError=processError,this.rejectPendingRequests(processError)}),childProcess.stdin?.on("error",error=>{if(this.process!==childProcess)return;let stdinError=this.exitError??new Error(`Agent process stdin error: ${error.message}. Stderr: ${this.stderr}`);this.exitError=stdinError,this.rejectPendingRequests(stdinError)}),this.stopReadingStdout=attachJsonlLineReader(childProcess.stdout,line=>{this.handleLine(line)}),await new Promise(resolve=>setTimeout(resolve,100)),this.process.exitCode!==null){let error=this.exitError??this.createProcessExitError(this.process.exitCode,this.process.signalCode);throw this.exitError=error,error}}isCompacting(){let now=Date.now();for(let[key,startedAt]of this.compactions)now-startedAt>=PROMPT_COMPACTION_DEADLINE_MS&&this.compactions.delete(key);return this.compactions.size>0}async stop(){if(this.compactions.clear(),this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.pendingSessionEventBytes=0,this.socket){this.stopping=!0,this.stopReadingStdout?.(),this.stopReadingStdout=null,this.socket.destroy(),this.socket=null,this.sessionId=void 0,this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.notifyDisconnect(new RpcTransportGoneError);return}this.process&&(this.stopReadingStdout?.(),this.stopReadingStdout=null,this.process.kill("SIGTERM"),await new Promise(resolve=>{let timeout=setTimeout(()=>{this.process?.kill("SIGKILL"),resolve()},1e3);this.process?.on("exit",()=>{clearTimeout(timeout),resolve()})}),this.process=null,this.pendingRequests.clear())}async startSocket(path){let secret=socketNeedsHandshake(path)?await readSocketSecret(socketSecretPath(path)):void 0,socket=createConnection(resolveSocketTransportAddress(path,process.platform,secret));this.socket=socket,await new Promise((resolve,reject)=>{let onConnect=()=>{cleanup(),resolve()},onError=error=>{cleanup(),this.socket=null,reject(error)},cleanup=()=>{socket.off("connect",onConnect),socket.off("error",onError)};socket.once("connect",onConnect),socket.once("error",onError)}),secret&&sendSocketHandshake(socket,secret),this.stopReadingStdout=attachJsonlLineReader(socket,line=>this.handleLine(line)),socket.once("close",()=>{this.socket===socket&&(this.socket=null,this.notifyDisconnect(new RpcTransportGoneError))}),socket.once("error",error=>{this.socket===socket&&this.notifyDisconnect(new RpcTransportGoneError)})}onEvent(listener){return this.eventListeners.push(listener),()=>{let index=this.eventListeners.indexOf(listener);index!==-1&&this.eventListeners.splice(index,1)}}getStderr(){return this.stderr}async setClientInfo(width,capabilities){await this.send({type:"set_client_info",width,capabilities},this.sessionId!==void 0)}async openSession(options){if(this.pendingOpenSession)throw new RpcClientOpenInFlightError;this.pendingOpenSession=!0;try{let response=await this.send({type:"open_session",...options},!1),opened=this.getData(response);return this.sessionId=opened.sessionId,this.compactions.clear(),this.pendingOpenSession=!1,this.flushPendingSessionEvents(),opened}catch(error){throw this.pendingOpenSession=!1,this.pendingSessionEvents=[],this.pendingSessionEventBytes=0,error}}async sendExtensionUIResponse(response){await this.send(response,!0,void 0,!1)}async sendExtensionUIProgress(progress){await this.send(progress,!0,void 0,!1)}async unloadSession(options){let sessionId=options.sessionId??this.sessionId;if(!sessionId)throw new Error("unload_session needs a session");return this.getData(await this.send({...options,type:"unload_session",sessionId},!1))}async closeSession(sessionId=this.sessionId){if(sessionId){try{this.getData(await this.send({type:"close_session",sessionId},!1))}catch(error){if(!isTransportGoneError(error))throw error}this.sessionId===sessionId&&(this.sessionId=void 0,this.compactions.clear())}}async detachConnection(){let info=this.getData(await this.send({type:"get_protocol_info"},!1));if(!Array.isArray(info.capabilities)||!info.capabilities.includes(DETACH_CONNECTION_CAPABILITY))return"unsupported";let data=this.getData(await this.send({type:"detach_connection"},!1));return{deferred:Array.isArray(data?.deferred)?data.deferred.filter(id=>typeof id=="string"):[]}}async listSessions(options){let response=await this.send({type:"list_sessions",...options},!1);return this.getData(response).sessions}async prompt(message,optionsOrImages){let options=Array.isArray(optionsOrImages)?{images:optionsOrImages}:optionsOrImages??{},response=await this.send({type:"prompt",message,...clientMessageIdentity(options),...options.images?{images:options.images}:{},...options.streamingBehavior?{streamingBehavior:options.streamingBehavior}:{},...options.thinkingLevel?{thinkingLevel:options.thinkingLevel}:{},...options.sessionTitlePrompt!==void 0?{sessionTitlePrompt:options.sessionTitlePrompt}:{},...options.expandPromptTemplates!==void 0?{expandPromptTemplates:options.expandPromptTemplates}:{},...options.unknownCommandAsText?{unknownCommandAsText:!0}:{}},!0,{onResponse:wireResponse=>{if(!wireResponse.success){options?.preflightResult?.(!1);return}let data=wireResponse.data;options?.promptDisposition?.(data?.disposition??"handled"),options?.preflightResult?.(!0)},onReject:()=>{options?.preflightResult?.(!1)}});if(!response.success){let failure=response;throw(failure.errorCode===RPC_ERROR_UNKNOWN_COMMAND?unknownCommandErrorFromWire(failure.errorData):void 0)??new RpcCommandError(failure.error,failure.errorCode,failure.errorData)}return response.data?.disposition??"handled"}async appendUserMessage(content){await this.send({type:"append_user_message",content})}async appendSessionEntry(entry){await this.send({type:"append_session_entry",entry})}async sendCustomMessage(message,options){await this.send({type:"send_custom_message",...message,...options})}async continueFromLeaf(){let response=await this.send({type:"continue_from_leaf"});this.getData(response)}async steer(message,images,recovery){let response=await this.send({type:"steer",message,images,enqueueOrder:recovery?.enqueueOrder,...clientMessageIdentity(recovery)});return this.getData(response)?.disposition??"handled"}async followUp(message,images,recovery){let response=await this.send({type:"follow_up",message,images,enqueueOrder:recovery?.enqueueOrder,...clientMessageIdentity(recovery)});return this.getData(response)?.disposition??"handled"}async abort(){try{await this.send({type:"abort"})}catch(error){if(!isTransportGoneError(error))throw error}}async abortStrict(){this.getData(await this.send({type:"abort"}))}async abortCompaction(){await this.send({type:"abort_compaction"})}async reload(){let response=await this.send({type:"reload"});return this.getData(response)}async checkReloadVeto(){let response=await this.send({type:"check_reload_veto"});return this.getData(response)}async clearQueue(options){let response=await this.send({type:"clear_queue",abortWillFollow:options?.abortWillFollow});return this.getData(response)}async getSteeringMessages(){let response=await this.send({type:"get_steering_messages"});return this.getData(response).messages}async getFollowUpMessages(){let response=await this.send({type:"get_follow_up_messages"});return this.getData(response).messages}async abortBranchSummary(){await this.send({type:"abort_branch_summary"})}async recordBashResult(command,result,excludeFromContext){await this.send({type:"record_bash_result",command,result,excludeFromContext})}async setLabel(entryId,label){await this.send({type:"set_label",entryId,label})}async newSession(parentSession){let response=await this.send({type:"new_session",parentSession});return this.getData(response)}async getState(){let response=await this.send({type:"get_state"});return this.getData(response)}async setModel(provider,modelId){let response=await this.send({type:"set_model",provider,modelId});return this.getData(response)}async setFavoriteModels(models){await this.send({type:"set_favorite_models",models})}async setScopedModels(models){await this.send({type:"set_scoped_models",models})}async cycleModel(direction="forward"){let response=await this.send({type:"cycle_model",direction});return this.getData(response)}async getAvailableModels(){let response=await this.send({type:"get_available_models"});return this.getData(response).models}async setThinkingLevel(level,options){let response=await this.send({type:"set_thinking_level",level,scope:options?.scope});if(!response.success)throw new Error(response.error)}async cycleThinkingLevel(){let response=await this.send({type:"cycle_thinking_level"});return this.getData(response)}async getAvailableThinkingLevels(){let response=await this.send({type:"get_available_thinking_levels"});return this.getData(response).levels}async setFastMode(enabled){let response=await this.send({type:"set_fast_mode",enabled});return this.getData(response)}async getFastMode(){let response=await this.send({type:"get_fast_mode"});return this.getData(response)}async setSteeringMode(mode){await this.send({type:"set_steering_mode",mode})}async setFollowUpMode(mode){await this.send({type:"set_follow_up_mode",mode})}async compact(customInstructions){let response=await this.send({type:"compact",customInstructions});return this.getData(response)}async setAutoCompaction(enabled){await this.send({type:"set_auto_compaction",enabled})}async setAutoRetry(enabled){await this.send({type:"set_auto_retry",enabled})}async abortRetry(){await this.send({type:"abort_retry"})}async setRetryFallback(retryFallback){await this.send({type:"set_retry_fallback",retryFallback})}async bash(command,options){let response=await this.send({type:"bash",command,excludeFromContext:options?.excludeFromContext,executionId:options?.executionId,operations:options?.operations});return this.getData(response)}async navigateTree(targetId,options){let response=await this.send({type:"navigate_tree",targetId,...options});return this.getData(response)}async abortBash(){await this.send({type:"abort_bash"})}async cleanupBashOutput(path){await this.send({type:"cleanup_bash_output",path})}async getSessionStats(){let response=await this.send({type:"get_session_stats"});return this.getData(response)}async resolvePath(path){let response=await this.send({type:"resolve_path",path});return this.getData(response)}async exportHtml(outputPath,themeName){let response=await this.send({type:"export_html",outputPath,themeName});return this.getData(response)}async exportJsonl(outputPath){let response=await this.send({type:"export_jsonl",outputPath});return this.getData(response)}async switchSession(sessionPath,options){let response=await this.send({type:"switch_session",sessionPath,cwdOverride:options?.cwdOverride});return this.getData(response)}async fork(entryId,options){let response=await this.send({type:"fork",entryId,position:options?.position});return this.getData(response)}async clone(){let response=await this.send({type:"clone"});return this.getData(response)}async getForkMessages(){let response=await this.send({type:"get_fork_messages"});return this.getData(response).messages}async getEntries(since){let response=await this.send({type:"get_entries",since});return this.getData(response)}async getTree(){let response=await this.send({type:"get_tree"});return this.getData(response)}async editAssistantMessage(entryId,text,options={}){let response=await this.send({type:"edit_assistant_message",entryId,text,expectedLeafId:options.expectedLeafId,summarize:options.summarize,customInstructions:options.customInstructions});return this.getData(response)}async editUserMessage(entryId,text,options={}){let response=await this.send({type:"edit_user_message",entryId,text,expectedLeafId:options.expectedLeafId,summarize:options.summarize,customInstructions:options.customInstructions});return this.getData(response)}async getLastAssistantText(){let response=await this.send({type:"get_last_assistant_text"});return this.getData(response).text}async setSessionName(name){await this.send({type:"set_session_name",name})}async importJsonl(inputPath,cwdOverride){let response=await this.send({type:"import_jsonl",inputPath,cwdOverride});return this.getData(response)}async getMessages(){let response=await this.send({type:"get_messages"});return this.getData(response).messages}async getMedia(toolCallId,contentIndex){let response=await this.send({type:"get_media",toolCallId,contentIndex});return this.getData(response).content}async getCommands(){let response=await this.send({type:"get_commands"});return this.getData(response).commands}async requestExtension(name,data){let response=await this.send({type:"extension_request",name,...data===void 0?{}:{data}});return this.getData(response)}async listEvalCells(){return this.getData(await this.send({type:"eval_cells.list"}))}async peekEvalCell(cellId,tailBytes){return this.getData(await this.send({type:"eval_cells.peek",cellId,...tailBytes===void 0?{}:{tailBytes}}))}async stopEvalCell(cellId){return this.getData(await this.send({type:"eval_cells.stop",cellId}))}async getProviderAccounts(provider){let response=await this.send({type:"get_provider_accounts",provider});return this.getData(response).accounts}async pinProviderAccount(provider,name){await this.send({type:"account_pin",provider,name})}async removeProviderAccount(provider,name){await this.send({type:"account_remove",provider,name})}waitForIdle(timeout=6e4){return new Promise((resolve,reject)=>{let timer=setTimeout(()=>{unsubscribe(),reject(new Error(`Timeout waiting for agent to become idle. Stderr: ${this.stderr}`))},timeout),unsubscribe=this.onEvent(event=>{event.type==="agent_settled"&&(clearTimeout(timer),unsubscribe(),resolve())})})}collectEvents(timeout=6e4){return new Promise((resolve,reject)=>{let events=[],timer=setTimeout(()=>{unsubscribe(),reject(new Error(`Timeout collecting events. Stderr: ${this.stderr}`))},timeout),unsubscribe=this.onEvent(event=>{isProviderAccountEvent(event)||event.type==="extension_event"||event.type==="bash_start"||event.type==="bash_end"||event.type==="extension_ui_request"||event.type==="session_replaced"||event.type==="session_parked"||event.type==="queued"||(events.push(event),event.type==="agent_settled"&&(clearTimeout(timer),unsubscribe(),resolve(events)))})})}async promptAndWait(message,images,timeout=6e4){let eventsPromise=this.collectEvents(timeout);return await this.prompt(message,images?{images}:void 0),eventsPromise}handleLine(line){try{let data=JSON.parse(line);if(data.type==="response"&&data.id&&this.pendingRequests.has(data.id)){let pending=this.pendingRequests.get(data.id);this.pendingRequests.delete(data.id),pending.onResponse?.(data),pending.resolve(data);return}if(data.type==="queued"&&typeof data.for_request=="string"&&this.pendingRequests.get(data.for_request)?.onQueued?.(data.position),typeof data.sessionId=="string"&&data.sessionId!==this.sessionId){if(this.pendingOpenSession){let bytes=Buffer.byteLength(line);for(this.pendingSessionEvents.push({sessionId:data.sessionId,event:data,bytes}),this.pendingSessionEventBytes+=bytes;this.pendingSessionEvents.length>MAX_PENDING_SESSION_EVENTS||this.pendingSessionEventBytes>MAX_PENDING_SESSION_EVENT_BYTES;){let oldest=this.pendingSessionEvents.shift();if(!oldest)break;this.pendingSessionEventBytes-=oldest.bytes}}return}for(let listener of[...this.eventListeners])listener(data)}catch{}}flushPendingSessionEvents(){let pending=this.pendingSessionEvents;this.pendingSessionEvents=[],this.pendingSessionEventBytes=0;for(let{sessionId,event}of pending)if(sessionId===this.sessionId)for(let listener of[...this.eventListeners])listener(event)}createProcessExitError(code,signal){return new Error(`Agent process exited (code=${code} signal=${signal}). Stderr: ${this.stderr}`)}notifyDisconnect(error){this.rejectPendingRequests(error),!this.disconnectNotified&&!this.stopping&&(this.disconnectNotified=!0,this.options.onDisconnect?.(error))}rejectPendingRequests(error){for(let pending of this.pendingRequests.values())pending.onReject?.(error),pending.reject(error);this.pendingRequests.clear()}async send(command,route=!0,hooks,expectResponse=!0){let childProcess=this.process,stream=this.socket??childProcess?.stdin;if(!stream)throw new RpcTransportGoneError;if(this.exitError)throw this.exitError;if(childProcess?.exitCode!==null&&childProcess?.exitCode!==void 0){let error=this.createProcessExitError(childProcess.exitCode,childProcess.signalCode);throw this.exitError=error,error}if(stream.destroyed||!stream.writable){let error=new RpcTransportGoneError;throw this.notifyDisconnect(error),error}let ownId=command.type==="extension_ui_response"||command.type==="extension_ui_progress"?command.id:void 0,id=ownId??`req_${++this.requestId}`,fullCommand={...command,...route&&this.sessionId&&!("sessionId"in command)?{sessionId:this.sessionId}:{},...ownId===void 0?{id}:{},...this.socket&&this.originHost?{[ORIGIN_HOST_INSTANCE_FIELD]:this.originHost}:{}};if(!expectResponse)return stream.write(serializeJsonLine(fullCommand)),Promise.resolve({type:"response",command:command.type,success:!0});let sessionId="sessionId"in fullCommand?fullCommand.sessionId:void 0;return new Promise((resolve,reject)=>{let timeoutMessage=()=>`Timeout waiting for response to ${command.type}. Stderr: ${this.stderr}`,promptCap,promptReceived=!1,expire=timeoutError=>{clearTimeout(promptCap);let pending=this.pendingRequests.get(id);this.pendingRequests.delete(id),pending?.onReject?.(timeoutError),reject(timeoutError)},deadline=armRequestDeadline(command.type==="prompt"&&sessionId===this.sessionId&&this.isCompacting()?PROMPT_COMPACTION_DEADLINE_MS:REQUEST_DEADLINE_MS,timeoutMessage,expire);command.type==="prompt"&&(promptCap=setTimeout(()=>{deadline.clear(),expire(new RpcRequestTimeoutError(timeoutMessage()))},PROMPT_ACK_MAX_WAIT_MS)),this.pendingRequests.set(id,{sessionId,resolve:response=>{deadline.clear(),clearTimeout(promptCap),resolve(response)},reject:error=>{deadline.clear(),clearTimeout(promptCap),reject(error)},...command.type==="prompt"?{onCompaction:compacting=>compacting?deadline.extend(PROMPT_COMPACTION_DEADLINE_MS,timeoutMessage):promptReceived?deadline.extend(PROMPT_AFTER_QUEUED_DEADLINE_MS,promptStalledMessage):deadline.extend(REQUEST_DEADLINE_MS,timeoutMessage),onQueued:()=>{promptReceived=!0,this.isCompacting()||deadline.extend(PROMPT_AFTER_QUEUED_DEADLINE_MS,promptStalledMessage)}}:{},...command.type==="open_session"?{onQueued:position=>deadline.extend(OPEN_AFTER_QUEUED_DEADLINE_MS,()=>openStalledMessage(position))}:{},...hooks?.onResponse?{onResponse:hooks.onResponse}:{},...hooks?.onReject?{onReject:hooks.onReject}:{}});try{stream.write(serializeJsonLine(fullCommand))}catch(error){let writeError=error instanceof Error?error:new Error(String(error)),pending=this.pendingRequests.get(id);this.pendingRequests.delete(id),pending?.onReject?.(writeError),pending?.reject(writeError)}})}getData(response){if(!response.success){let errorResponse=response;throw errorResponse.errorCode==="missing_session_cwd"&&errorResponse.errorData?new MissingSessionCwdError(errorResponse.errorData):new RpcCommandError(errorResponse.error,errorResponse.errorCode,errorResponse.errorData)}return response.data}};export{StreamIdleTimeoutError,StreamDurationBudgetError,DEFAULT_SUMMARIZATION_IDLE_TIMEOUT_MS,DEFAULT_SUMMARIZATION_MAX_DURATION_MS,SUMMARIZATION_MAX_DURATION_PER_TOKEN_MS,SUMMARIZATION_TOTAL_BUDGET_MS,summarizationTotalBudgetMs,SummarizationTotalBudgetError,createSummarizationDeadline,summarizationMaxDurationMs,consumeStreamWithIdleTimeout,UnknownCommandError,UNKNOWN_COMMAND_CONFIRM_HINT,commandShapedName,findUnknownCommand,getMissingSessionCwdIssue,formatMissingSessionCwdPrompt,MissingSessionCwdError,assertSessionCwdExists,RPC_ERROR_UNKNOWN_SESSION,RPC_ERROR_SESSION_NOT_ATTACHED,RPC_ERROR_SESSION_CLOSING,RPC_ERROR_SESSION_HELD,RPC_ERROR_MISSING_SESSION_ID,RPC_ERROR_INVALID_PATH,RPC_ERROR_INVALID_ARGUMENT,RPC_ERROR_OPEN_FAILED,RPC_ERROR_OWNER_CHANGED,RPC_ERROR_MEDIA_NOT_FOUND,RPC_ERROR_INVALID_SESSION_CONTEXT,RPC_ERROR_INVALID_SUBSCRIPTION,RPC_ERROR_UNKNOWN_SUBSCRIPTION,RPC_ERROR_SUBSCRIPTION_LIMIT,RPC_ERROR_UNSUPPORTED_TRANSPORT,RPC_ERROR_INVALID_SESSION_KIND,RPC_ERROR_INVALID_LAUNCH_PROFILE,RPC_ERROR_WARM_FAILED,RPC_ERROR_ENTRY_NOT_FOUND,RPC_ERROR_MODEL_NOT_FOUND,RPC_ERROR_EFFECT_ID_CONFLICT,RPC_ERROR_TRANSCRIPT_UNREADABLE,RPC_ERROR_PROVIDER_UNAVAILABLE,RPC_ERROR_CONTEXT_BUDGET,RPC_ERROR_EVAL_CELL_NOT_FOUND,RPC_ERROR_INVALID_EVAL_CELL_REQUEST,RPC_ERROR_EVAL_CELLS_UNAVAILABLE,RPC_ERROR_UNKNOWN_COMMAND,RPC_ERROR_TURN_ACTIVE,RPC_ERROR_SESSION_BUSY,RPC_ERROR_ATTACHED,RPC_ERROR_INVALID_RELEASE_REASON,RPC_ERROR_RELEASE_UNSUPPORTED,RPC_ERROR_RELEASE_FAILED,RPC_ERROR_HOST_DRAINING,STEER_WHEN_IDLE_CAPABILITY,RpcRequestTimeoutError,RpcCommandError,RpcTransportGoneError,isTransportGoneError,RpcClientOpenInFlightError,RpcClient};
@@ -1,3 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
2
- import{require_proper_lockfile}from"./chunk-GZVCRV7I.js";import{CONFIG_DIR_NAME,canonicalizePath,canonicalizePathStrict,findNodePackageDir,getDocsPath,getExamplesPath,getExportTemplateDir,getInstallPackageDir,getInteractiveAssetsDir,getPackageDir,getThemesDir,isBunBinary,resolvePath,stripBom}from"./chunk-NP42HAJO.js";import{__toESM}from"./chunk-IKOFCODM.js";var import_proper_lockfile=__toESM(require_proper_lockfile(),1);import{existsSync as existsSync2,mkdirSync,readFileSync,writeFileSync}from"node:fs";import{homedir}from"node:os";import{dirname as dirname3,join as join3}from"node:path";import{existsSync}from"node:fs";import{createRequire}from"node:module";import{dirname as dirname2,join as join2,resolve,sep}from"node:path";import{fileURLToPath as fileURLToPath2}from"node:url";import{dirname,join}from"node:path";import{fileURLToPath}from"node:url";function imagegenSkillPath(baseDir=dirname(fileURLToPath(import.meta.url))){return join(baseDir,"skill","SKILL.md")}var moduleRequire=createRequire(import.meta.url),runningFromSource=fileURLToPath2(import.meta.url).includes(`${sep}src${sep}core${sep}`),bundledBuiltinExtensions=[{id:"codemode",resolvePackage:()=>resolveBundledPackageJson("@code-yeongyu/senpi-codemode/package.json","senpi-codemode/package.json",join2("node_modules","@code-yeongyu","senpi-codemode","package.json"))}];function resolveBundledPackageJson(packageSpecifier,workspaceRelativePath,binaryRelativePath){let packageRoot=getPackageDir(),workspacePath=resolve(packageRoot,"..",workspaceRelativePath);if(runningFromSource&&existsSync(workspacePath))return workspacePath;let binaryPath=resolve(packageRoot,binaryRelativePath);if(isBunBinary&&existsSync(binaryPath))return binaryPath;try{return moduleRequire.resolve(packageSpecifier)}catch(error){if(existsSync(workspacePath))return workspacePath;throw error}}function getBundledResourceRoots(){let roots=[getThemesDir(),getExportTemplateDir(),getInteractiveAssetsDir(),getDocsPath(),getExamplesPath(),dirname2(imagegenSkillPath())];!isBunBinary&&!runningFromSource?roots.push(getPackageDir(),getInstallPackageDir(),findNodePackageDir(dirname2(fileURLToPath2(import.meta.url)))):isBunBinary&&roots.push(dirname2(fileURLToPath2(import.meta.url)));for(let extension of bundledBuiltinExtensions)try{roots.push(dirname2(extension.resolvePackage()))}catch(error){if(!(error instanceof Error&&"code"in error&&error.code==="MODULE_NOT_FOUND"))throw error}return roots}var TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES=["settings.json","mcp.json","extensions","skills","prompts","themes","SYSTEM.md","APPEND_SYSTEM.md"],LEGACY_PROJECT_CONFIG_DIR_NAME=".pi";function projectCodemodeNamesExecutable(configDir){let path=join3(configDir,"codemode.json");if(!existsSync2(path))return!1;let executableSettings=codemodeExecutableSettings();if(executableSettings===void 0)return!0;let parsed;try{parsed=JSON.parse(stripBom(readFileSync(path,"utf8")))}catch{return!0}return executableSettings.some(settingPath=>valueAtPath(parsed,settingPath)!==void 0)}function codemodeExecutableSettings(){let codemode=bundledBuiltinExtensions.find(extension=>extension.id==="codemode");try{let packageJson=codemode?.resolvePackage();if(packageJson===void 0)return;let list=JSON.parse(readFileSync(join3(dirname3(packageJson),"src","config","executable-settings.json"),"utf8"));if(typeof list!="object"||list===null)return;let paths=Reflect.get(list,"projectExecutableSettings");return Array.isArray(paths)&&paths.every(entry=>typeof entry=="string")?paths:void 0}catch{return}}function valueAtPath(value,path){let current=value;for(let key of path.split(".")){if(typeof current!="object"||current===null)return;current=Reflect.get(current,key)}return current}function normalizeCwd(cwd){let resolved=resolvePath(cwd);try{return canonicalizePathStrict(resolved)}catch{return resolved}}function findNearestTrustEntry(data,cwd){let currentDir=normalizeCwd(cwd);for(;;){let value=data[currentDir];if(value===!0||value===!1)return{path:currentDir,decision:value};let parentDir=dirname3(currentDir);if(parentDir===currentDir)return null;currentDir=parentDir}}function getProjectTrustParentPath(cwd){let trustPath=normalizeCwd(cwd),parentDir=dirname3(trustPath);return parentDir===trustPath?void 0:parentDir}function getProjectTrustOptions(cwd,options){let trustPath=normalizeCwd(cwd),trustOptions=[{label:"Trust",trusted:!0,updates:[{path:trustPath,decision:!0}],savedPath:trustPath}],parentPath=getProjectTrustParentPath(cwd);return parentPath!==void 0&&trustOptions.push({label:`Trust parent folder (${parentPath})`,trusted:!0,updates:[{path:parentPath,decision:!0},{path:trustPath,decision:null}],savedPath:parentPath}),options?.includeSessionOnly&&trustOptions.push({label:"Trust (this session only)",trusted:!0,updates:[]}),trustOptions.push({label:"Do not trust",trusted:!1,updates:[{path:trustPath,decision:!1}],savedPath:trustPath}),options?.includeSessionOnly&&trustOptions.push({label:"Do not trust (this session only)",trusted:!1,updates:[]}),trustOptions}function readTrustFile(path){if(!existsSync2(path))return{};let parsed;try{parsed=JSON.parse(stripBom(readFileSync(path,"utf-8")))}catch(error){let message=error instanceof Error?error.message:String(error);throw new Error(`Failed to read trust store ${path}: ${message}`)}if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))throw new Error(`Invalid trust store ${path}: expected an object`);let data={};for(let[key,value]of Object.entries(parsed)){if(value!==!0&&value!==!1&&value!==null)throw new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);data[key]=value}return data}function writeTrustFile(path,data){let sorted={};for(let key of Object.keys(data).sort()){let value=data[key];(value===!0||value===!1||value===null)&&(sorted[key]=value)}mkdirSync(dirname3(path),{recursive:!0}),writeFileSync(path,`${JSON.stringify(sorted,null,2)}
3
- `,"utf-8")}function acquireTrustLockSync(path){let trustDir=dirname3(path);mkdirSync(trustDir,{recursive:!0});let maxAttempts=10,delayMs=20,lastError;for(let attempt=1;attempt<=maxAttempts;attempt++)try{return import_proper_lockfile.default.lockSync(trustDir,{realpath:!1,lockfilePath:`${path}.lock`})}catch(error){if((typeof error=="object"&&error!==null&&"code"in error?String(error.code):void 0)!=="ELOCKED"||attempt===maxAttempts)throw error;lastError=error;let start=Date.now();for(;Date.now()-start<delayMs;);}throw lastError instanceof Error?lastError:new Error("Failed to acquire trust store lock")}function withTrustFileLock(path,fn){let release=acquireTrustLockSync(path);try{return fn()}finally{release()}}function hasTrustRequiringProjectResources(cwd){let homeDir=canonicalizePath(resolvePath(process.env.HOME||homedir())),userAgentsSkillsDir=join3(homeDir,".agents","skills"),currentDir=canonicalizePath(resolvePath(cwd));if([join3(currentDir,CONFIG_DIR_NAME),join3(currentDir,LEGACY_PROJECT_CONFIG_DIR_NAME)].some(configDir=>TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some(entry=>existsSync2(join3(configDir,entry))))||projectCodemodeNamesExecutable(join3(currentDir,CONFIG_DIR_NAME)))return!0;for(;;){let agentsSkillsDir=join3(currentDir,".agents","skills");if(agentsSkillsDir!==userAgentsSkillsDir&&existsSync2(agentsSkillsDir))return!0;let parentDir=dirname3(currentDir);if(parentDir===currentDir)return!1;currentDir=parentDir}}var ProjectTrustStore=class{constructor(agentDir){this.trustPath=join3(resolvePath(agentDir),"trust.json")}get(cwd){return this.getEntry(cwd)?.decision??null}getEntry(cwd){return withTrustFileLock(this.trustPath,()=>{let data=readTrustFile(this.trustPath);return findNearestTrustEntry(data,cwd)})}set(cwd,decision){this.setMany([{path:cwd,decision}])}setMany(decisions){withTrustFileLock(this.trustPath,()=>{let data=readTrustFile(this.trustPath);for(let{path,decision}of decisions){let key=normalizeCwd(path);decision===null?delete data[key]:data[key]=decision}writeTrustFile(this.trustPath,data)})}};export{imagegenSkillPath,bundledBuiltinExtensions,getBundledResourceRoots,getProjectTrustOptions,hasTrustRequiringProjectResources,ProjectTrustStore};
@@ -1,2 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
2
- import{SessionSelectorComponent,createStartupTui,startStartupTui}from"./chunk-I5ZJEXTM.js";import"./chunk-P37AEPIE.js";import"./chunk-DCQCK6CK.js";import{KeybindingsManager}from"./chunk-77PE5H2A.js";import{setKeybindings}from"./chunk-UUMKAA7S.js";import"./chunk-JABBHOD5.js";import"./chunk-QMRJYMNH.js";import"./chunk-QC4KECXI.js";import"./chunk-YU4AYH4C.js";import"./chunk-7X72IACK.js";import"./chunk-GZVCRV7I.js";import"./chunk-26UBFF6N.js";import"./chunk-ZDTNH3XU.js";import"./chunk-U3VOXCGP.js";import"./chunk-2VZJTDUX.js";import"./chunk-YU2P3CAR.js";import"./chunk-COH5F7QV.js";import"./chunk-RFB5CBW7.js";import"./chunk-G3OOGWMC.js";import"./chunk-ZOFZRJSL.js";import"./chunk-NP42HAJO.js";import"./chunk-ATQFN5HI.js";import"./chunk-RBCQOIAD.js";import"./chunk-RKJNJ3GI.js";import"./chunk-WHPDEDCC.js";import"./chunk-PFNBY43W.js";import"./chunk-UAQW2T7G.js";import"./chunk-HK46JMSC.js";import"./chunk-5ROMKDLK.js";import"./chunk-45SZOLCU.js";import"./chunk-PELNNWGO.js";import"./chunk-3W47LTXQ.js";import"./chunk-4D2PPFKR.js";import"./chunk-WDTJ6JKE.js";import"./chunk-2ON6BFHO.js";import"./chunk-DMI55M2N.js";import"./chunk-PJ34QVL5.js";import"./chunk-IKOFCODM.js";async function selectSession(currentSessionsLoader,allSessionsLoader,settingsManager){let ui=await createStartupTui(settingsManager);return new Promise(resolve=>{let keybindings=KeybindingsManager.create();setKeybindings(keybindings);let resolved=!1,selector=new SessionSelectorComponent(currentSessionsLoader,allSessionsLoader,path=>{resolved||(resolved=!0,ui.stop(),resolve(path))},()=>{resolved||(resolved=!0,ui.stop(),resolve(null))},()=>{ui.stop(),process.exit(0)},()=>ui.requestRender(),{showRenameHint:!1,keybindings});ui.addChild(selector),ui.setFocus(selector.getSessionList()),startStartupTui(ui,settingsManager)})}export{selectSession};