@code-yeongyu/senpi 2026.10.21-1 → 2026.10.21-2
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 +33 -0
- package/dist/bundle/chunks/{anthropic-messages-S5BTP7U2.js → anthropic-messages-2IQD75YN.js} +1 -1
- package/dist/bundle/chunks/args-SV2KM4KA.js +2 -0
- package/dist/bundle/chunks/azure-openai-responses-HFI6SDH5.js +2 -0
- package/dist/bundle/chunks/{chunk-WQLMQGOW.js → chunk-2AJB5S4C.js} +1 -1
- package/dist/bundle/chunks/{chunk-XSRM367N.js → chunk-3AK3WUN4.js} +2 -2
- package/dist/bundle/chunks/{chunk-6TL72TUD.js → chunk-4HJTPWZY.js} +1 -1
- package/dist/bundle/chunks/chunk-4NPBDGC4.js +26 -0
- package/dist/bundle/chunks/{chunk-E4WOCULF.js → chunk-4XR5HDWC.js} +72 -69
- package/dist/bundle/chunks/{chunk-6IWSMXBA.js → chunk-5GC4IQ3A.js} +1 -1
- package/dist/bundle/chunks/{chunk-7I7ZMSBF.js → chunk-A4B4CMOR.js} +1 -1
- package/dist/bundle/chunks/chunk-B4N4N7BR.js +8 -0
- package/dist/bundle/chunks/{chunk-X23XXKMH.js → chunk-BVFJAXWO.js} +1 -1
- package/dist/bundle/chunks/{chunk-25B6JQC4.js → chunk-C3BJRXEV.js} +1 -1
- package/dist/bundle/chunks/{chunk-DKBJM22C.js → chunk-CQDU6S6C.js} +2 -2
- package/dist/bundle/chunks/{chunk-MF57DS3V.js → chunk-EV4TWAJI.js} +1 -1
- package/dist/bundle/chunks/{chunk-JJXDWX46.js → chunk-F3JGTOLH.js} +1 -1
- package/dist/bundle/chunks/{chunk-CRY5CP3B.js → chunk-G42JHX5Q.js} +3 -3
- package/dist/bundle/chunks/{chunk-5VRI3CME.js → chunk-G7VYQRE7.js} +2 -2
- package/dist/bundle/chunks/chunk-GVSBCJFI.js +2 -0
- package/dist/bundle/chunks/{chunk-KUZPBZUK.js → chunk-HKDGQE6D.js} +2 -2
- package/dist/bundle/chunks/{chunk-HD67U37N.js → chunk-HRV7NEOJ.js} +1 -1
- package/dist/bundle/chunks/chunk-JDETIKL7.js +7 -0
- package/dist/bundle/chunks/{chunk-MVNVDGYF.js → chunk-JGMG227A.js} +1 -1
- package/dist/bundle/chunks/chunk-MHD7SSHF.js +2 -0
- package/dist/bundle/chunks/{chunk-CEQMTVYS.js → chunk-MXLEWTWC.js} +2 -2
- package/dist/bundle/chunks/{chunk-C5W3D56I.js → chunk-N6UN2SDS.js} +3 -3
- package/dist/bundle/chunks/chunk-NTK2MHJ5.js +2 -0
- package/dist/bundle/chunks/chunk-OMN4QWFA.js +3 -0
- package/dist/bundle/chunks/chunk-PLQ5WRXN.js +2 -0
- package/dist/bundle/chunks/{chunk-2MOGCBPT.js → chunk-PUMCQ5HS.js} +1 -1
- package/dist/bundle/chunks/{chunk-55YTTFNY.js → chunk-PUQFKAB4.js} +1 -1
- package/dist/bundle/chunks/{chunk-IJT3GSB2.js → chunk-SN7PTZTC.js} +1 -1
- package/dist/bundle/chunks/{chunk-FWHSGCWR.js → chunk-T2IIUI3J.js} +3 -3
- package/dist/bundle/chunks/{chunk-5AOKDC6G.js → chunk-TSETMVJP.js} +1 -1
- package/dist/bundle/chunks/{chunk-SR7DBNWN.js → chunk-V6AYNGMX.js} +1 -1
- package/dist/bundle/chunks/chunk-VDPJ7W52.js +11 -0
- package/dist/bundle/chunks/{chunk-EPQK674G.js → chunk-VLKULV6F.js} +1 -1
- package/dist/bundle/chunks/{chunk-SL7TSFR5.js → chunk-VXACHCLB.js} +1 -1
- package/dist/bundle/chunks/{chunk-FM7NDZR5.js → chunk-VYSRUDEU.js} +1 -1
- package/dist/bundle/chunks/chunk-YMFGBSO3.js +2 -0
- package/dist/bundle/chunks/cli-main-RA6EBOOU.js +3 -0
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-4QEQ7MGK.js → cloudflare-workers-ai-system-one-BVENEDT6.js} +1 -1
- package/dist/bundle/chunks/cursor-agent.js +3 -3
- package/dist/bundle/chunks/google-generative-ai-ONS2KSGN.js +2 -0
- package/dist/bundle/chunks/google-vertex-5VS7ZESH.js +2 -0
- package/dist/bundle/chunks/help-fast-path-FZY53UXB.js +2 -0
- package/dist/bundle/chunks/host-command-TNUIHEFP.js +2 -0
- package/dist/bundle/chunks/{host-lifecycle-EZQ5VCQV.js → host-lifecycle-LDOHAYLK.js} +1 -1
- package/dist/bundle/chunks/host-session-command-OKASUQPA.js +20 -0
- package/dist/bundle/chunks/interactive-mode-PSGTSURY.js +2 -0
- package/dist/bundle/chunks/{mistral-conversations-5EHLE7PZ.js → mistral-conversations-RLDMF2BI.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-OMJZ5VXM.js → multi-session-host-J5BVVUG3.js} +7 -6
- package/dist/bundle/chunks/openai-codex-responses-VRIJIZJG.js +10 -0
- package/dist/bundle/chunks/{openai-completions-NHDMDAS7.js → openai-completions-EZBOIXHP.js} +2 -2
- package/dist/bundle/chunks/{openai-images-PNXQDBSP.js → openai-images-KKM343F2.js} +1 -1
- package/dist/bundle/chunks/openai-responses-CMBH3DCY.js +3 -0
- package/dist/bundle/chunks/{package-manager-cli-7GQDB5DX.js → package-manager-cli-JHVRG35I.js} +1 -1
- package/dist/bundle/chunks/{role-WQWYL4ZD.js → role-LNF5MFGC.js} +1 -1
- package/dist/bundle/chunks/rotation-stream-JK3Q4IWI.js +2 -0
- package/dist/bundle/chunks/rpc-mode-HVSPPRTT.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-FI7H7LGY.js → rpc-monitor-source-2DQZLFFJ.js} +1 -1
- package/dist/bundle/chunks/{runtime-session-IQAKNRTV.js → runtime-session-CLNODAYE.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-XXJC4QQN.js → schedule-command-OMI4VWUV.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-UPYF4OFP.js +3 -0
- package/dist/bundle/chunks/session-picker-NR7CRVNQ.js +2 -0
- package/dist/bundle/chunks/session-worker.js +143 -139
- package/dist/bundle/chunks/{typesafe-system-one-W2ZTYF7X.js → typesafe-system-one-PJIWA5SP.js} +1 -1
- 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/host-command.d.ts +2 -0
- package/dist/cli/host-command.js +14 -19
- package/dist/cli/host-handover-args.d.ts +4 -0
- package/dist/cli/host-handover-args.js +20 -0
- package/dist/core/agent-session.d.ts +29 -1
- package/dist/core/agent-session.js +74 -19
- package/dist/core/auth-storage.d.ts +44 -1
- package/dist/core/auth-storage.js +120 -3
- package/dist/core/client-message-identity.d.ts +3 -0
- package/dist/core/credential-accounts.d.ts +2 -0
- package/dist/core/credential-accounts.js +8 -1
- package/dist/core/credential-pool/rotation-slots.js +3 -2
- package/dist/core/extensions/builtin/account/index.js +6 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +2 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js +6 -5
- package/dist/core/extensions/builtin/goal/provider-retry.d.ts +2 -2
- package/dist/core/extensions/builtin/goal/provider-retry.js +18 -6
- package/dist/core/extensions/builtin/terminal/command-monitor-source.d.ts +17 -4
- package/dist/core/extensions/builtin/terminal/command-monitor-source.js +33 -6
- package/dist/core/extensions/builtin/terminal/durable-command.js +1 -0
- package/dist/core/extensions/builtin/terminal/extension.js +28 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify-details.d.ts +6 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify-details.js +19 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify-format.d.ts +18 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify-format.js +52 -0
- package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts +8 -1
- package/dist/core/extensions/builtin/terminal/monitor-notify.js +65 -119
- package/dist/core/extensions/builtin/terminal/monitor-overflow-file.d.ts +7 -0
- package/dist/core/extensions/builtin/terminal/monitor-overflow-file.js +67 -0
- package/dist/core/extensions/builtin/terminal/monitor-overflow.d.ts +37 -0
- package/dist/core/extensions/builtin/terminal/monitor-overflow.js +75 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +10 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +24 -1
- package/dist/core/extensions/builtin/terminal/monitor-types.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/monitor-wake-fit.d.ts +9 -0
- package/dist/core/extensions/builtin/terminal/monitor-wake-fit.js +34 -0
- package/dist/core/extensions/builtin/terminal/monitor-wake-pacing.d.ts +10 -0
- package/dist/core/extensions/builtin/terminal/monitor-wake-pacing.js +41 -0
- package/dist/core/extensions/builtin/terminal/restore-session.js +2 -0
- package/dist/core/extensions/builtin/terminal/rpc-monitor-observer.js +5 -1
- package/dist/core/extensions/builtin/terminal/settings.d.ts +10 -0
- package/dist/core/extensions/builtin/terminal/settings.js +13 -1
- package/dist/core/extensions/builtin/terminal/shared.d.ts +17 -10
- package/dist/core/extensions/builtin/terminal/shared.js +19 -10
- package/dist/core/extensions/builtin/terminal/terminal-manifest-model.d.ts +3 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest-parse.js +7 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-schema.d.ts +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor-schema.js +6 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +1 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +9 -1
- package/dist/core/extensions/builtin/tps.js +1 -8
- package/dist/core/extensions/loader.js +3 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.js +12 -1
- package/dist/core/extensions/types.d.ts +3 -1
- package/dist/core/session-manager.d.ts +10 -1
- package/dist/core/session-manager.js +4 -3
- package/dist/core/terminal-settings.d.ts +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/main.d.ts +14 -0
- package/dist/main.js +25 -8
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.js +1 -0
- package/dist/modes/interactive/components/footer.js +4 -2
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.js +13 -1
- package/dist/modes/rpc/client-admission-record.d.ts +5 -0
- package/dist/modes/rpc/client-admission-record.js +2 -0
- package/dist/modes/rpc/connection-handler.d.ts +3 -0
- package/dist/modes/rpc/connection-handler.js +33 -5
- package/dist/modes/rpc/connection-roles.d.ts +30 -0
- package/dist/modes/rpc/connection-roles.js +53 -0
- package/dist/modes/rpc/custom-capability.d.ts +6 -0
- package/dist/modes/rpc/custom-capability.js +6 -0
- package/dist/modes/rpc/host-capabilities.js +2 -1
- package/dist/modes/rpc/host-decision.d.ts +4 -1
- package/dist/modes/rpc/host-decision.js +6 -3
- package/dist/modes/rpc/host-ensure-liveness.d.ts +11 -0
- package/dist/modes/rpc/host-ensure-liveness.js +26 -0
- package/dist/modes/rpc/host-ensure.js +2 -3
- package/dist/modes/rpc/host-holder.d.ts +27 -0
- package/dist/modes/rpc/host-holder.js +18 -0
- package/dist/modes/rpc/host-runner.d.ts +2 -0
- package/dist/modes/rpc/host-runner.js +8 -1
- package/dist/modes/rpc/host-status-all.js +1 -0
- package/dist/modes/rpc/host-status.d.ts +1 -0
- package/dist/modes/rpc/host-status.js +5 -0
- package/dist/modes/rpc/host-stop-generation.d.ts +22 -0
- package/dist/modes/rpc/host-stop-generation.js +36 -0
- package/dist/modes/rpc/host-stop-start-time.d.ts +15 -0
- package/dist/modes/rpc/host-stop-start-time.js +30 -0
- package/dist/modes/rpc/host-stop.d.ts +13 -1
- package/dist/modes/rpc/host-stop.js +78 -9
- package/dist/modes/rpc/rpc-client.d.ts +9 -0
- package/dist/modes/rpc/rpc-client.js +15 -0
- package/dist/modes/rpc/rpc-types.d.ts +18 -0
- package/dist/modes/rpc/rpc-types.js +2 -0
- package/dist/modes/rpc/session-binding.d.ts +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +1 -0
- package/dist/modes/rpc/session-command-router.js +13 -0
- package/dist/modes/rpc/session-worker-client.d.ts +1 -1
- package/dist/modes/rpc/session-worker-client.js +12 -1
- package/dist/modes/rpc/session-worker-protocol.d.ts +3 -0
- package/dist/modes/rpc/session-worker.js +9 -0
- package/docs/rpc.md +54 -2
- package/docs/session-format.md +5 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/args-CRXNC7IS.js +0 -2
- package/dist/bundle/chunks/azure-openai-responses-2DCY6QKO.js +0 -2
- package/dist/bundle/chunks/chunk-5ZBDN3LB.js +0 -26
- package/dist/bundle/chunks/chunk-AEJUOQ7U.js +0 -2
- package/dist/bundle/chunks/chunk-LAWS3FYQ.js +0 -11
- package/dist/bundle/chunks/chunk-LM76EMEJ.js +0 -7
- package/dist/bundle/chunks/chunk-M237EDHB.js +0 -2
- package/dist/bundle/chunks/chunk-V6RY5A6T.js +0 -2
- package/dist/bundle/chunks/chunk-WIAXG52U.js +0 -2
- package/dist/bundle/chunks/chunk-WTFD4TII.js +0 -8
- package/dist/bundle/chunks/chunk-Z33PKFXM.js +0 -3
- package/dist/bundle/chunks/cli-main-RKC5Z25H.js +0 -3
- package/dist/bundle/chunks/google-generative-ai-3SKUSCIL.js +0 -2
- package/dist/bundle/chunks/google-vertex-YHSIIE4G.js +0 -2
- package/dist/bundle/chunks/help-fast-path-FWS5ARO5.js +0 -2
- package/dist/bundle/chunks/host-command-L66Y7ZL5.js +0 -2
- package/dist/bundle/chunks/host-session-command-2YNQNTEH.js +0 -20
- package/dist/bundle/chunks/interactive-mode-XOKTNBLA.js +0 -2
- package/dist/bundle/chunks/openai-codex-responses-4BIPACKV.js +0 -10
- package/dist/bundle/chunks/openai-responses-2UOPEJLP.js +0 -3
- package/dist/bundle/chunks/rotation-stream-J2NUPRMG.js +0 -2
- package/dist/bundle/chunks/rpc-mode-WM5WH7TH.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-MTOAYRRY.js +0 -3
- package/dist/bundle/chunks/session-picker-LU7BPLNI.js +0 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-monitor wake pacing: a token bucket of `rate` tokens refilled at `rate` an hour, one token
|
|
3
|
+
* per wake. Monitors without a rate are unpaced and always ready.
|
|
4
|
+
*/
|
|
5
|
+
const HOUR_MS = 3_600_000;
|
|
6
|
+
export class WakePacer {
|
|
7
|
+
#rateOf;
|
|
8
|
+
#buckets = new Map();
|
|
9
|
+
constructor(rateOf) {
|
|
10
|
+
this.#rateOf = rateOf;
|
|
11
|
+
}
|
|
12
|
+
/** When `id` next holds a whole token: `now` for an unpaced monitor or one with a token left. */
|
|
13
|
+
readyAt(id, now) {
|
|
14
|
+
const paced = this.#refill(id, now);
|
|
15
|
+
if (paced === undefined || paced.bucket.tokens >= 1)
|
|
16
|
+
return now;
|
|
17
|
+
return now + Math.ceil(((1 - paced.bucket.tokens) * HOUR_MS) / paced.rate);
|
|
18
|
+
}
|
|
19
|
+
spend(id, now) {
|
|
20
|
+
const paced = this.#refill(id, now);
|
|
21
|
+
if (paced !== undefined)
|
|
22
|
+
paced.bucket.tokens = Math.max(0, paced.bucket.tokens - 1);
|
|
23
|
+
}
|
|
24
|
+
forget(id) {
|
|
25
|
+
this.#buckets.delete(id);
|
|
26
|
+
}
|
|
27
|
+
clear() {
|
|
28
|
+
this.#buckets.clear();
|
|
29
|
+
}
|
|
30
|
+
#refill(id, now) {
|
|
31
|
+
const rate = this.#rateOf(id);
|
|
32
|
+
if (rate === undefined || !Number.isFinite(rate) || rate < 1)
|
|
33
|
+
return undefined;
|
|
34
|
+
const bucket = this.#buckets.get(id) ?? { tokens: rate, at: now };
|
|
35
|
+
bucket.tokens = Math.min(rate, bucket.tokens + ((now - bucket.at) * rate) / HOUR_MS);
|
|
36
|
+
bucket.at = now;
|
|
37
|
+
this.#buckets.set(id, bucket);
|
|
38
|
+
return { bucket, rate };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=monitor-wake-pacing.js.map
|
|
@@ -10,6 +10,7 @@ import { existsSync } from "node:fs";
|
|
|
10
10
|
import { encodedSessionId } from "../../../session-sidecar-store.js";
|
|
11
11
|
import { createLeaseKeeper } from "./lease-keeper.js";
|
|
12
12
|
import { acquireTerminalLease, releaseTerminalLease, retireLeaseToken, } from "./manifest-lease.js";
|
|
13
|
+
import { sweepMonitorOverflowFiles } from "./monitor-overflow-file.js";
|
|
13
14
|
import { removeMonitorStateDir, terminalStateDir } from "./monitor-state-dir.js";
|
|
14
15
|
import { ownProcessStartedAtMs, processBootAtMs } from "./process-identity.js";
|
|
15
16
|
import { restoreTerminalState } from "./restore.js";
|
|
@@ -114,6 +115,7 @@ export async function startPersistence({ pi, state, toolCtx, sessionKey }) {
|
|
|
114
115
|
const self = { pid: process.pid, bootAtMs: processBootAtMs(), processStartedAtMs: ownProcessStartedAtMs() };
|
|
115
116
|
// Housekeeping only: a failed sweep leaves the files for the next start to reclaim.
|
|
116
117
|
void sweepTerminalStateDir(dir, { self, keep: new Set([`${encoded}.lease`, `${encoded}.json`]) }).catch(() => undefined);
|
|
118
|
+
void sweepMonitorOverflowFiles(dir).catch(() => undefined);
|
|
117
119
|
flushRestoreDigest(pi, state);
|
|
118
120
|
decided();
|
|
119
121
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* reads the stream itself.
|
|
7
7
|
*/
|
|
8
8
|
import { createConnection } from "node:net";
|
|
9
|
+
import { OBSERVE_REQUEST_FIELD } from "../../../../modes/rpc/host-observe-request.js";
|
|
9
10
|
import { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from "../../../../modes/rpc/jsonl.js";
|
|
10
11
|
import { readSocketSecret, resolveSocketTransportAddress, sendSocketHandshake, socketSecretPath, } from "../../../../modes/rpc/socket-transport.js";
|
|
11
12
|
import { socketNeedsHandshake } from "../../../../modes/rpc/tui-socket.js";
|
|
@@ -25,7 +26,10 @@ export async function openRpcObserver(socketPath, subscription, handlers) {
|
|
|
25
26
|
socket.once("connect", () => {
|
|
26
27
|
if (secret !== undefined)
|
|
27
28
|
sendSocketHandshake(socket, secret);
|
|
28
|
-
|
|
29
|
+
// Marked observing (senpi#3956): without it the supervisor counts this watch as an attached client, so a
|
|
30
|
+
// watched host never idle-exits and a `children`/`all` watch pins every task shard host on the machine.
|
|
31
|
+
const frame = { ...subscription, [OBSERVE_REQUEST_FIELD]: true, id: SUBSCRIBE_ID, type: "subscribe_sessions" };
|
|
32
|
+
socket.write(`${JSON.stringify(frame)}\n`);
|
|
29
33
|
});
|
|
30
34
|
const detach = attachJsonlLineReader(socket, (line) => {
|
|
31
35
|
let record;
|
|
@@ -7,6 +7,13 @@ export interface MonitorDeliverySettings {
|
|
|
7
7
|
readonly maxLinesPerInjection: number;
|
|
8
8
|
readonly maxCharsPerInjection: number;
|
|
9
9
|
readonly wakeBudget: number;
|
|
10
|
+
/** Average wakes an hour (and burst size) of a persistent command watch; more lines batch. */
|
|
11
|
+
readonly wakesPerHour: number;
|
|
12
|
+
}
|
|
13
|
+
/** Runaway guard of a persistent command watch: pause after this many lines a minute for this many minutes. */
|
|
14
|
+
export interface MonitorRunawayGuard {
|
|
15
|
+
readonly linesPerMinute: number;
|
|
16
|
+
readonly minutes: number;
|
|
10
17
|
}
|
|
11
18
|
export interface ResolvedTerminalSettings {
|
|
12
19
|
readonly defaultCols: number;
|
|
@@ -16,10 +23,13 @@ export interface ResolvedTerminalSettings {
|
|
|
16
23
|
readonly timeoutAction: TimeoutAction;
|
|
17
24
|
readonly notify: NotifyMode;
|
|
18
25
|
readonly monitor: MonitorDeliverySettings;
|
|
26
|
+
readonly monitorRunaway: MonitorRunawayGuard;
|
|
19
27
|
/** Admission cap on persistent (restart-surviving) monitors per session. */
|
|
20
28
|
readonly maxDurableMonitors: number | "unlimited";
|
|
21
29
|
}
|
|
22
30
|
export declare const TERMINAL_SETTINGS_DEFAULTS: ResolvedTerminalSettings;
|
|
31
|
+
/** Upper bound of a wake rate (setting or per-monitor override): one wake a second on average. */
|
|
32
|
+
export declare const MAX_MONITOR_WAKES_PER_HOUR = 3600;
|
|
23
33
|
/** Resolve terminal-tool config from a raw `terminal` settings block, filling defaults. */
|
|
24
34
|
export declare function resolveTerminalSettings(raw: TerminalSettings | undefined): ResolvedTerminalSettings;
|
|
25
35
|
/** Load and merge terminal-tool settings from global + project settings.json. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_COLS, DEFAULT_MAX_SESSIONS, DEFAULT_ROWS, DEFAULT_SCROLLBACK } from "./shared.js";
|
|
1
|
+
import { DEFAULT_COLS, DEFAULT_MAX_SESSIONS, DEFAULT_MONITOR_RUNAWAY_LINES_PER_MINUTE, DEFAULT_MONITOR_RUNAWAY_MINUTES, DEFAULT_MONITOR_WAKES_PER_HOUR, DEFAULT_ROWS, DEFAULT_SCROLLBACK, } from "./shared.js";
|
|
2
2
|
export const TERMINAL_SETTINGS_DEFAULTS = {
|
|
3
3
|
defaultCols: DEFAULT_COLS,
|
|
4
4
|
defaultRows: DEFAULT_ROWS,
|
|
@@ -12,9 +12,16 @@ export const TERMINAL_SETTINGS_DEFAULTS = {
|
|
|
12
12
|
maxLinesPerInjection: 50,
|
|
13
13
|
maxCharsPerInjection: 4096,
|
|
14
14
|
wakeBudget: 5,
|
|
15
|
+
wakesPerHour: DEFAULT_MONITOR_WAKES_PER_HOUR,
|
|
16
|
+
},
|
|
17
|
+
monitorRunaway: {
|
|
18
|
+
linesPerMinute: DEFAULT_MONITOR_RUNAWAY_LINES_PER_MINUTE,
|
|
19
|
+
minutes: DEFAULT_MONITOR_RUNAWAY_MINUTES,
|
|
15
20
|
},
|
|
16
21
|
maxDurableMonitors: "unlimited",
|
|
17
22
|
};
|
|
23
|
+
/** Upper bound of a wake rate (setting or per-monitor override): one wake a second on average. */
|
|
24
|
+
export const MAX_MONITOR_WAKES_PER_HOUR = 3600;
|
|
18
25
|
function positiveInt(value, fallback, maximum = Number.MAX_SAFE_INTEGER) {
|
|
19
26
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 1)
|
|
20
27
|
return fallback;
|
|
@@ -51,6 +58,11 @@ export function resolveTerminalSettings(raw) {
|
|
|
51
58
|
maxLinesPerInjection: positiveInt(raw.monitorMaxLinesPerInjection, TERMINAL_SETTINGS_DEFAULTS.monitor.maxLinesPerInjection, 200),
|
|
52
59
|
maxCharsPerInjection: positiveInt(raw.monitorMaxCharsPerInjection, TERMINAL_SETTINGS_DEFAULTS.monitor.maxCharsPerInjection, 16_384),
|
|
53
60
|
wakeBudget: positiveInt(raw.monitorWakeBudget, TERMINAL_SETTINGS_DEFAULTS.monitor.wakeBudget, 100),
|
|
61
|
+
wakesPerHour: positiveInt(raw.monitorWakesPerHour, TERMINAL_SETTINGS_DEFAULTS.monitor.wakesPerHour, MAX_MONITOR_WAKES_PER_HOUR),
|
|
62
|
+
},
|
|
63
|
+
monitorRunaway: {
|
|
64
|
+
linesPerMinute: positiveInt(raw.monitorRunawayLinesPerMinute, TERMINAL_SETTINGS_DEFAULTS.monitorRunaway.linesPerMinute),
|
|
65
|
+
minutes: positiveInt(raw.monitorRunawayMinutes, TERMINAL_SETTINGS_DEFAULTS.monitorRunaway.minutes, 1440),
|
|
54
66
|
},
|
|
55
67
|
maxDurableMonitors: durableMonitorCap(raw.maxDurableMonitors),
|
|
56
68
|
};
|
|
@@ -44,20 +44,27 @@ export declare const KILLED_SESSION_EXIT_GRACE_MS = 5000;
|
|
|
44
44
|
*/
|
|
45
45
|
export declare const DURABLE_MONITOR_EXPIRY_MS: number;
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* it. The session-global wake budget only counts consecutive line-only wakes on an idle
|
|
50
|
-
* session, so a busy session needs this per-monitor backstop against a runaway watch.
|
|
47
|
+
* Width of a durable command watch's rolling fire window: the persisted count of matched lines it
|
|
48
|
+
* delivered inside the current window. It is a statistic only; nothing mutes on it.
|
|
51
49
|
*/
|
|
52
|
-
export declare const DEFAULT_DURABLE_MONITOR_FIRE_BUDGET = 200;
|
|
53
|
-
/** Width of the rolling fire-budget window: the budget replenishes after this span elapses. */
|
|
54
50
|
export declare const FIRE_BUDGET_WINDOW_MS: number;
|
|
55
51
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* clear the session-global wake streak.
|
|
52
|
+
* Wake pacing of a persistent command watch: at most this many wakes an hour on average, with a
|
|
53
|
+
* burst of the same size. Lines matched between wakes are batched into the next one, never dropped.
|
|
59
54
|
*/
|
|
60
|
-
export declare const
|
|
55
|
+
export declare const DEFAULT_MONITOR_WAKES_PER_HOUR = 12;
|
|
56
|
+
/** Runaway guard: a persistent command watch matching more lines than this in each minute... */
|
|
57
|
+
export declare const DEFAULT_MONITOR_RUNAWAY_LINES_PER_MINUTE = 120;
|
|
58
|
+
/** ...for this many consecutive minutes is paused with a visible notice until it is re-armed. */
|
|
59
|
+
export declare const DEFAULT_MONITOR_RUNAWAY_MINUTES = 5;
|
|
60
|
+
/** One per-monitor overflow file grows to this size before it rotates to a single `.1` copy. */
|
|
61
|
+
export declare const MONITOR_OVERFLOW_FILE_MAX_BYTES: number;
|
|
62
|
+
/**
|
|
63
|
+
* Machine-consumed prefix of the runaway-guard pause summary: the monitor notifier treats a summary
|
|
64
|
+
* that starts with it as a pause, not a completion, so it must not clear the session wake streak.
|
|
65
|
+
*/
|
|
66
|
+
export declare const RUNAWAY_PAUSE_SUMMARY_PREFIX = "paused: runaway output";
|
|
67
|
+
export declare function runawayPauseSummary(bashId: string, linesPerMinute: number, minutes: number): string;
|
|
61
68
|
/**
|
|
62
69
|
* Non-interactive environment for foreground one-shot commands (codex-style):
|
|
63
70
|
* cooperative tools (`gh`, `git`, pagers, color libs) skip spinners/colors at
|
|
@@ -50,20 +50,29 @@ export const KILLED_SESSION_EXIT_GRACE_MS = 5000;
|
|
|
50
50
|
*/
|
|
51
51
|
export const DURABLE_MONITOR_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* it. The session-global wake budget only counts consecutive line-only wakes on an idle
|
|
56
|
-
* session, so a busy session needs this per-monitor backstop against a runaway watch.
|
|
53
|
+
* Width of a durable command watch's rolling fire window: the persisted count of matched lines it
|
|
54
|
+
* delivered inside the current window. It is a statistic only; nothing mutes on it.
|
|
57
55
|
*/
|
|
58
|
-
export const DEFAULT_DURABLE_MONITOR_FIRE_BUDGET = 200;
|
|
59
|
-
/** Width of the rolling fire-budget window: the budget replenishes after this span elapses. */
|
|
60
56
|
export const FIRE_BUDGET_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
61
57
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* clear the session-global wake streak.
|
|
58
|
+
* Wake pacing of a persistent command watch: at most this many wakes an hour on average, with a
|
|
59
|
+
* burst of the same size. Lines matched between wakes are batched into the next one, never dropped.
|
|
65
60
|
*/
|
|
66
|
-
export const
|
|
61
|
+
export const DEFAULT_MONITOR_WAKES_PER_HOUR = 12;
|
|
62
|
+
/** Runaway guard: a persistent command watch matching more lines than this in each minute... */
|
|
63
|
+
export const DEFAULT_MONITOR_RUNAWAY_LINES_PER_MINUTE = 120;
|
|
64
|
+
/** ...for this many consecutive minutes is paused with a visible notice until it is re-armed. */
|
|
65
|
+
export const DEFAULT_MONITOR_RUNAWAY_MINUTES = 5;
|
|
66
|
+
/** One per-monitor overflow file grows to this size before it rotates to a single `.1` copy. */
|
|
67
|
+
export const MONITOR_OVERFLOW_FILE_MAX_BYTES = 4 * 1024 * 1024;
|
|
68
|
+
/**
|
|
69
|
+
* Machine-consumed prefix of the runaway-guard pause summary: the monitor notifier treats a summary
|
|
70
|
+
* that starts with it as a pause, not a completion, so it must not clear the session wake streak.
|
|
71
|
+
*/
|
|
72
|
+
export const RUNAWAY_PAUSE_SUMMARY_PREFIX = "paused: runaway output";
|
|
73
|
+
export function runawayPauseSummary(bashId, linesPerMinute, minutes) {
|
|
74
|
+
return `${RUNAWAY_PAUSE_SUMMARY_PREFIX} (more than ${linesPerMinute} matched lines a minute for ${minutes} minute${minutes === 1 ? "" : "s"}); later lines stay in bash_output. Resume with monitor({ action: "rearm", bash_id: "${bashId}" }).`;
|
|
75
|
+
}
|
|
67
76
|
/**
|
|
68
77
|
* Non-interactive environment for foreground one-shot commands (codex-style):
|
|
69
78
|
* cooperative tools (`gh`, `git`, pagers, color libs) skip spinners/colors at
|
|
@@ -49,6 +49,8 @@ export interface ManifestMonitor {
|
|
|
49
49
|
readonly filter?: string;
|
|
50
50
|
/** Command watch whose output lines are a person's words; absent on older entries. */
|
|
51
51
|
readonly humanInput?: boolean;
|
|
52
|
+
/** Per-monitor wake-rate override of a persistent command watch; absent uses the setting. */
|
|
53
|
+
readonly wakesPerHour?: number;
|
|
52
54
|
readonly cwd?: string;
|
|
53
55
|
readonly approvedParent?: string;
|
|
54
56
|
/** Rpc watches: the target as written, the record kinds reported and dropped. */
|
|
@@ -91,6 +93,7 @@ export interface CommandMonitorSpec {
|
|
|
91
93
|
readonly command: string;
|
|
92
94
|
readonly filter?: string;
|
|
93
95
|
readonly humanInput?: boolean;
|
|
96
|
+
readonly wakesPerHour?: number;
|
|
94
97
|
readonly cwd?: string;
|
|
95
98
|
readonly persistent: boolean;
|
|
96
99
|
}
|
|
@@ -44,6 +44,12 @@ function bool(raw, field) {
|
|
|
44
44
|
function opt(raw, field, required) {
|
|
45
45
|
return raw[field] === undefined ? undefined : required(raw, field);
|
|
46
46
|
}
|
|
47
|
+
function wakeRate(raw, field) {
|
|
48
|
+
const value = raw[field];
|
|
49
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 3600)
|
|
50
|
+
invalid(`field ${field} must be an integer from 1 to 3600`);
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
47
53
|
function oneOf(values, raw, field) {
|
|
48
54
|
const value = raw[field];
|
|
49
55
|
if (typeof value !== "string" || !values.includes(value))
|
|
@@ -127,6 +133,7 @@ function parseMonitor(entry) {
|
|
|
127
133
|
event: opt(entry, "event", (raw, field) => oneOf(FILE_EVENTS, raw, field)),
|
|
128
134
|
filter: opt(entry, "filter", str),
|
|
129
135
|
humanInput: opt(entry, "humanInput", bool),
|
|
136
|
+
...(entry.wakesPerHour === undefined ? {} : { wakesPerHour: wakeRate(entry, "wakesPerHour") }),
|
|
130
137
|
cwd: opt(entry, "cwd", str),
|
|
131
138
|
approvedParent: opt(entry, "approvedParent", str),
|
|
132
139
|
target: opt(entry, "target", str),
|
|
@@ -227,6 +227,7 @@ export class TerminalManifestWriter {
|
|
|
227
227
|
event: spec.kind === "file" ? spec.event : undefined,
|
|
228
228
|
filter: spec.kind === "file" ? undefined : spec.filter,
|
|
229
229
|
humanInput: spec.kind === "command" && spec.humanInput === true ? true : undefined,
|
|
230
|
+
...(spec.kind === "command" && spec.wakesPerHour !== undefined ? { wakesPerHour: spec.wakesPerHour } : {}),
|
|
230
231
|
cwd: spec.kind === "rpc" ? undefined : spec.cwd,
|
|
231
232
|
...(spec.kind === "rpc"
|
|
232
233
|
? {
|
|
@@ -21,6 +21,7 @@ export declare const monitorSchema: Type.TObject<{
|
|
|
21
21
|
timeout_ms: Type.TOptional<Type.TNumber>;
|
|
22
22
|
persistent: Type.TOptional<Type.TBoolean>;
|
|
23
23
|
bash_id: Type.TOptional<Type.TString>;
|
|
24
|
+
wakes_per_hour: Type.TOptional<Type.TInteger>;
|
|
24
25
|
human_input: Type.TOptional<Type.TBoolean>;
|
|
25
26
|
}>;
|
|
26
27
|
export type MonitorInput = Static<typeof monitorSchema>;
|
|
@@ -10,7 +10,7 @@ export const MAX_MONITOR_TIMEOUT_MS = 3_600_000;
|
|
|
10
10
|
*/
|
|
11
11
|
export const monitorSchema = Type.Object({
|
|
12
12
|
action: Type.Optional(StringEnum(["create", "rearm"], {
|
|
13
|
-
description: "Defaults to create. rearm resumes a monitor paused by the wake budget.",
|
|
13
|
+
description: "Defaults to create. rearm resumes a monitor paused by the wake budget or the runaway guard.",
|
|
14
14
|
})),
|
|
15
15
|
type: Type.Optional(StringEnum(["command", "path", "rpc"], {
|
|
16
16
|
description: "Create: which source to watch; inferred when omitted (command -> command, path -> path, target -> rpc).",
|
|
@@ -50,6 +50,11 @@ export const monitorSchema = Type.Object({
|
|
|
50
50
|
description: "Standing watch: no deadline, and it survives a session restart (command re-run once, file rescanned and any detached change reported). Expires 7 days after creation; no per-session cap unless the terminal.maxDurableMonitors setting sets one; stop one with kill_bash.",
|
|
51
51
|
})),
|
|
52
52
|
bash_id: Type.Optional(Type.String({ description: "Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused." })),
|
|
53
|
+
wakes_per_hour: Type.Optional(Type.Integer({
|
|
54
|
+
minimum: 1,
|
|
55
|
+
maximum: 3600,
|
|
56
|
+
description: "Persistent command branch only: average wakes an hour for this watch (default: terminal.monitorWakesPerHour, 12). Lines matched between wakes are batched into the next one, never dropped; raise it for a chat bridge that needs quicker replies.",
|
|
57
|
+
})),
|
|
53
58
|
human_input: Type.Optional(Type.Boolean({
|
|
54
59
|
description: "Command branch only: set true when each output line is a message a person wrote (a chat or inbox bridge). Its deliveries then count as new user input, so a running goal keeps going; leave unset for machine status.",
|
|
55
60
|
})),
|
|
@@ -24,6 +24,7 @@ export declare function createMonitorTool(ctx: TerminalToolContext): {
|
|
|
24
24
|
timeout_ms: import("typebox").TOptional<import("typebox").TNumber>;
|
|
25
25
|
persistent: import("typebox").TOptional<import("typebox").TBoolean>;
|
|
26
26
|
bash_id: import("typebox").TOptional<import("typebox").TString>;
|
|
27
|
+
wakes_per_hour: import("typebox").TOptional<import("typebox").TInteger>;
|
|
27
28
|
human_input: import("typebox").TOptional<import("typebox").TBoolean>;
|
|
28
29
|
}>;
|
|
29
30
|
renderCall: typeof renderMonitorCall;
|
|
@@ -50,7 +50,9 @@ async function createMonitor(ctx, registry, input, execCtx) {
|
|
|
50
50
|
persistent: input.persistent === true,
|
|
51
51
|
deadlineMs,
|
|
52
52
|
...(input.human_input === true ? { humanInput: true } : {}),
|
|
53
|
-
|
|
53
|
+
...(input.wakes_per_hour !== undefined ? { wakesPerHour: input.wakes_per_hour } : {}),
|
|
54
|
+
// Only persistent command watches are restartable-command durable: those carry the fire window
|
|
55
|
+
// and the runaway guard, and only they are wake-paced.
|
|
54
56
|
durabilityClass: input.persistent === true ? "restartable-command" : "ephemeral",
|
|
55
57
|
// Same deadline the manifest writer persists, so the footer warns off the live record too.
|
|
56
58
|
...(input.persistent === true ? { expiresAt: Date.now() + DURABLE_MONITOR_EXPIRY_MS } : {}),
|
|
@@ -67,6 +69,7 @@ async function createMonitor(ctx, registry, input, execCtx) {
|
|
|
67
69
|
command: input.command,
|
|
68
70
|
filter: input.filter,
|
|
69
71
|
...(input.human_input === true ? { humanInput: true } : {}),
|
|
72
|
+
...(input.wakes_per_hour !== undefined ? { wakesPerHour: input.wakes_per_hour } : {}),
|
|
70
73
|
cwd,
|
|
71
74
|
persistent: input.persistent === true,
|
|
72
75
|
},
|
|
@@ -130,6 +133,11 @@ export function createMonitorTool(ctx) {
|
|
|
130
133
|
const source = monitorSourceOf(input);
|
|
131
134
|
if (typeof source !== "string")
|
|
132
135
|
return errorResult(source.error);
|
|
136
|
+
if (input.wakes_per_hour !== undefined && (source !== "command" || input.persistent !== true))
|
|
137
|
+
return errorResult("wakes_per_hour applies to persistent command monitors (command with persistent: true); other watches are not wake-paced.");
|
|
138
|
+
if (input.wakes_per_hour !== undefined &&
|
|
139
|
+
(!Number.isInteger(input.wakes_per_hour) || input.wakes_per_hour < 1 || input.wakes_per_hour > 3600))
|
|
140
|
+
return errorResult("wakes_per_hour must be an integer from 1 to 3600.");
|
|
133
141
|
if (source === "rpc") {
|
|
134
142
|
if (input.human_input === true)
|
|
135
143
|
return errorResult("human_input applies to command monitors; an rpc watch reports session status, not a person's text.");
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
+
import { reasonedBeforeFirstDelta, STREAMED_DELTA_TYPES } from "@earendil-works/pi-agent-core";
|
|
1
2
|
function isAssistantMessage(message) {
|
|
2
3
|
if (!message || typeof message !== "object")
|
|
3
4
|
return false;
|
|
4
5
|
const role = message.role;
|
|
5
6
|
return role === "assistant";
|
|
6
7
|
}
|
|
7
|
-
function reasonedBeforeFirstDelta(message, thinkingStreamed) {
|
|
8
|
-
if ((message.usage?.reasoning ?? 0) > 0)
|
|
9
|
-
return true;
|
|
10
|
-
if (thinkingStreamed)
|
|
11
|
-
return false;
|
|
12
|
-
return message.content?.some((block) => block.type === "thinking") ?? false;
|
|
13
|
-
}
|
|
14
|
-
const STREAMED_DELTA_TYPES = new Set(["text_delta", "thinking_delta", "toolcall_delta"]);
|
|
15
8
|
/** Below this much streamed generation time a rate is mostly timer noise, so none is shown (senpi#3393). */
|
|
16
9
|
const MIN_GENERATION_MS = 500;
|
|
17
10
|
export default function (pi) {
|
|
@@ -900,7 +900,9 @@ function discoverExtensionsInDir(dir) {
|
|
|
900
900
|
}
|
|
901
901
|
const discovered = [];
|
|
902
902
|
try {
|
|
903
|
-
const entries = fs
|
|
903
|
+
const entries = fs
|
|
904
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
905
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
904
906
|
for (const entry of entries) {
|
|
905
907
|
const entryPath = path.join(dir, entry.name);
|
|
906
908
|
// 1. Direct files: *.ts or *.js
|
|
@@ -249,6 +249,7 @@ export class ExtensionRunner {
|
|
|
249
249
|
this.isIdleFn = () => true;
|
|
250
250
|
this.engineTurnsHeldFn = () => false;
|
|
251
251
|
this.noteHeldWakeFn = () => { };
|
|
252
|
+
this.personAnswerPending = false;
|
|
252
253
|
this.isProjectTrustedFn = () => true;
|
|
253
254
|
this.getSignalFn = () => undefined;
|
|
254
255
|
this.waitForIdleFn = async () => { };
|
|
@@ -336,7 +337,12 @@ export class ExtensionRunner {
|
|
|
336
337
|
else
|
|
337
338
|
actions.sendMessage(message, options);
|
|
338
339
|
};
|
|
339
|
-
this.runtime.sendUserMessage = (content, options) =>
|
|
340
|
+
this.runtime.sendUserMessage = (content, options) => {
|
|
341
|
+
// Captured at admission: a deferred answer keeps the origin it was delivered with.
|
|
342
|
+
const inputOrigin = this.personAnswerPending ? "person" : "extension";
|
|
343
|
+
this.personAnswerPending = false;
|
|
344
|
+
this.sessionStartTurns.admit(() => actions.sendUserMessage(content, options, inputOrigin));
|
|
345
|
+
};
|
|
340
346
|
this.runtime.appendEntry = actions.appendEntry;
|
|
341
347
|
this.runtime.setSessionName = actions.setSessionName;
|
|
342
348
|
this.runtime.getSessionName = actions.getSessionName;
|
|
@@ -1248,6 +1254,11 @@ export class ExtensionRunner {
|
|
|
1248
1254
|
if (this.questionInput) {
|
|
1249
1255
|
bindQuestionInput(context, () => {
|
|
1250
1256
|
runner.assertActive();
|
|
1257
|
+
// The answer's own sendUserMessage follows synchronously and takes this token at admission.
|
|
1258
|
+
runner.personAnswerPending = true;
|
|
1259
|
+
queueMicrotask(() => {
|
|
1260
|
+
runner.personAnswerPending = false;
|
|
1261
|
+
});
|
|
1251
1262
|
runner.questionInput?.();
|
|
1252
1263
|
});
|
|
1253
1264
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Register commands, keyboard shortcuts, and CLI flags
|
|
8
8
|
* - Interact with the user via UI primitives
|
|
9
9
|
*/
|
|
10
|
+
import type { InputOrigin } from "../input-origin.ts";
|
|
10
11
|
import type { UploadRedaction, UploadRedactionOptions } from "../share/upload-redaction.ts";
|
|
11
12
|
import type { ContextEvent } from "./context-event.ts";
|
|
12
13
|
export type { ContextEvent } from "./context-event.ts";
|
|
@@ -2507,7 +2508,8 @@ export interface ExtensionRuntimeState {
|
|
|
2507
2508
|
*/
|
|
2508
2509
|
export interface ExtensionActions {
|
|
2509
2510
|
sendMessage: SendMessageHandler;
|
|
2510
|
-
sendUserMessage
|
|
2511
|
+
/** The runner passes the origin it captured when the extension called `sendUserMessage` (a person's answer is `person`). */
|
|
2512
|
+
sendUserMessage: (content: Parameters<SendUserMessageHandler>[0], options?: Parameters<SendUserMessageHandler>[1], inputOrigin?: InputOrigin) => void;
|
|
2511
2513
|
appendEntry: AppendEntryHandler;
|
|
2512
2514
|
setSessionName: SetSessionNameHandler;
|
|
2513
2515
|
getSessionName: GetSessionNameHandler;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { AssistantMessage, ImageContent, Message, SystemMessage, TextContent, ThinkingSelection, ToolResultMessage, Usage, UserMessage } from "@earendil-works/pi-ai";
|
|
3
3
|
import { EventLoopSlicer } from "./event-loop-slicer.ts";
|
|
4
|
+
import type { InputSource } from "./extensions/types.ts";
|
|
4
5
|
import type { ModelChangeOrigin, ModelChangeSource } from "./model-change-origin.ts";
|
|
5
6
|
import type { RepositoryIdentity } from "./repository-identity.ts";
|
|
6
7
|
import { type SessionListProgress } from "./session-discovery.ts";
|
|
@@ -53,6 +54,12 @@ export interface SessionEntryBase {
|
|
|
53
54
|
export interface SessionMessageEntry extends SessionEntryBase {
|
|
54
55
|
type: "message";
|
|
55
56
|
message: AgentMessage;
|
|
57
|
+
/**
|
|
58
|
+
* Where a user message came from: the `source` its `input` event reported. Stored on the entry, never on
|
|
59
|
+
* the message, so it stays in the session record and out of provider requests. Absent on every other
|
|
60
|
+
* entry and on user input that ran no `input` event (restored, moved or edited input): unknown origin.
|
|
61
|
+
*/
|
|
62
|
+
inputSource?: InputSource;
|
|
56
63
|
}
|
|
57
64
|
export interface ThinkingLevelChangeEntry extends SessionEntryBase {
|
|
58
65
|
type: "thinking_level_change";
|
|
@@ -480,7 +487,9 @@ export declare class SessionManager {
|
|
|
480
487
|
* instead of a full JSON copy (see `_shareMessage` for when it falls back). `appendMessage` keeps the
|
|
481
488
|
* full copy: a caller may still change its object after appending.
|
|
482
489
|
*/
|
|
483
|
-
appendOwnedMessage(message: Message | CustomMessage | BashExecutionMessage
|
|
490
|
+
appendOwnedMessage(message: Message | CustomMessage | BashExecutionMessage, options?: {
|
|
491
|
+
readonly inputSource?: InputSource | undefined;
|
|
492
|
+
}): string;
|
|
484
493
|
private _appendMessage;
|
|
485
494
|
/** Runtime append position for this exact persisted message object, if known. */
|
|
486
495
|
getMessageEntryPosition(message: AgentMessage): Readonly<{
|
|
@@ -1369,16 +1369,17 @@ export class SessionManager {
|
|
|
1369
1369
|
* instead of a full JSON copy (see `_shareMessage` for when it falls back). `appendMessage` keeps the
|
|
1370
1370
|
* full copy: a caller may still change its object after appending.
|
|
1371
1371
|
*/
|
|
1372
|
-
appendOwnedMessage(message) {
|
|
1373
|
-
return this._appendMessage(message, true);
|
|
1372
|
+
appendOwnedMessage(message, options) {
|
|
1373
|
+
return this._appendMessage(message, true, options?.inputSource);
|
|
1374
1374
|
}
|
|
1375
|
-
_appendMessage(message, owned) {
|
|
1375
|
+
_appendMessage(message, owned, inputSource) {
|
|
1376
1376
|
const entry = {
|
|
1377
1377
|
type: "message",
|
|
1378
1378
|
id: generateId(this.idsInUse),
|
|
1379
1379
|
parentId: this.leafId,
|
|
1380
1380
|
timestamp: new Date().toISOString(),
|
|
1381
1381
|
message,
|
|
1382
|
+
...(inputSource === undefined ? {} : { inputSource }),
|
|
1382
1383
|
};
|
|
1383
1384
|
this._appendEntry(entry, owned ? message : undefined);
|
|
1384
1385
|
const order = this.entryOrdersById.get(entry.id);
|
|
@@ -21,6 +21,9 @@ export interface TerminalSettings {
|
|
|
21
21
|
monitorMaxLinesPerInjection?: number;
|
|
22
22
|
monitorMaxCharsPerInjection?: number;
|
|
23
23
|
monitorWakeBudget?: number;
|
|
24
|
+
monitorWakesPerHour?: number;
|
|
25
|
+
monitorRunawayLinesPerMinute?: number;
|
|
26
|
+
monitorRunawayMinutes?: number;
|
|
24
27
|
maxDurableMonitors?: number | "unlimited";
|
|
25
28
|
}
|
|
26
29
|
export interface BranchSummarySettings {
|
package/dist/index.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export { UnknownCommandError, type UnknownCommandReason } from "./core/unknown-c
|
|
|
37
37
|
export { type ModelRoute, type ModelRouteReason, type ModelRouteRequest, VIRTUAL_MODEL_STATE_ENTRY, type VirtualModelDefinition, type VirtualModelStateData, } from "./core/virtual-models.ts";
|
|
38
38
|
export { connectWebViewService, type WebViewServiceConnection } from "./core/webview/webview-broker.ts";
|
|
39
39
|
export { type MainOptions, main } from "./main.ts";
|
|
40
|
-
export { classifyEndpointLiveness, createHostDaemonPaths, DEFAULT_HOST_LAUNCH_SPEC, daemonDirectoryName, decideHostAction, ENDPOINT_REGISTRY_VERSION, type EndpointKind, type EndpointLiveness, type EnsuredHost, type EnsureHostOptions, endpointProbeTimeoutMs, ensureHost, GENERATION_HANDOFF_CAPABILITY, gcHostEndpoints, type HandoffHostOptions, type HandoffRefusal, type HandoffResult, HOST_EXIT_ERROR, HOST_EXIT_FALLBACK, HOST_EXIT_OK, HOST_EXIT_REFUSED, HOST_EXIT_USAGE, HOST_PROTOCOL_VERSION, type HostAction, type HostDaemonPaths, type HostDecision, type HostDecisionClient, type HostDecisionPolicy, type HostDecisionWarning, type HostEndpointEntry, type HostEndpointIdentitySource, type HostEndpointStatus, HostEnsureRefusedError, type HostGcOptions, type HostGcResult, type HostGenerationRow, type HostLaunchSpec, type HostLaunchSpecCore, HostLaunchSpecError, type HostLaunchSpecRefusal, type HostOutcome, type HostProtocolInfo, type HostRefusalReason, type HostRequest, type HostSessionCounts, type HostStatusOptions, type HostStatusReport, type HostTarget, type HostUpgradePolicy, handoffHost, InteractiveMode, type InteractiveModeOptions, isTransportGoneError, type JsonAgentSessionEvent, listHostEndpoints, loadHostLaunchSpec, type ModelInfo, PINNED_HOST_CLIENT_CAPABILITIES, type PrintModeOptions, type ProbeHostOptions, parseHostLaunchSpec, parseHostProtocolInfo, probeHost, REQUIRED_HOST_CAPABILITIES, type ResolvedHostLaunchSpec, RpcClient, type RpcClientEvent, RpcClientOpenInFlightError, type RpcClientOptions, type RpcCommand, type RpcEvalCell, type RpcEvalCellSnapshot, type RpcEventListener, type RpcExtensionEvent, type RpcExtensionUIRequest, type RpcExtensionUIResponse, type RpcResponse, type RpcSessionState, RpcTransportGoneError, readAllHostStatus, readHostStatus, runHostRequest, runPrintMode, runRpcMode, type ShardKind, type StopHostOptions, type StopHostResult, shardKey, shardSocketPath, shardSocketPathForKey, stopHost, TUI_PROBE_TIMEOUT_MS, } from "./modes/index.ts";
|
|
40
|
+
export { classifyEndpointLiveness, createHostDaemonPaths, DEFAULT_HOST_LAUNCH_SPEC, daemonDirectoryName, decideHostAction, ENDPOINT_REGISTRY_VERSION, type EndpointKind, type EndpointLiveness, type EnsuredHost, type EnsureHostOptions, endpointProbeTimeoutMs, ensureHost, GENERATION_HANDOFF_CAPABILITY, gcHostEndpoints, type HandoffHostOptions, type HandoffRefusal, type HandoffResult, HOST_EXIT_ERROR, HOST_EXIT_FALLBACK, HOST_EXIT_OK, HOST_EXIT_REFUSED, HOST_EXIT_USAGE, HOST_PROTOCOL_VERSION, type HostAction, type HostDaemonPaths, type HostDecision, type HostDecisionClient, type HostDecisionPolicy, type HostDecisionWarning, type HostEndpointEntry, type HostEndpointIdentitySource, type HostEndpointStatus, HostEnsureRefusedError, type HostGcOptions, type HostGcResult, type HostGenerationRow, type HostHolder, type HostLaunchSpec, type HostLaunchSpecCore, HostLaunchSpecError, type HostLaunchSpecRefusal, type HostOutcome, type HostProtocolInfo, type HostRefusalReason, type HostRequest, type HostSessionCounts, type HostStatusOptions, type HostStatusReport, type HostTarget, type HostUpgradePolicy, handoffHost, InteractiveMode, type InteractiveModeOptions, isTransportGoneError, type JsonAgentSessionEvent, listHostEndpoints, loadHostLaunchSpec, type ModelInfo, PINNED_HOST_CLIENT_CAPABILITIES, type PrintModeOptions, type ProbeHostOptions, parseHostLaunchSpec, parseHostProtocolInfo, probeHost, REQUIRED_HOST_CAPABILITIES, type ResolvedHostLaunchSpec, RpcClient, type RpcClientEvent, RpcClientOpenInFlightError, type RpcClientOptions, type RpcCommand, type RpcEvalCell, type RpcEvalCellSnapshot, type RpcEventListener, type RpcExtensionEvent, type RpcExtensionUIRequest, type RpcExtensionUIResponse, type RpcResponse, type RpcSessionState, RpcTransportGoneError, readAllHostStatus, readHostStatus, runHostRequest, runPrintMode, runRpcMode, type ShardKind, type StopHostOptions, type StopHostResult, shardKey, shardSocketPath, shardSocketPathForKey, stopHost, TUI_PROBE_TIMEOUT_MS, } from "./modes/index.ts";
|
|
41
41
|
export { ArminComponent, AssistantMessageComponent, BashExecutionComponent, BorderedLoader, BranchSummaryMessageComponent, CompactionSummaryMessageComponent, CustomEditor, type CustomEditorOptions, CustomMessageComponent, DynamicBorder, ExtensionEditorComponent, ExtensionInputComponent, ExtensionSelectorComponent, FooterComponent, keyHint, keyText, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, type RenderDiffOptions, rawKeyHint, renderDiff, SessionSelectorComponent, type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent, ShowImagesSelectorComponent, SkillInvocationMessageComponent, ThemeSelectorComponent, ThinkingSelectorComponent, ToolExecutionComponent, type ToolExecutionOptions, TreeSelectorComponent, truncateToVisualLines, UserMessageComponent, UserMessageSelectorComponent, type VisualTruncateResult, } from "./modes/interactive/components/index.ts";
|
|
42
42
|
export { getLanguageFromPath, getMarkdownTheme, getSelectListTheme, getSettingsListTheme, highlightCode, initTheme, Theme, type ThemeAppearance, type ThemeBg, type ThemeColor, type ThemeStyle, type ThemeToken, } from "./modes/interactive/theme/theme.ts";
|
|
43
43
|
export { collectOrphanedChildren } from "./modes/rpc/child-reaper.ts";
|
package/dist/main.d.ts
CHANGED
|
@@ -8,12 +8,26 @@ import { type Args } from "./cli/args.ts";
|
|
|
8
8
|
import { createStartupLoadingIndicator } from "./cli/startup-loading-indicator.ts";
|
|
9
9
|
import { type ToolRuntimeKind } from "./cli/tool-runtime.ts";
|
|
10
10
|
import { type AgentSessionRuntimeDiagnostic } from "./core/agent-session-services.ts";
|
|
11
|
+
import { AuthStorage } from "./core/auth-storage.ts";
|
|
11
12
|
import type { InlineExtension } from "./core/extensions/types.ts";
|
|
12
13
|
import { ModelRuntime } from "./core/model-runtime.ts";
|
|
13
14
|
import { type AppMode } from "./core/project-trust.ts";
|
|
14
15
|
import { SessionManager } from "./core/session-manager.ts";
|
|
15
16
|
import { SettingsManager } from "./core/settings-manager.ts";
|
|
16
17
|
import type { PreparableRuntimeFactory } from "./modes/rpc/host-warm.ts";
|
|
18
|
+
/**
|
|
19
|
+
* Auth problems for a run's diagnostics. Each credential notice appears once per process; with
|
|
20
|
+
* `persistSeen` (a run a person reads) it is also recorded as seen, so later runs stay quiet until
|
|
21
|
+
* the problem changes (senpi#3486).
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectAuthDiagnostics(authStorage: AuthStorage, context: string, options: {
|
|
24
|
+
persistSeen: boolean;
|
|
25
|
+
}): AgentSessionRuntimeDiagnostic[];
|
|
26
|
+
/** The TUI shows auth problems at startup; credential notices are marked seen once shown (senpi#3486). */
|
|
27
|
+
export declare function interactiveAuthNotices(authStorage: AuthStorage): {
|
|
28
|
+
messages: string[];
|
|
29
|
+
markSeen: () => void;
|
|
30
|
+
};
|
|
17
31
|
export declare function collectExtensionLoadDiagnostics(errors: readonly {
|
|
18
32
|
path: string;
|
|
19
33
|
error: string;
|
package/dist/main.js
CHANGED
|
@@ -74,11 +74,27 @@ import { isLocalPath, normalizePath, resolvePath } from "./utils/paths.js";
|
|
|
74
74
|
import { readPipedStdin } from "./utils/piped-stdin.js";
|
|
75
75
|
import { cleanupWindowsSelfUpdateQuarantine } from "./utils/windows-self-update.js";
|
|
76
76
|
const EXTENSION_LOAD_FAILURE_HINT = `Hint: Start without extensions using "${APP_NAME} -ne".`;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Auth problems for a run's diagnostics. Each credential notice appears once per process; with
|
|
79
|
+
* `persistSeen` (a run a person reads) it is also recorded as seen, so later runs stay quiet until
|
|
80
|
+
* the problem changes (senpi#3486).
|
|
81
|
+
*/
|
|
82
|
+
export function collectAuthDiagnostics(authStorage, context, options) {
|
|
83
|
+
return [
|
|
84
|
+
...authStorage.drainErrors().map((error) => error.message),
|
|
85
|
+
...authStorage.takePendingNotices(options).map((notice) => notice.message),
|
|
86
|
+
].map((message) => ({ type: "warning", message: `(${context}, auth storage) ${message}` }));
|
|
87
|
+
}
|
|
88
|
+
/** The TUI shows auth problems at startup; credential notices are marked seen once shown (senpi#3486). */
|
|
89
|
+
export function interactiveAuthNotices(authStorage) {
|
|
90
|
+
const pending = authStorage.takePendingNotices({ persistSeen: false });
|
|
91
|
+
return {
|
|
92
|
+
messages: [
|
|
93
|
+
...authStorage.drainErrors().map((error) => error.message),
|
|
94
|
+
...pending.map((notice) => notice.message),
|
|
95
|
+
],
|
|
96
|
+
markSeen: () => authStorage.markNoticesSeen(pending.map((notice) => notice.id)),
|
|
97
|
+
};
|
|
82
98
|
}
|
|
83
99
|
export function collectExtensionLoadDiagnostics(errors) {
|
|
84
100
|
return errors.map(({ path, error }) => ({
|
|
@@ -1020,7 +1036,7 @@ export async function main(args, options) {
|
|
|
1020
1036
|
reportDiagnostics([
|
|
1021
1037
|
...services.diagnostics,
|
|
1022
1038
|
...collectSettingsDiagnosticsWithContext(services.settingsManager, "model listing"),
|
|
1023
|
-
...collectAuthDiagnostics(services.authStorage, "model listing"),
|
|
1039
|
+
...collectAuthDiagnostics(services.authStorage, "model listing", { persistSeen: true }),
|
|
1024
1040
|
]);
|
|
1025
1041
|
const searchPattern = typeof parsed.listModels === "string" ? parsed.listModels : undefined;
|
|
1026
1042
|
await printThenExit(() => listModels(services.modelRuntime, searchPattern), 0);
|
|
@@ -1216,7 +1232,7 @@ export async function main(args, options) {
|
|
|
1216
1232
|
type: "warning",
|
|
1217
1233
|
message: diagnostic.path ? `${diagnostic.path}: ${diagnostic.message}` : diagnostic.message,
|
|
1218
1234
|
})));
|
|
1219
|
-
reportDiagnostics(collectAuthDiagnostics(services.authStorage, "runtime creation"));
|
|
1235
|
+
reportDiagnostics(collectAuthDiagnostics(services.authStorage, "runtime creation", { persistSeen: appMode !== "rpc" }));
|
|
1220
1236
|
}
|
|
1221
1237
|
if (runtime.diagnostics.some((diagnostic) => diagnostic.type === "error")) {
|
|
1222
1238
|
if (runtime.diagnostics.some((diagnostic) => diagnostic.message.includes("Failed to load extension"))) {
|
|
@@ -1266,6 +1282,7 @@ export async function main(args, options) {
|
|
|
1266
1282
|
const { InteractiveMode } = await import("./modes/interactive/interactive-mode.js");
|
|
1267
1283
|
const interactiveMode = new InteractiveMode(runtime, {
|
|
1268
1284
|
migratedProviders,
|
|
1285
|
+
authNotices: interactiveAuthNotices(services.authStorage),
|
|
1269
1286
|
legacyPiEditNotice: legacyPiEditStartupNotice(),
|
|
1270
1287
|
modelFallbackMessage,
|
|
1271
1288
|
autoTrustOnReloadCwd,
|
|
@@ -1308,7 +1325,7 @@ export async function main(args, options) {
|
|
|
1308
1325
|
initialMessage,
|
|
1309
1326
|
initialImages,
|
|
1310
1327
|
});
|
|
1311
|
-
reportDiagnostics(collectAuthDiagnostics(services.authStorage, "print mode"));
|
|
1328
|
+
reportDiagnostics(collectAuthDiagnostics(services.authStorage, "print mode", { persistSeen: true }));
|
|
1312
1329
|
stopChildReaper();
|
|
1313
1330
|
stopThemeWatcher();
|
|
1314
1331
|
restoreStdout();
|