@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.
- package/CHANGELOG.md +38 -0
- package/dist/bundle/chunks/{args-6T5EQVY3.js → args-JXULI4EH.js} +1 -1
- package/dist/bundle/chunks/{chunk-VUKNGMDT.js → chunk-3LNSLJ7N.js} +1 -1
- package/dist/bundle/chunks/{chunk-MRN6IKOQ.js → chunk-3VOG6WFU.js} +1 -1
- package/dist/bundle/chunks/{chunk-AVLYZET7.js → chunk-7WAJCESX.js} +1 -1
- package/dist/bundle/chunks/{chunk-4TRKE3ZC.js → chunk-BMYNMUVZ.js} +1 -1
- package/dist/bundle/chunks/{chunk-LR6Z6V54.js → chunk-BTNYLNWZ.js} +2 -2
- package/dist/bundle/chunks/{chunk-JAXZOEJE.js → chunk-BWA4FVFW.js} +1 -1
- package/dist/bundle/chunks/{chunk-AGQSRWNN.js → chunk-EHQXLHRB.js} +1 -1
- package/dist/bundle/chunks/{chunk-I5NIJZDY.js → chunk-EYBDBR7V.js} +1 -1
- package/dist/bundle/chunks/{chunk-YJA2B7FQ.js → chunk-FDL4CF6F.js} +1 -1
- package/dist/bundle/chunks/{chunk-HCF5M6NA.js → chunk-GXY5JEI5.js} +1 -1
- package/dist/bundle/chunks/{chunk-CYG4LS4S.js → chunk-J5P2VL3Y.js} +1 -1
- package/dist/bundle/chunks/{chunk-DYWHYQUD.js → chunk-PXKF2RHD.js} +1 -1
- package/dist/bundle/chunks/{chunk-H6IG2ITJ.js → chunk-QH2HSCMX.js} +1 -1
- package/dist/bundle/chunks/{chunk-CZYADQ2E.js → chunk-SGDOZT6B.js} +1 -1
- package/dist/bundle/chunks/{chunk-BZ75DRZB.js → chunk-U67ML6CQ.js} +4 -2
- package/dist/bundle/chunks/{chunk-DITSGW4R.js → chunk-URBJS24R.js} +75 -75
- package/dist/bundle/chunks/{chunk-CZR7C6CY.js → chunk-VOBD6FQG.js} +1 -1
- package/dist/bundle/chunks/{chunk-SGATKHO4.js → chunk-Y5OTHMXO.js} +1 -1
- package/dist/bundle/chunks/{chunk-GOB5BLDV.js → chunk-YDCRA5ES.js} +2 -2
- package/dist/bundle/chunks/{cli-main-BOYWBHOB.js → cli-main-TABBP55A.js} +1 -1
- package/dist/bundle/chunks/{dispatch-KQEGIXQW.js → dispatch-DEP4PEDZ.js} +3 -2
- package/dist/bundle/chunks/{help-fast-path-6PVN7WI5.js → help-fast-path-ZHZGXAUG.js} +1 -1
- package/dist/bundle/chunks/{host-command-7MAXSZIH.js → host-command-OQR4RDTM.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-L2UTORL5.js → host-session-command-IGB27ZPJ.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-NVBFOE5R.js → interactive-mode-DGQNSXUA.js} +1 -1
- package/dist/bundle/chunks/{list-tips-BO5RP34W.js → list-tips-CREX4IXZ.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-3TVASSQD.js → multi-session-host-M7HVUMRH.js} +2 -2
- package/dist/bundle/chunks/{package-manager-cli-5BHVDVJC.js → package-manager-cli-FSR5P6YL.js} +1 -1
- package/dist/bundle/chunks/{role-BHA5ROSF.js → role-M2MTVACJ.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-RXMCJJUL.js → rotation-stream-U7YQMC7H.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-LPP46I7H.js → rpc-mode-SZAJNKKL.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-QW67J57R.js → schedule-command-XBW4EOG5.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-QQBBY5AT.js → session-control-endpoint-WEF3XTJR.js} +1 -1
- package/dist/bundle/chunks/{session-picker-HIC5ZTE6.js → session-picker-KQUFWDQC.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +98 -95
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +27 -0
- package/dist/core/agent-session.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/index.js +4 -2
- package/dist/core/extensions/builtin/anthropic-subscription/options.js +2 -1
- package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.js +9 -2
- package/dist/core/extensions/builtin/anthropic-subscription/session-binding.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-commit-boundary.d.ts +6 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-commit-boundary.js +18 -7
- package/dist/core/extensions/builtin/anthropic-subscription/session-commit-divergence.d.ts +23 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-commit-divergence.js +107 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-cwd.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-cwd.js +9 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-entry-annotations.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-entry-annotations.js +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-observability.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-observability.js +4 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry-wiring.js +12 -7
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry.d.ts +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry.js +3 -2
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/settings.js +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +4 -1
- package/dist/core/extensions/builtin/btw/index.js +4 -1
- package/dist/core/extensions/builtin/compaction/lane-policy.js +3 -1
- package/dist/core/extensions/builtin/look-at/runner.js +1 -0
- package/dist/core/extensions/builtin/mcp/connection-types.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/connection.js +3 -0
- package/dist/core/extensions/builtin/mcp/elicitation.d.ts +5 -2
- package/dist/core/extensions/builtin/mcp/elicitation.js +57 -11
- package/dist/core/extensions/builtin/mcp/index.js +1 -1
- package/dist/core/extensions/builtin/mcp/modern/dispatch.js +6 -9
- package/dist/core/extensions/builtin/mcp/service-connection.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/service-connection.js +1 -0
- package/dist/core/extensions/builtin/mcp/service.js +1 -0
- package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/transport-sdk.js +5 -5
- package/dist/core/extensions/builtin/mcp/transport.d.ts +1 -0
- package/dist/core/extensions/builtin/mcp/transport.js +1 -0
- package/dist/core/external-admission.d.ts +4 -1
- package/dist/core/external-admission.js +18 -2
- package/dist/core/lifecycle-session-binding.js +20 -0
- package/dist/core/sdk.js +1 -0
- package/dist/core/session-spawned-by.d.ts +4 -0
- package/dist/core/session-spawned-by.js +16 -0
- package/dist/core/skill-access.js +15 -3
- package/dist/core/skill-discovery.d.ts +2 -0
- package/dist/core/skill-discovery.js +1 -1
- package/dist/main.js +7 -2
- package/dist/modes/rpc/session-router-open-profile.js +2 -10
- package/docs/usage.md +14 -0
- 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
|
-
/**
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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.
|
|
75
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.
|
|
76
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.
|
|
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.
|
|
96
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.
|
|
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",
|