@code-yeongyu/senpi 2026.10.7 → 2026.10.9
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 +34 -0
- package/dist/bundle/chunks/{anthropic-messages-4MNFPQ66.js → anthropic-messages-CVXI6CLT.js} +1 -1
- package/dist/bundle/chunks/{app-server-command-SO5RJ2N7.js → app-server-command-2HNDZGTR.js} +1 -1
- package/dist/bundle/chunks/{chunk-BE66QJ3Z.js → chunk-3YXB7Z7O.js} +1 -1
- package/dist/bundle/chunks/{chunk-JJNIU2A2.js → chunk-CB77NQMN.js} +1 -1
- package/dist/bundle/chunks/{chunk-EUU6W5B3.js → chunk-DERNLWDS.js} +43 -43
- package/dist/bundle/chunks/{chunk-K4AZKFRB.js → chunk-J5MUMVWB.js} +2 -2
- package/dist/bundle/chunks/{chunk-D2W3MXRF.js → chunk-LSE7KE2C.js} +1 -1
- package/dist/bundle/chunks/{chunk-V2VQ4BHG.js → chunk-M2IHGL3M.js} +1 -1
- package/dist/bundle/chunks/{chunk-7SMFF2QJ.js → chunk-M3BQ2PAQ.js} +4 -4
- package/dist/bundle/chunks/{chunk-HY4AOLON.js → chunk-MJGHZT4N.js} +3 -3
- package/dist/bundle/chunks/{chunk-7A3WRAKP.js → chunk-Q2ZTPMWD.js} +1 -1
- package/dist/bundle/chunks/{chunk-ESSQUSRC.js → chunk-SB22X6ZR.js} +1 -1
- package/dist/bundle/chunks/{cli-main-XBQN73O2.js → cli-main-VMDAGZCA.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-FUMH5NSA.js → help-fast-path-JBQLYO7W.js} +1 -1
- package/dist/bundle/chunks/{host-command-YXQAXNKK.js → host-command-HTYYQBSG.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-L4XQIATP.js → host-lifecycle-XZF6IKKO.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +3 -3
- package/dist/bundle/chunks/{interactive-mode-ROO5W56P.js → interactive-mode-U7IH4VMJ.js} +1 -1
- package/dist/bundle/chunks/{list-tips-JQDZPB6C.js → list-tips-IIRFXBTO.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-5WTKAQJM.js → multi-session-host-HHIC6N7C.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-NCD5FQGV.js → package-manager-cli-2FXWPM2U.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-7E37KOCX.js → rotation-stream-ZJQ6I2RZ.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-MWURKCP2.js → rpc-mode-K5YGBGQO.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-UTXB465F.js → schedule-command-CVAPK2WQ.js} +1 -1
- package/dist/bundle/chunks/{sdk-EO76I33H.js → sdk-U52J75K2.js} +50 -50
- package/dist/bundle/chunks/{session-control-endpoint-J6RHZXWB.js → session-control-endpoint-THEF3K7M.js} +1 -1
- package/dist/bundle/chunks/{session-picker-VTTRFGE7.js → session-picker-R5N2EKUS.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +104 -104
- package/dist/bundle/cli.js +1 -1
- 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/agent-session.d.ts +7 -2
- package/dist/core/agent-session.js +31 -5
- package/dist/core/continue-from-leaf.d.ts +21 -0
- package/dist/core/continue-from-leaf.js +39 -0
- package/dist/core/dynamic-prompt/handoff.js +3 -3
- package/dist/core/dynamic-prompt/intent-gate.js +1 -1
- package/dist/core/dynamic-prompt/style.js +1 -1
- package/dist/core/extensions/builtin/permission-system/index.js +4 -0
- package/dist/core/extensions/builtin/permission-system/prompt.js +10 -2
- package/dist/core/extensions/builtin/permission-system/types.d.ts +3 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/gpt-surface.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +1 -1
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +2 -2
- package/dist/core/extensions/types.d.ts +4 -0
- package/dist/modes/rpc/connection-handler.js +11 -2
- package/dist/modes/rpc/host-lifecycle.js +5 -28
- package/dist/modes/rpc/host-run-activity.d.ts +13 -0
- package/dist/modes/rpc/host-run-activity.js +39 -0
- package/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/docs/rpc.md +13 -0
- package/package.json +7 -7
|
@@ -121,11 +121,11 @@ export declare const GPT6_ASTRA_RULES: readonly [{
|
|
|
121
121
|
}, {
|
|
122
122
|
readonly id: "handoff-report";
|
|
123
123
|
readonly concern: "reporting";
|
|
124
|
-
readonly directive: "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message; the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n
|
|
124
|
+
readonly directive: "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself); the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n[Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].\n\nNow and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration. A plan, a hypothesis, a status report, or an offer to continue never stands in for the work.";
|
|
125
125
|
}, {
|
|
126
126
|
readonly id: "final-message-shape";
|
|
127
127
|
readonly concern: "reporting";
|
|
128
|
-
readonly directive: "The final message is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
|
|
128
|
+
readonly directive: "The final message of work is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
|
|
129
129
|
}];
|
|
130
130
|
export declare function buildGpt6AstraPrompt(options: BuildDynamicSystemPromptOptions): string;
|
|
131
131
|
//# sourceMappingURL=gpt-6-astra.d.ts.map
|
|
@@ -206,8 +206,8 @@ const PLAIN_PROSE = "Write the way a careful engineer writes to a colleague: pla
|
|
|
206
206
|
const SLOP_BAN = 'Leave out stock phrases and filler: "delve", "leverage", "foster", "it\'s worth noting", "importantly", "genuinely", "Bottom line:", "In short:", "The simplest mental model is:", "Question? Answer." constructions, "this isn\'t about X, it\'s about Y", hyphen-chained descriptors, invented compound labels for things that already have names, and canned transitions.';
|
|
207
207
|
const DIRECT_STATEMENTS = "State the action or finding directly and connect it to its purpose or consequence. Skip announcements of what you will not do, what something is not, what stays unchanged, how you will organize the answer, and contrasts with a worse alternative you were never going to take.";
|
|
208
208
|
const NO_REFLEXIVE_APOLOGY = "Apologize or fault yourself only for an avoidable mistake of your own, and then plainly: acknowledge it, correct it, move on. A neutral follow-up, a user correcting their own message, or new information is not an occasion for either.";
|
|
209
|
-
const HANDOFF_REPORT = "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message; the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n
|
|
210
|
-
const FINAL_MESSAGE_SHAPE = "The final message is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
|
|
209
|
+
const HANDOFF_REPORT = "At a handoff - the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself); the routing line is not one - first work out what the user asked for and what they need to know now, then open with one block:\n\n[Outcome so far] toward [the user's original ask and the result they wanted]. You need: [ledger N/M done, findings, blockers]. Now: [todo task in progress]. Next: [next open task].\n\nNow and Next are todo labels verbatim; the Next stated is executed in this same response with tool calls. Between handoffs, no narration. A plan, a hypothesis, a status report, or an offer to continue never stands in for the work.";
|
|
210
|
+
const FINAL_MESSAGE_SHAPE = "The final message of work is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs to trust it - the checks that ran, summarized rather than listed, anything left unverified, and any pre-existing problem you left in place - ordered so the conclusion is easiest to check rather than in the order you worked. Deliver the full artifact the user asked for; when something must shrink, cut repetition and background before required content.";
|
|
211
211
|
export const GPT6_ASTRA_RULES = [
|
|
212
212
|
{ id: "initiative-bias", concern: "initiative", directive: INITIATIVE_BIAS },
|
|
213
213
|
{ id: "approval-last", concern: "initiative", directive: APPROVAL_LAST },
|
|
@@ -244,7 +244,7 @@ export const GPT6_ASTRA_RULES = [
|
|
|
244
244
|
const INTENT_GATE_LEAD = {
|
|
245
245
|
terminal: `Open a new request with one short routing line:
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this task].
|
|
248
248
|
|
|
249
249
|
The declared stop condition is binding: work until it holds, then stop (see Stop Goal).`,
|
|
250
250
|
app: "Open a new request by settling the exact, observable condition that ends the task. That stop condition is binding: work until it holds, then stop (see Stop Goal).",
|
|
@@ -262,7 +262,7 @@ const SURFACE_DIRECTIVE = {
|
|
|
262
262
|
chat: {
|
|
263
263
|
steering: APP_STEERING,
|
|
264
264
|
handoffReport: HANDOFF_REPORT.replace(/^[\s\S]*Between handoffs, no narration\. /, `${CHAT_REPLY_RULE} `),
|
|
265
|
-
finalMessageShape: APP_FINAL_MESSAGE_SHAPE.replace("The final message is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs", `${CHAT_FINAL_MESSAGE} and stands alone: the outcome first, then what a reader needs`),
|
|
265
|
+
finalMessageShape: APP_FINAL_MESSAGE_SHAPE.replace("The final message of work is the handoff block and stands alone: the outcome first, then in its You need slot what a reader needs", `${CHAT_FINAL_MESSAGE} and stands alone: the outcome first, then what a reader needs`),
|
|
266
266
|
},
|
|
267
267
|
};
|
|
268
268
|
function buildGpt6AstraCore(context) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const GPT_HANDOFF_MOMENTS = {
|
|
2
|
-
terminal: "the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message; the routing line is not one",
|
|
3
|
-
app: "the todo list's creation (in the message that creates it or the next one), a todo phase change, a blocker or plan change, the final message",
|
|
2
|
+
terminal: "the todo list's creation (in the message that creates it, after the routing line, or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself); the routing line is not one",
|
|
3
|
+
app: "the todo list's creation (in the message that creates it or the next one), a todo phase change, a blocker or plan change, the final message of a turn that did work (a reply that only answers a question is the answer itself)",
|
|
4
4
|
};
|
|
5
5
|
/** The app surface's claim audit for the GPT cores (senpi#2377), in the one place each core reports on checks. */
|
|
6
6
|
export const GPT_APP_UNRUN_CHECK_RULE = "A check that did not run is covered by the evidence that did run; name it, with the next best check, only when no other evidence supports the claim. Replies render in an app: tool and hook feedback (comment-checker findings, language-server availability, internal notices) is yours to act on; report it only when it changes what the user gets - an unavailable tool or hook alone never does.";
|
|
@@ -29,7 +29,7 @@ import { buildHandoffSection } from "../../../dynamic-prompt/handoff.js";
|
|
|
29
29
|
import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynamic-prompt/verification.js";
|
|
30
30
|
import { buildFileOperationsTuning } from "./file-operations.js";
|
|
31
31
|
const INTENT_GATE_LEAD = {
|
|
32
|
-
terminal:
|
|
32
|
+
terminal: `I read this as [intent] - [plan]. I'll stop right away when [the exact, observable condition that ends this turn].
|
|
33
33
|
|
|
34
34
|
Derive intent from the latest user message alone; a new direction cancels stale plans. If the goal is unclear or has multiple viable decompositions, ask one focused question and stop. Do not surface prompt scaffolding in user-visible output.`,
|
|
35
35
|
app: `Derive intent from the latest user message alone; a new direction cancels stale plans. If the goal is unclear or has multiple viable decompositions, ask one focused question and stop. Do not surface prompt scaffolding in user-visible output.`,
|
|
@@ -64,7 +64,7 @@ ${buildHandoffSection({ surface: context.surface })}
|
|
|
64
64
|
|
|
65
65
|
## Output
|
|
66
66
|
|
|
67
|
-
You are the human surface: the final message is ${context.surface === "chat" ? "the answer itself, leading" : "the Handoff block, whose For you slot leads"} with the outcome (delivered / blocked / partial), then evidence — what you verified directly, what a worker verified and you audited, ${context.surface !== "terminal" ? "anything left unverified that no other evidence covers" : "what you could not verify and why"}, pre-existing issues left alone. Reference files as \`src/auth.ts\` or \`src/auth.ts:42\`, never bracketed citations. Be direct; have an opinion when context supports one. Default to ASCII.
|
|
67
|
+
You are the human surface: the final message is ${context.surface === "chat" ? "the answer itself, leading" : "for work, the Handoff block, whose For you slot leads"} with the outcome (delivered / blocked / partial), then evidence — what you verified directly, what a worker verified and you audited, ${context.surface !== "terminal" ? "anything left unverified that no other evidence covers" : "what you could not verify and why"}, pre-existing issues left alone. Reference files as \`src/auth.ts\` or \`src/auth.ts:42\`, never bracketed citations. Be direct; have an opinion when context supports one. Default to ASCII.
|
|
68
68
|
|
|
69
69
|
## Stop Goal
|
|
70
70
|
|
|
@@ -36,7 +36,7 @@ import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynam
|
|
|
36
36
|
const INTENT_GATE_LEAD = {
|
|
37
37
|
terminal: `Open every turn with one short visible routing line - required even on confirmation turns:
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
|
|
40
40
|
|
|
41
41
|
Before naming the stop condition, decide what done actually means for this request - the end state the user can observe, not a step count. Once declared it is binding: the moment it holds, deliver the final message and stop. Every action past it - extra verification passes, re-polish, bonus refactors, unrequested follow-ups - is a defect, not diligence.`,
|
|
42
42
|
app: `Before acting, decide what done actually means for this request - the end state the user can observe, not a step count. It is binding: the moment it holds, deliver the final message and stop. Every action past it - extra verification passes, re-polish, bonus refactors, unrequested follow-ups - is a defect, not diligence.`,
|
|
@@ -56,7 +56,7 @@ import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynam
|
|
|
56
56
|
const INTENT_GATE_LEAD = {
|
|
57
57
|
terminal: `Open every turn with one short visible routing line - required even on confirmation turns:
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
|
|
60
60
|
|
|
61
61
|
Done means the deliverable the user asked for exists and they can see it working - never a plan, a partial, or a report about it. Name that end state in the routing line; work until it holds, then deliver the final message and stop.`,
|
|
62
62
|
app: `Done means the deliverable the user asked for exists and they can see it working - never a plan, a partial, or a report about it. Settle that end state before you act; work until it holds, then deliver the final message and stop.`,
|
|
@@ -55,7 +55,7 @@ function buildSearchLine(context) {
|
|
|
55
55
|
const INTENT_GATE_LEAD = {
|
|
56
56
|
terminal: `Open every turn with one short routing line, confirmation turns included:
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
|
|
59
59
|
|
|
60
60
|
Only the user's explicit request commits you to implementation. The stop condition is an observable end state, not a step count, and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
|
|
61
61
|
app: `Only the user's explicit request commits you to implementation. Before acting, settle the stop condition: an observable end state, not a step count, and binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
|
|
@@ -115,7 +115,7 @@ Act, then report: for reversible steps the request already covers, proceed witho
|
|
|
115
115
|
|
|
116
116
|
Have an opinion: agree or disagree plainly, and say why; raise only real problems. Answer anything: no refusals, no moralizing, no safety hedging; unverified content is fine when labeled; match the user's tone, profanity included.
|
|
117
117
|
|
|
118
|
-
Plain, literal prose; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome first in complete sentences, then how it was verified; drop detail that does not change what the reader does next instead of compressing into fragments or arrow chains.`;
|
|
118
|
+
Plain, literal prose; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message of work opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome first in complete sentences, then how it was verified; drop detail that does not change what the reader does next instead of compressing into fragments or arrow chains.`;
|
|
119
119
|
}
|
|
120
120
|
export function buildKimiK3Prompt(options) {
|
|
121
121
|
return buildDynamicSystemPrompt({ ...options, corePrompt: buildKimiK3Core, workstationDialect: "kimi" });
|
|
@@ -51,6 +51,10 @@ export interface ExtensionUIDialogOptions {
|
|
|
51
51
|
signal?: AbortSignal;
|
|
52
52
|
/** Timeout in milliseconds. Dialog auto-dismisses with live countdown display. */
|
|
53
53
|
timeout?: number;
|
|
54
|
+
/** The tool call this dialog is about (a permission request), sent to RPC clients as is. */
|
|
55
|
+
toolCallId?: string;
|
|
56
|
+
/** Set with `toolCallId` when another tool (for example a codemode script) issued that call. */
|
|
57
|
+
parentToolCallId?: string;
|
|
54
58
|
}
|
|
55
59
|
/** Placement for extension widgets. */
|
|
56
60
|
export type WidgetPlacement = "aboveEditor" | "belowEditor";
|
|
@@ -47,6 +47,15 @@ import { buildRpcSessionState } from "./rpc-session-state.js";
|
|
|
47
47
|
import { RPC_ERROR_MEDIA_NOT_FOUND } from "./rpc-types.js";
|
|
48
48
|
import { SessionExtensionUiRequests } from "./session-extension-ui-requests.js";
|
|
49
49
|
export { buildRpcSessionState } from "./rpc-session-state.js";
|
|
50
|
+
/** The tool call a dialog is about, as wire fields (absent when the dialog is not a permission request). */
|
|
51
|
+
function dialogCall(opts) {
|
|
52
|
+
if (opts?.toolCallId === undefined)
|
|
53
|
+
return {};
|
|
54
|
+
return {
|
|
55
|
+
toolCallId: opts.toolCallId,
|
|
56
|
+
...(opts.parentToolCallId === undefined ? {} : { parentToolCallId: opts.parentToolCallId }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
50
59
|
function loadedExtensionName(path) {
|
|
51
60
|
const synthetic = /^<(?:builtin|inline):([^>]+)>$/.exec(path);
|
|
52
61
|
if (synthetic)
|
|
@@ -292,9 +301,9 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
292
301
|
question: (request, opts) => clientCapabilities?.includes(QUESTION_CAPABILITY)
|
|
293
302
|
? questions.ask(request, opts)
|
|
294
303
|
: degradeQuestion(createExtensionUIContext(), request, opts),
|
|
295
|
-
select: (title, options, opts) => createDialogPromise(opts, undefined, { method: "select", title, options, timeout: opts?.timeout }, (r) => "cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined),
|
|
304
|
+
select: (title, options, opts) => createDialogPromise(opts, undefined, { method: "select", title, options, timeout: opts?.timeout, ...dialogCall(opts) }, (r) => ("cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined)),
|
|
296
305
|
confirm: (title, message, opts) => createDialogPromise(opts, false, { method: "confirm", title, message, timeout: opts?.timeout }, (r) => "cancelled" in r && r.cancelled ? false : "confirmed" in r ? r.confirmed : false),
|
|
297
|
-
input: (title, placeholder, opts) => createDialogPromise(opts, undefined, { method: "input", title, placeholder, timeout: opts?.timeout }, (r) => "cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined),
|
|
306
|
+
input: (title, placeholder, opts) => createDialogPromise(opts, undefined, { method: "input", title, placeholder, timeout: opts?.timeout, ...dialogCall(opts) }, (r) => ("cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined)),
|
|
298
307
|
notify(message, type) {
|
|
299
308
|
// Fire and forget - no response needed
|
|
300
309
|
output({
|
|
@@ -63,6 +63,7 @@ import { hostCrashCleanupPaths } from "./host-cleanup-paths.js";
|
|
|
63
63
|
import { createHostDaemonPaths, generationPaths, HOST_DAEMON_DIR_ENV } from "./host-daemon-paths.js";
|
|
64
64
|
import { releaseGeneration } from "./host-daemon-registration.js";
|
|
65
65
|
import { HOST_INSTANCE_ID_ENV } from "./host-identity-env.js";
|
|
66
|
+
import { SessionRunActivity } from "./host-run-activity.js";
|
|
66
67
|
import { watchForSupersession } from "./host-supersession.js";
|
|
67
68
|
import { HOST_CLEANUP_PATHS_ENV, HOST_PUBLIC_SOCKET_ENV, HOST_SCRATCH_DIR_ENV, HOST_WATCH_FD_ENV, HOST_WATCH_PPID_ENV, } from "./host-watchdog.js";
|
|
68
69
|
import { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from "./jsonl.js";
|
|
@@ -319,7 +320,7 @@ export async function runHostSupervisor(launch) {
|
|
|
319
320
|
const internalSecretPath = internal.secretPath ?? socketSecretPath(internalSocket);
|
|
320
321
|
const internalSecret = process.platform === "win32" ? await createSocketSecret(internalSecretPath) : undefined;
|
|
321
322
|
const clients = new ClientOccupancy(() => decider.update(currentActivity()));
|
|
322
|
-
const
|
|
323
|
+
const runs = new SessionRunActivity();
|
|
323
324
|
// Declared before anything that can reach `currentActivity()`. A client accepted during startup
|
|
324
325
|
// asks for the activity snapshot, and a `const` read before its initializer runs is a
|
|
325
326
|
// ReferenceError that fails the connection - which is how a successor's first `open_session`
|
|
@@ -461,37 +462,13 @@ export async function runHostSupervisor(launch) {
|
|
|
461
462
|
unhealthySince: observerLink.unhealthySince(),
|
|
462
463
|
now: Date.now(),
|
|
463
464
|
unknownGraceMs: decider.idleExitMs,
|
|
464
|
-
observedBusy:
|
|
465
|
+
observedBusy: runs.busySessions,
|
|
465
466
|
}),
|
|
466
467
|
};
|
|
467
468
|
}
|
|
468
|
-
function countBusySessions() {
|
|
469
|
-
let busy = 0;
|
|
470
|
-
for (const count of busySessions.values())
|
|
471
|
-
if (count > 0)
|
|
472
|
-
busy++;
|
|
473
|
-
return busy;
|
|
474
|
-
}
|
|
475
469
|
function observeHostEvent(line) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
event = JSON.parse(line);
|
|
479
|
-
}
|
|
480
|
-
catch {
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
if (typeof event !== "object" || event === null)
|
|
484
|
-
return;
|
|
485
|
-
const { type, sessionId } = event;
|
|
486
|
-
if (typeof sessionId !== "string")
|
|
487
|
-
return;
|
|
488
|
-
if (type === "agent_start")
|
|
489
|
-
busySessions.set(sessionId, (busySessions.get(sessionId) ?? 0) + 1);
|
|
490
|
-
else if (type === "agent_settled")
|
|
491
|
-
busySessions.set(sessionId, Math.max(0, (busySessions.get(sessionId) ?? 1) - 1));
|
|
492
|
-
else
|
|
493
|
-
return;
|
|
494
|
-
decider.update(currentActivity());
|
|
470
|
+
if (runs.observe(line))
|
|
471
|
+
decider.update(currentActivity());
|
|
495
472
|
}
|
|
496
473
|
async function shutdown(reason, exitCode) {
|
|
497
474
|
// Single-flight: concurrent triggers (listener error, child exit, signals)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which sessions have an agent run in progress, from the host child's events. A run emits
|
|
3
|
+
* `agent_start` on every loop iteration (a provider retry, a post-compaction continue, a follow-up)
|
|
4
|
+
* but `agent_settled` once, so this tracks runs, not starts: a repeated start for a running session
|
|
5
|
+
* changes nothing. A session that closes is dropped even if its settle never arrives (#2713).
|
|
6
|
+
*/
|
|
7
|
+
export declare class SessionRunActivity {
|
|
8
|
+
#private;
|
|
9
|
+
/** Applies one host event line; true when it is a run or session event the idle decision must re-read. */
|
|
10
|
+
observe(line: string): boolean;
|
|
11
|
+
get busySessions(): number;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=host-run-activity.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which sessions have an agent run in progress, from the host child's events. A run emits
|
|
3
|
+
* `agent_start` on every loop iteration (a provider retry, a post-compaction continue, a follow-up)
|
|
4
|
+
* but `agent_settled` once, so this tracks runs, not starts: a repeated start for a running session
|
|
5
|
+
* changes nothing. A session that closes is dropped even if its settle never arrives (#2713).
|
|
6
|
+
*/
|
|
7
|
+
export class SessionRunActivity {
|
|
8
|
+
#running = new Set();
|
|
9
|
+
/** Applies one host event line; true when it is a run or session event the idle decision must re-read. */
|
|
10
|
+
observe(line) {
|
|
11
|
+
const event = parseHostEvent(line);
|
|
12
|
+
if (event === undefined)
|
|
13
|
+
return false;
|
|
14
|
+
if (event.type === "agent_start")
|
|
15
|
+
this.#running.add(event.sessionId);
|
|
16
|
+
else if (event.type === "agent_settled" || event.type === "session_closed")
|
|
17
|
+
this.#running.delete(event.sessionId);
|
|
18
|
+
else
|
|
19
|
+
return false;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
get busySessions() {
|
|
23
|
+
return this.#running.size;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseHostEvent(line) {
|
|
27
|
+
let event;
|
|
28
|
+
try {
|
|
29
|
+
event = JSON.parse(line);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (typeof event !== "object" || event === null)
|
|
35
|
+
return undefined;
|
|
36
|
+
const { type, sessionId } = event;
|
|
37
|
+
return typeof sessionId === "string" ? { type, sessionId } : undefined;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=host-run-activity.js.map
|
|
@@ -1319,6 +1319,9 @@ export type RpcExtensionUIRequest = {
|
|
|
1319
1319
|
title: string;
|
|
1320
1320
|
options: string[];
|
|
1321
1321
|
timeout?: number;
|
|
1322
|
+
/** The tool call this dialog approves, when it is a permission request. */
|
|
1323
|
+
toolCallId?: string;
|
|
1324
|
+
parentToolCallId?: string;
|
|
1322
1325
|
} | {
|
|
1323
1326
|
type: "extension_ui_request";
|
|
1324
1327
|
id: string;
|
|
@@ -1333,6 +1336,8 @@ export type RpcExtensionUIRequest = {
|
|
|
1333
1336
|
title: string;
|
|
1334
1337
|
placeholder?: string;
|
|
1335
1338
|
timeout?: number;
|
|
1339
|
+
toolCallId?: string;
|
|
1340
|
+
parentToolCallId?: string;
|
|
1336
1341
|
} | {
|
|
1337
1342
|
type: "extension_ui_request";
|
|
1338
1343
|
id: string;
|
package/docs/rpc.md
CHANGED
|
@@ -3562,6 +3562,19 @@ Prompt the user to choose from a list. Dialog methods with a `timeout` field inc
|
|
|
3562
3562
|
|
|
3563
3563
|
Expected response: `extension_ui_response` with `value` (the selected option string) or `cancelled: true`.
|
|
3564
3564
|
|
|
3565
|
+
A permission prompt (title `Permission required: <permission>`) also carries `toolCallId`, the id of the tool call it approves, and `parentToolCallId` when another tool (for example a codemode script) issued that call. Clients bind the prompt to that call by id: the engine raises the prompts for every call of a message before any of them runs, so the order of `tool_execution_start` events does not say which call a prompt belongs to. The `input` that follows a "Deny with feedback" choice carries the same fields.
|
|
3566
|
+
|
|
3567
|
+
```json
|
|
3568
|
+
{
|
|
3569
|
+
"type": "extension_ui_request",
|
|
3570
|
+
"id": "uuid-3",
|
|
3571
|
+
"method": "select",
|
|
3572
|
+
"title": "Permission required: read\n\nPath: /project/a.txt\n\nPatterns:\n - /project/a.txt",
|
|
3573
|
+
"options": ["Allow once", "Allow always", "Deny", "Deny with feedback"],
|
|
3574
|
+
"toolCallId": "call-a"
|
|
3575
|
+
}
|
|
3576
|
+
```
|
|
3577
|
+
|
|
3565
3578
|
#### confirm
|
|
3566
3579
|
|
|
3567
3580
|
Prompt the user for yes/no confirmation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.9",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@earendil-works/chord": "0.99.1",
|
|
60
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.
|
|
61
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.
|
|
62
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.
|
|
60
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.9",
|
|
61
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.9",
|
|
62
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.9",
|
|
63
63
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
64
64
|
"chalk": "6.0.0",
|
|
65
65
|
"cross-spawn": "7.0.6",
|
|
@@ -76,10 +76,10 @@
|
|
|
76
76
|
"typebox": "1.3.34",
|
|
77
77
|
"undici": "8.10.2",
|
|
78
78
|
"yaml": "2.9.1",
|
|
79
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
79
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.289",
|
|
80
80
|
"@anthropic-ai/sdk": "0.127.0",
|
|
81
|
-
"@code-yeongyu/senpi-codemode": "2026.10.
|
|
82
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.
|
|
81
|
+
"@code-yeongyu/senpi-codemode": "2026.10.9",
|
|
82
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.9",
|
|
83
83
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
84
84
|
"@mozilla/readability": "0.6.0",
|
|
85
85
|
"linkedom": "0.18.13",
|