@code-yeongyu/senpi 2026.10.23-1 → 2026.10.24-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 +3 -1
- package/dist/bundle/chunks/{chunk-TFFJEEHQ.js → chunk-3PTVLFBA.js} +1 -1
- package/dist/bundle/chunks/{chunk-52DQGTJR.js → chunk-CXED7XG5.js} +1 -1
- package/dist/bundle/chunks/{chunk-TCWZBUBG.js → chunk-FKXOOQMF.js} +1 -1
- package/dist/bundle/chunks/{chunk-7R3FUPMO.js → chunk-OS42QW66.js} +1 -1
- package/dist/bundle/chunks/{chunk-NYYDXANA.js → chunk-QEHPPMVF.js} +2 -2
- package/dist/bundle/chunks/{chunk-TFULKHNU.js → chunk-T75A2MVF.js} +2 -2
- package/dist/bundle/chunks/{chunk-E6IDMANN.js → chunk-XE753Y6U.js} +1 -1
- package/dist/bundle/chunks/{chunk-RWRICRZB.js → chunk-ZSU4SPDQ.js} +80 -80
- package/dist/bundle/chunks/{cli-main-FVJRIDEL.js → cli-main-DQBJUCQS.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-I7W5WSGN.js → host-session-command-MMYPIHCX.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-2GTQWY5I.js → interactive-mode-KIYNWS2N.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-4DDNG3S4.js → multi-session-host-X2XCJ7BZ.js} +4 -4
- package/dist/bundle/chunks/{package-manager-cli-VMY6B2WQ.js → package-manager-cli-UQMDWNHH.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-CN5HBSB3.js → rpc-mode-I7CRGV4X.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-I3IYHTWW.js → schedule-command-6LF6KHSF.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-W3EL4JYJ.js → session-control-endpoint-VV6PUFSF.js} +1 -1
- package/dist/bundle/chunks/{session-picker-HES2YE6K.js → session-picker-QZZ3DJER.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +143 -143
- 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/core/lifecycle-ledger.d.ts +3 -0
- package/dist/core/lifecycle-ledger.js +18 -1
- package/dist/core/lifecycle-session-binding.js +1 -0
- package/dist/core/session-holders.js +2 -1
- package/dist/core/session-lifecycle.d.ts +3 -0
- package/dist/core/session-lifecycle.js +11 -1
- package/dist/core/session-move/export.d.ts +3 -3
- package/dist/core/session-move/export.js +32 -27
- package/dist/core/session-move/extension-state.d.ts +5 -5
- package/dist/core/session-move/extension-state.js +25 -25
- package/dist/core/session-move/finish.d.ts +1 -1
- package/dist/core/session-move/finish.js +14 -13
- package/dist/core/session-move/import.js +37 -36
- package/dist/core/session-move/move-ledger.d.ts +3 -3
- package/dist/core/session-move/move-ledger.js +7 -6
- package/dist/core/session-move/requeue.js +1 -1
- package/dist/core/session-move/revoke.d.ts +1 -1
- package/dist/core/session-move/revoke.js +5 -5
- package/dist/core/session-move/session-lock.d.ts +4 -3
- package/dist/core/session-move/session-lock.js +23 -9
- package/dist/core/session-rebind.d.ts +1 -1
- package/dist/core/session-rebind.js +61 -31
- package/dist/modes/rpc/session-move-commands.js +16 -15
- package/dist/modes/rpc/session-release.js +3 -3
- package/docs/release-guide.md +1 -1
- package/package.json +6 -6
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { cp, mkdir, open, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
2
|
import { basename, dirname, join } from "node:path";
|
|
4
3
|
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
5
4
|
import { compareRepositoryIdentities, parseRepositoryIdentity, REPOSITORY_IDENTITY_ENTRY_TYPE, readRepositoryIdentity, } from "./repository-identity.js";
|
|
6
5
|
import { withSessionMoveLock } from "./session-holders.js";
|
|
7
|
-
import { getDefaultSessionDir,
|
|
6
|
+
import { getDefaultSessionDir, loadEntriesFromFileAsync } from "./session-manager.js";
|
|
8
7
|
import { encodedSessionId } from "./session-sidecar-store.js";
|
|
9
|
-
|
|
8
|
+
import { pathExists } from "./tools/path-utils.js";
|
|
9
|
+
export async function readRecordedRepositoryIdentity(sessionFile) {
|
|
10
10
|
let recorded;
|
|
11
|
-
for (const entry of
|
|
11
|
+
for (const entry of await loadEntriesFromFileAsync(sessionFile)) {
|
|
12
12
|
if (entry.type === "custom" && entry.customType === REPOSITORY_IDENTITY_ENTRY_TYPE) {
|
|
13
13
|
recorded = parseRepositoryIdentity(entry.data) ?? recorded;
|
|
14
14
|
}
|
|
@@ -23,12 +23,12 @@ export function readRecordedRepositoryIdentity(sessionFile) {
|
|
|
23
23
|
export async function classifySessionRepository(sessionFile, sessionCwd, cwd, read = readRepositoryIdentity) {
|
|
24
24
|
const [current, live] = await Promise.all([
|
|
25
25
|
read(cwd),
|
|
26
|
-
|
|
26
|
+
pathExists(sessionCwd).then((exists) => (exists ? read(sessionCwd) : undefined)),
|
|
27
27
|
]);
|
|
28
|
-
return compareRepositoryIdentities(live ?? readRecordedRepositoryIdentity(sessionFile), current);
|
|
28
|
+
return compareRepositoryIdentities(live ?? (await readRecordedRepositoryIdentity(sessionFile)), current);
|
|
29
29
|
}
|
|
30
30
|
export async function readSessionCwd(sessionFile) {
|
|
31
|
-
const { cwd } =
|
|
31
|
+
const { cwd } = await readHeader(resolvePath(sessionFile));
|
|
32
32
|
return typeof cwd === "string" && cwd !== "" ? cwd : undefined;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
@@ -46,7 +46,7 @@ export async function rebindSessionFile(sourcePath, targetCwd, sessionDir, optio
|
|
|
46
46
|
const cwd = resolvePath(targetCwd);
|
|
47
47
|
const targetDir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
|
|
48
48
|
const target = join(targetDir, basename(source));
|
|
49
|
-
if (target !== source &&
|
|
49
|
+
if (target !== source && (await movedAlready(source, target)))
|
|
50
50
|
return target;
|
|
51
51
|
const header = await readHeaderBeforeLock(source, target);
|
|
52
52
|
if (header === undefined)
|
|
@@ -60,36 +60,66 @@ export async function rebindSessionFile(sourcePath, targetCwd, sessionDir, optio
|
|
|
60
60
|
// missing source next to an existing target means the move is already done (`undefined`).
|
|
61
61
|
async function readHeaderBeforeLock(source, target) {
|
|
62
62
|
try {
|
|
63
|
-
return
|
|
63
|
+
return await readHeader(source);
|
|
64
64
|
}
|
|
65
65
|
catch (error) {
|
|
66
|
-
if (error.code === "ENOENT" && target !== source &&
|
|
66
|
+
if (error.code === "ENOENT" && target !== source && (await pathExists(target)))
|
|
67
67
|
return undefined;
|
|
68
68
|
throw error;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
async function moveSessionFile(source, target, cwd) {
|
|
72
|
-
if (target !== source &&
|
|
72
|
+
if (target !== source && (await movedAlready(source, target)))
|
|
73
73
|
return target;
|
|
74
74
|
const { header, rest } = await readHeaderLine(source);
|
|
75
|
-
if (target !== source &&
|
|
75
|
+
if (target !== source && (await pathExists(target))) {
|
|
76
76
|
throw new Error(`Cannot rebind session: ${target} already exists`);
|
|
77
77
|
}
|
|
78
78
|
const targetDir = dirname(target);
|
|
79
|
-
|
|
79
|
+
await mkdir(targetDir, { recursive: true });
|
|
80
80
|
const temp = join(targetDir, `.${basename(source)}.rebind-${process.pid}.tmp`);
|
|
81
81
|
await writeFile(temp, `${JSON.stringify({ ...header, cwd })}\n${rest}`, { flag: "wx" });
|
|
82
82
|
if (target !== source)
|
|
83
|
-
moveSessionSidecars(dirname(source), targetDir, header.id);
|
|
84
|
-
|
|
83
|
+
await moveSessionSidecars(dirname(source), targetDir, header.id);
|
|
84
|
+
await rename(temp, target);
|
|
85
85
|
if (target !== source)
|
|
86
|
-
|
|
86
|
+
await rm(source, { force: true });
|
|
87
87
|
return target;
|
|
88
88
|
}
|
|
89
|
+
/** A concurrent rebind writes the target before it removes the source: no source next to a target means done. */
|
|
90
|
+
async function movedAlready(source, target) {
|
|
91
|
+
return !(await pathExists(source)) && (await pathExists(target));
|
|
92
|
+
}
|
|
89
93
|
async function readHeaderLine(sessionFile) {
|
|
90
94
|
const content = await readFile(sessionFile, "utf8");
|
|
91
95
|
const newline = content.indexOf("\n");
|
|
92
96
|
const firstLine = newline === -1 ? content : content.slice(0, newline);
|
|
97
|
+
return { header: parseHeader(firstLine, sessionFile), rest: newline === -1 ? "" : content.slice(newline + 1) };
|
|
98
|
+
}
|
|
99
|
+
const HEADER_CHUNK_BYTES = 64 * 1024;
|
|
100
|
+
/** The header alone: reads up to the first newline, never the transcript after it. */
|
|
101
|
+
async function readHeader(sessionFile) {
|
|
102
|
+
const handle = await open(sessionFile, "r");
|
|
103
|
+
try {
|
|
104
|
+
const chunks = [];
|
|
105
|
+
const buffer = Buffer.allocUnsafe(HEADER_CHUNK_BYTES);
|
|
106
|
+
while (true) {
|
|
107
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
|
|
108
|
+
if (bytesRead === 0)
|
|
109
|
+
break;
|
|
110
|
+
const chunk = buffer.subarray(0, bytesRead);
|
|
111
|
+
const newline = chunk.indexOf(0x0a);
|
|
112
|
+
chunks.push(Buffer.from(newline === -1 ? chunk : chunk.subarray(0, newline)));
|
|
113
|
+
if (newline !== -1)
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
return parseHeader(Buffer.concat(chunks).toString("utf8"), sessionFile);
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await handle.close();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function parseHeader(firstLine, sessionFile) {
|
|
93
123
|
let parsed;
|
|
94
124
|
try {
|
|
95
125
|
parsed = JSON.parse(firstLine);
|
|
@@ -100,7 +130,7 @@ async function readHeaderLine(sessionFile) {
|
|
|
100
130
|
if (!isSessionHeader(parsed)) {
|
|
101
131
|
throw new Error(`Cannot rebind session: ${sessionFile} does not start with a session header`);
|
|
102
132
|
}
|
|
103
|
-
return
|
|
133
|
+
return parsed;
|
|
104
134
|
}
|
|
105
135
|
function isSessionHeader(value) {
|
|
106
136
|
return (typeof value === "object" &&
|
|
@@ -110,34 +140,34 @@ function isSessionHeader(value) {
|
|
|
110
140
|
}
|
|
111
141
|
// Per-session extension state lives at `<sessionDir>/extensions/<extension>/<encoded id>.json`
|
|
112
142
|
// (goal, loop, terminal monitors); without it a rebound session would lose its goal and monitors.
|
|
113
|
-
function moveSessionSidecars(fromDir, toDir, sessionId) {
|
|
143
|
+
async function moveSessionSidecars(fromDir, toDir, sessionId) {
|
|
114
144
|
const extensionsDir = join(fromDir, "extensions");
|
|
115
145
|
const sidecarName = `${encodedSessionId(sessionId)}.json`;
|
|
116
|
-
if (
|
|
117
|
-
for (const extension of
|
|
146
|
+
if (await pathExists(extensionsDir)) {
|
|
147
|
+
for (const extension of await readdir(extensionsDir, { withFileTypes: true })) {
|
|
118
148
|
if (!extension.isDirectory())
|
|
119
149
|
continue;
|
|
120
150
|
const from = join(extensionsDir, extension.name, sidecarName);
|
|
121
|
-
if (
|
|
122
|
-
movePath(from, join(toDir, "extensions", extension.name, sidecarName));
|
|
151
|
+
if (await pathExists(from))
|
|
152
|
+
await movePath(from, join(toDir, "extensions", extension.name, sidecarName));
|
|
123
153
|
}
|
|
124
154
|
}
|
|
125
155
|
const blobs = join(fromDir, "resident-blobs", sessionId);
|
|
126
|
-
if (
|
|
127
|
-
movePath(blobs, join(toDir, "resident-blobs", sessionId));
|
|
156
|
+
if (await pathExists(blobs))
|
|
157
|
+
await movePath(blobs, join(toDir, "resident-blobs", sessionId));
|
|
128
158
|
}
|
|
129
|
-
function movePath(from, to) {
|
|
130
|
-
if (
|
|
159
|
+
async function movePath(from, to) {
|
|
160
|
+
if (await pathExists(to))
|
|
131
161
|
return;
|
|
132
|
-
|
|
162
|
+
await mkdir(dirname(to), { recursive: true });
|
|
133
163
|
try {
|
|
134
|
-
|
|
164
|
+
await rename(from, to);
|
|
135
165
|
}
|
|
136
166
|
catch (error) {
|
|
137
167
|
if (error.code !== "EXDEV")
|
|
138
168
|
throw error;
|
|
139
|
-
|
|
140
|
-
|
|
169
|
+
await cp(from, to, { recursive: true });
|
|
170
|
+
await rm(from, { recursive: true, force: true });
|
|
141
171
|
}
|
|
142
172
|
}
|
|
143
173
|
//# sourceMappingURL=session-rebind.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* outgoing bundles and the session folder for a working directory) and maps refusals to wire codes.
|
|
5
5
|
*/
|
|
6
6
|
import { isAbsolute, join } from "node:path";
|
|
7
|
-
import { getDefaultSessionDir,
|
|
7
|
+
import { getDefaultSessionDir, loadEntriesFromFileAsync } from "../../core/session-manager.js";
|
|
8
8
|
import { parseMoveId } from "../../core/session-move/bundle.js";
|
|
9
9
|
import { exportSessionForMove } from "../../core/session-move/export.js";
|
|
10
10
|
import { sealedOutgoingStateDir } from "../../core/session-move/extension-state.js";
|
|
@@ -13,7 +13,7 @@ import { answerMoveStatus, importMovedSession } from "../../core/session-move/im
|
|
|
13
13
|
import { MoveLedger, parseMoveStatus } from "../../core/session-move/move-ledger.js";
|
|
14
14
|
import { headerSessionId, sessionOwnership } from "../../core/session-move/ownership.js";
|
|
15
15
|
import { revokeImportedMove } from "../../core/session-move/revoke.js";
|
|
16
|
-
import { realSessionPath } from "../../core/session-move/session-lock.js";
|
|
16
|
+
import { orderedRealPath, realSessionPath } from "../../core/session-move/session-lock.js";
|
|
17
17
|
import { normalizePath, resolvePath } from "../../utils/paths.js";
|
|
18
18
|
import { RPC_ERROR_INVALID_MOVE_REQUEST, RPC_ERROR_MOVE_FAILED, RPC_ERROR_MOVE_NOT_RELEASED, RPC_ERROR_SESSION_OPEN_HERE, } from "./session-move-wire.js";
|
|
19
19
|
/** Refusal codes the move core throws (`error.code`), passed through to the wire as they are. */
|
|
@@ -113,7 +113,7 @@ async function run(parsed, port, ledger, outgoing) {
|
|
|
113
113
|
case "status":
|
|
114
114
|
return answerMoveStatus(ledger, parsed.moveId, parsed.sessionPath);
|
|
115
115
|
case "export": {
|
|
116
|
-
const sessionFile =
|
|
116
|
+
const sessionFile = await orderedRealPath(parsed.sessionPath);
|
|
117
117
|
const bundleDir = outgoing(parsed.moveId);
|
|
118
118
|
const manifest = await exportSessionForMove({
|
|
119
119
|
sessionFile,
|
|
@@ -122,9 +122,9 @@ async function run(parsed, port, ledger, outgoing) {
|
|
|
122
122
|
droppedUserMessages: parsed.dropped,
|
|
123
123
|
bundleDir,
|
|
124
124
|
extensionStateDir: () => sealedOutgoingStateDir(sessionFile, parsed.moveId),
|
|
125
|
-
guard: () => {
|
|
126
|
-
refuseOpenHere(port, sessionFile, "This session is open on this host. Release it with release_session reason move and this moveId first.");
|
|
127
|
-
refuseUnlessSealedFor(sessionFile, parsed.moveId);
|
|
125
|
+
guard: async () => {
|
|
126
|
+
await refuseOpenHere(port, sessionFile, "This session is open on this host. Release it with release_session reason move and this moveId first.");
|
|
127
|
+
await refuseUnlessSealedFor(sessionFile, parsed.moveId);
|
|
128
128
|
},
|
|
129
129
|
// Again inside the cross-process lock: a finish from another generation may have reclaimed it meanwhile.
|
|
130
130
|
lockedGuard: () => refuseUnlessSealedFor(sessionFile, parsed.moveId),
|
|
@@ -146,13 +146,13 @@ async function run(parsed, port, ledger, outgoing) {
|
|
|
146
146
|
sessionDir,
|
|
147
147
|
from: parsed.from,
|
|
148
148
|
});
|
|
149
|
-
const sessionId = headerSessionId(
|
|
149
|
+
const sessionId = headerSessionId(await loadEntriesFromFileAsync(sessionFile));
|
|
150
150
|
if (sessionId === undefined)
|
|
151
151
|
throw new Error(`The imported session at ${sessionFile} has no id in its header.`);
|
|
152
152
|
return { session_id: sessionId, session_path: sessionFile };
|
|
153
153
|
}
|
|
154
154
|
case "finish": {
|
|
155
|
-
const sessionFile =
|
|
155
|
+
const sessionFile = await orderedRealPath(parsed.sessionPath);
|
|
156
156
|
await finishMove({
|
|
157
157
|
sessionFile,
|
|
158
158
|
bundleDir: outgoing(parsed.moveId),
|
|
@@ -163,7 +163,7 @@ async function run(parsed, port, ledger, outgoing) {
|
|
|
163
163
|
return ownershipOf(sessionFile);
|
|
164
164
|
}
|
|
165
165
|
case "revoke": {
|
|
166
|
-
const sessionFile =
|
|
166
|
+
const sessionFile = await orderedRealPath(parsed.sessionPath);
|
|
167
167
|
await revokeImportedMove({
|
|
168
168
|
sessionFile,
|
|
169
169
|
moveId: parsed.moveId,
|
|
@@ -174,8 +174,8 @@ async function run(parsed, port, ledger, outgoing) {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
function ownershipOf(sessionFile) {
|
|
178
|
-
const ownership = sessionOwnership(
|
|
177
|
+
async function ownershipOf(sessionFile) {
|
|
178
|
+
const ownership = sessionOwnership(await loadEntriesFromFileAsync(sessionFile));
|
|
179
179
|
return { ownership: ownership.writable ? "writable" : ownership.state };
|
|
180
180
|
}
|
|
181
181
|
class MoveRefusal extends Error {
|
|
@@ -185,13 +185,14 @@ class MoveRefusal extends Error {
|
|
|
185
185
|
this.code = code;
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
-
function refuseUnlessSealedFor(sessionFile, moveId) {
|
|
189
|
-
const ownership = sessionOwnership(
|
|
188
|
+
async function refuseUnlessSealedFor(sessionFile, moveId) {
|
|
189
|
+
const ownership = sessionOwnership(await loadEntriesFromFileAsync(sessionFile));
|
|
190
190
|
if (ownership.writable || ownership.state !== "moving" || ownership.moveId !== moveId)
|
|
191
191
|
throw new MoveRefusal(RPC_ERROR_MOVE_NOT_RELEASED, "This session file is not sealed for this move. Release it with release_session reason move and this moveId first; that also hands back its queued input.");
|
|
192
192
|
}
|
|
193
|
-
function refuseOpenHere(port, sessionFile, detail) {
|
|
194
|
-
|
|
193
|
+
async function refuseOpenHere(port, sessionFile, detail) {
|
|
194
|
+
const hosted = await Promise.all(port.hostedPaths().map((path) => realSessionPath(path)));
|
|
195
|
+
if (hosted.includes(sessionFile))
|
|
195
196
|
throw new MoveRefusal(RPC_ERROR_SESSION_OPEN_HERE, detail);
|
|
196
197
|
}
|
|
197
198
|
function answer(id, data) {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Also refused while clients are attached (`attached`, unless `force`) and for a session this host
|
|
22
22
|
* cannot hand over (`release_unsupported`: a worker isolate owns the runtime, or there is no file).
|
|
23
23
|
*/
|
|
24
|
-
import {
|
|
24
|
+
import { rm } from "node:fs/promises";
|
|
25
25
|
import { isMoveId, isPlainId, parseMoveId } from "../../core/session-move/bundle.js";
|
|
26
26
|
import { prepareOutgoingState, STATE_ATTEMPT_FIELD } from "../../core/session-move/extension-state.js";
|
|
27
27
|
import { SessionMovedAwayError } from "../../core/session-move/ownership.js";
|
|
@@ -79,7 +79,7 @@ export async function releaseSession(port, command) {
|
|
|
79
79
|
// Each handler runs under the session_shutdown handler budget: a hung one fails the release instead of holding it.
|
|
80
80
|
if (command.reason === "move") {
|
|
81
81
|
const moveId = parseMoveId(command.moveId);
|
|
82
|
-
stagedState = prepareOutgoingState(first.sessionPath, moveId);
|
|
82
|
+
stagedState = await prepareOutgoingState(first.sessionPath, moveId);
|
|
83
83
|
const [failed] = await first.session.extensionRunner.emitSessionMove({
|
|
84
84
|
type: "session_move_export",
|
|
85
85
|
moveId,
|
|
@@ -113,7 +113,7 @@ export async function releaseSession(port, command) {
|
|
|
113
113
|
// A move refused before its seal is abandoned: what its export staged is never bundled, and a retry stages
|
|
114
114
|
// afresh. Once the seal names this attempt, its folder is the move's state and stays for export.
|
|
115
115
|
if (stagedState !== undefined && !sealed)
|
|
116
|
-
|
|
116
|
+
await rm(stagedState.dir, { recursive: true, force: true });
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
}
|
package/docs/release-guide.md
CHANGED
|
@@ -618,7 +618,7 @@ For the next release:
|
|
|
618
618
|
|
|
619
619
|
1. Start from a clean, current `main`.
|
|
620
620
|
2. Audit every commit since the latest tag.
|
|
621
|
-
3.
|
|
621
|
+
3. Record affected-package changelog entries in `changes/unreleased/<pr>.md`; the release cut compiles them before stamping.
|
|
622
622
|
4. Duplicate user-facing AI, agent, and TUI changes into the coding-agent changelog.
|
|
623
623
|
5. Query registry-verifiable plugin dependencies for newer exact versions.
|
|
624
624
|
6. Preserve vendored snapshots when no authoritative upstream source is available.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.24-1",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@earendil-works/chord": "0.99.1",
|
|
71
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.
|
|
72
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.
|
|
73
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.
|
|
71
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.24-1",
|
|
72
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.24-1",
|
|
73
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.24-1",
|
|
74
74
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
75
75
|
"chalk": "6.0.0",
|
|
76
76
|
"cross-spawn": "7.0.6",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"yaml": "2.9.1",
|
|
90
90
|
"@anthropic-ai/claude-agent-sdk": "0.3.306",
|
|
91
91
|
"@anthropic-ai/sdk": "0.127.0",
|
|
92
|
-
"@code-yeongyu/senpi-codemode": "2026.10.
|
|
93
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.
|
|
92
|
+
"@code-yeongyu/senpi-codemode": "2026.10.24-1",
|
|
93
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.24-1",
|
|
94
94
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
95
95
|
"@mozilla/readability": "0.6.0",
|
|
96
96
|
"linkedom": "0.18.13",
|