@code-yeongyu/senpi 2026.11.3-1 → 2026.11.3-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 +27 -0
- package/dist/bundle/chunks/anthropic-messages-LJAKRKMX.js +11 -0
- package/dist/bundle/chunks/{args-O3WS4WCO.js → args-GQTXDYS3.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-YLBBQXI7.js → azure-openai-responses-HDEKTPMZ.js} +1 -1
- package/dist/bundle/chunks/chatgpt-subscription.js +10 -2
- package/dist/bundle/chunks/{chunk-ED7VJGL4.js → chunk-2PGSRQBF.js} +1 -1
- package/dist/bundle/chunks/{chunk-4BF4FLT6.js → chunk-3LDASCJN.js} +1 -1
- package/dist/bundle/chunks/{chunk-PIAY636U.js → chunk-3QPE75AR.js} +1 -1
- package/dist/bundle/chunks/{chunk-FQZZGSYH.js → chunk-4ZGQUOBV.js} +1 -1
- package/dist/bundle/chunks/{chunk-S2M5D5GS.js → chunk-5BMIET5O.js} +2 -2
- package/dist/bundle/chunks/{chunk-XLLQJALL.js → chunk-67M7HKXO.js} +1 -1
- package/dist/bundle/chunks/chunk-6ISOXWH2.js +2 -0
- package/dist/bundle/chunks/{chunk-7KLEFHSU.js → chunk-6NPK2O3C.js} +2 -2
- package/dist/bundle/chunks/{chunk-BNIZP4OS.js → chunk-7C4W5HMU.js} +1 -1
- package/dist/bundle/chunks/chunk-7KIHKE2T.js +2 -0
- package/dist/bundle/chunks/{chunk-WCRNJYHO.js → chunk-A4KOQUHT.js} +1 -1
- package/dist/bundle/chunks/{chunk-TOOB6OLG.js → chunk-A5PZGQCP.js} +1 -1
- package/dist/bundle/chunks/{chunk-TBARMJ75.js → chunk-AVP6DDBF.js} +1 -1
- package/dist/bundle/chunks/{chunk-FLM6GZPT.js → chunk-B3GTID4P.js} +1 -1
- package/dist/bundle/chunks/{chunk-2XBKE2BZ.js → chunk-BIWFWGUA.js} +1 -1
- package/dist/bundle/chunks/{chunk-6LDUM5CL.js → chunk-DCI4QOUX.js} +1 -1
- package/dist/bundle/chunks/{chunk-2YFPDNNP.js → chunk-DNZKKG4V.js} +1 -1
- package/dist/bundle/chunks/{chunk-W2V7RIBF.js → chunk-E7SA75PP.js} +1 -1
- package/dist/bundle/chunks/{chunk-YWD54RHU.js → chunk-EBZ6IP4V.js} +1 -1
- package/dist/bundle/chunks/{chunk-J2OBN7TI.js → chunk-G5XUGK4B.js} +1 -1
- package/dist/bundle/chunks/{chunk-PX7FF5LE.js → chunk-GKP34H3C.js} +2 -2
- package/dist/bundle/chunks/{chunk-PP52OWBB.js → chunk-GLTOBXZY.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZNLCKKF6.js → chunk-IJGXAACA.js} +117 -35
- package/dist/bundle/chunks/{chunk-LGO25HPL.js → chunk-JCTODLUP.js} +1 -1
- package/dist/bundle/chunks/{chunk-3ESZE3DG.js → chunk-KHBCZ6TR.js} +1 -1
- package/dist/bundle/chunks/chunk-KJR3XXMI.js +2 -0
- package/dist/bundle/chunks/{chunk-FJ2UCBOA.js → chunk-KY4V3TSO.js} +2 -2
- package/dist/bundle/chunks/{chunk-4SENPCEA.js → chunk-MCXOJHN2.js} +1 -1
- package/dist/bundle/chunks/{chunk-DPBPO5OU.js → chunk-NKI7L5UM.js} +3 -3
- package/dist/bundle/chunks/{chunk-CJWSQ6CK.js → chunk-OD7RT7G7.js} +1 -1
- package/dist/bundle/chunks/{chunk-F4OHTL2K.js → chunk-OOU3BRUY.js} +1 -1
- package/dist/bundle/chunks/{chunk-LPHUMR6R.js → chunk-OT6ALXSJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-XGBRBCIQ.js → chunk-Q5CKFBQT.js} +1 -1
- package/dist/bundle/chunks/{chunk-F7GBSQRV.js → chunk-QPYDQ4QK.js} +1 -1
- package/dist/bundle/chunks/{chunk-MOOFLKLI.js → chunk-QSMT5KVJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-SAZ6E74O.js → chunk-RUOTFZQR.js} +1 -1
- package/dist/bundle/chunks/{chunk-5DZD623T.js → chunk-RWF6IJQL.js} +2 -2
- package/dist/bundle/chunks/{chunk-HBCNDC53.js → chunk-S5Y7NEHB.js} +1 -1
- package/dist/bundle/chunks/{chunk-DAPBNRN5.js → chunk-SCBTASM4.js} +1 -1
- package/dist/bundle/chunks/chunk-SJWTZ3Z2.js +2 -0
- package/dist/bundle/chunks/{chunk-ISZ4EWIS.js → chunk-SODMQEHO.js} +1 -1
- package/dist/bundle/chunks/{chunk-WEGTCUO3.js → chunk-SQAQEITV.js} +1 -1
- package/dist/bundle/chunks/{chunk-UFVZTQKN.js → chunk-TQQG5L4D.js} +1 -1
- package/dist/bundle/chunks/{chunk-VPDQ6MOL.js → chunk-U6A4X3FV.js} +1 -1
- package/dist/bundle/chunks/{chunk-ACIFXWJ6.js → chunk-UHUM55TR.js} +1 -1
- package/dist/bundle/chunks/{chunk-L5OFGHDA.js → chunk-USKWGPA6.js} +1 -1
- package/dist/bundle/chunks/{chunk-7ORS33CE.js → chunk-VDDJADPV.js} +1 -1
- package/dist/bundle/chunks/{chunk-R65P6LAT.js → chunk-VIHXG53S.js} +1 -1
- package/dist/bundle/chunks/{chunk-SGS76663.js → chunk-VPVD5G32.js} +1 -1
- package/dist/bundle/chunks/chunk-WO6I5PRF.js +3 -0
- package/dist/bundle/chunks/{chunk-ETCYVVVO.js → chunk-WVEIJ437.js} +1 -1
- package/dist/bundle/chunks/{chunk-7565XQAU.js → chunk-XKFG4N3Z.js} +1 -1
- package/dist/bundle/chunks/{chunk-57MRFI6E.js → chunk-XMWPCJU6.js} +1 -1
- package/dist/bundle/chunks/{chunk-GDPWQJDQ.js → chunk-XPSXUKSD.js} +1 -1
- package/dist/bundle/chunks/{chunk-FNBEICAA.js → chunk-YI6KRJDC.js} +1 -1
- package/dist/bundle/chunks/{chunk-UURGXV5B.js → chunk-YP47G4YP.js} +1 -1
- package/dist/bundle/chunks/{chunk-6BSMXZKQ.js → chunk-YRYDCKUI.js} +1 -1
- package/dist/bundle/chunks/{chunk-QTQ3ES2S.js → chunk-YTKZSGMQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-V36CXIIB.js → chunk-YXL3PNTL.js} +1 -1
- package/dist/bundle/chunks/chunk-Z7HTHT6K.js +2 -0
- package/dist/bundle/chunks/{chunk-27XEDYQA.js → chunk-ZTPG27EJ.js} +1 -1
- package/dist/bundle/chunks/{cli-main-XM5FWY3Z.js → cli-main-RN7YB427.js} +1 -1
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-FC7QLRU2.js → cloudflare-workers-ai-system-one-N5KFNKK4.js} +1 -1
- package/dist/bundle/chunks/credential-accounts-BJX55MTT.js +2 -0
- package/dist/bundle/chunks/devin.js +10 -2
- package/dist/bundle/chunks/{dispatch-UY6LBJJE.js → dispatch-M7UAPGVY.js} +1 -1
- package/dist/bundle/chunks/{google-generative-ai-7PQ4QRHJ.js → google-generative-ai-2TZSCSIS.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-DY7EO4EJ.js → google-vertex-7NJJOI7V.js} +1 -1
- package/dist/bundle/chunks/{health-write-45CX64T2.js → health-write-YUSPVH7U.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-3EDGRKQM.js → help-fast-path-DWCA77QP.js} +1 -1
- package/dist/bundle/chunks/host-command-KJXUTSQ7.js +2 -0
- package/dist/bundle/chunks/{host-lifecycle-TGG7KGQH.js → host-lifecycle-QHIJ4G2K.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +1 -1
- package/dist/bundle/chunks/{host-session-command-OJFARE5Q.js → host-session-command-IPH3WXSG.js} +1 -1
- package/dist/bundle/chunks/{host-socket-request-F2R5P34M.js → host-socket-request-XPXJV3CS.js} +1 -1
- package/dist/bundle/chunks/interactive-mode-XUXMDYLA.js +2 -0
- package/dist/bundle/chunks/{list-tips-3D3A6GFK.js → list-tips-XJY7EL5S.js} +1 -1
- package/dist/bundle/chunks/{login-health-YOPTYRTU.js → login-health-226NO44U.js} +1 -1
- package/dist/bundle/chunks/main-P7PKMJUG.js +2 -0
- package/dist/bundle/chunks/{mistral-conversations-6F2RAIB2.js → mistral-conversations-QMHMHSUF.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-N2RQ7JSZ.js → multi-session-host-ZQY4AN46.js} +1 -1
- package/dist/bundle/chunks/{native-loader-NMDTMHMA.js → native-loader-A6DNMGCK.js} +1 -1
- package/dist/bundle/chunks/openai-chatgpt.js +11 -3
- package/dist/bundle/chunks/{openai-codex-responses-YTWLG4OL.js → openai-codex-responses-GUAHKQE2.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-TIFDYHZL.js → openai-completions-UPRTFM54.js} +1 -1
- package/dist/bundle/chunks/{openai-images-OAEMY4RG.js → openai-images-IKWNS6VB.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-L35NIG5Y.js → openai-responses-AXN5NUHH.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +10 -2
- package/dist/bundle/chunks/{package-manager-cli-JPNA27E5.js → package-manager-cli-PP7L2Q5M.js} +1 -1
- package/dist/bundle/chunks/radius.js +10 -2
- package/dist/bundle/chunks/{rg-engine-JDUYBM6L.js → rg-engine-VRSXT6HG.js} +1 -1
- package/dist/bundle/chunks/{role-45Y7PQFC.js → role-L5J7ZPCF.js} +1 -1
- package/dist/bundle/chunks/rotation-stream-6INZXQFC.js +2 -0
- package/dist/bundle/chunks/rpc-mode-W4UATRAT.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-5TUR27NY.js → rpc-monitor-source-RMMUPBMS.js} +1 -1
- package/dist/bundle/chunks/{run-attached-tui-TI6MELEI.js → run-attached-tui-RLXFGCEP.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-OZ5ESMIT.js → schedule-command-ZNFEJNTT.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-5YKBYJOO.js → session-control-endpoint-TWTONJWF.js} +1 -1
- package/dist/bundle/chunks/{session-picker-CMRV72VA.js → session-picker-KRUVCHOV.js} +1 -1
- package/dist/bundle/chunks/session-slot-NJOZCOHQ.js +2 -0
- package/dist/bundle/chunks/session-worker.js +218 -135
- package/dist/bundle/chunks/{state-store-Q47AYMRI.js → state-store-ZAFZ4JOM.js} +1 -1
- package/dist/bundle/chunks/{typesafe-system-one-5JYDJK6S.js → typesafe-system-one-CICIRAYN.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.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.js +5 -3
- package/dist/core/auth-storage.d.ts +19 -0
- package/dist/core/auth-storage.js +99 -24
- package/dist/core/auth-store-load-error.d.ts +29 -0
- package/dist/core/auth-store-load-error.js +74 -0
- package/dist/core/brand.d.ts +2 -0
- package/dist/core/brand.js +1 -0
- package/dist/core/credential-pool/failover.d.ts +8 -0
- package/dist/core/credential-pool/failover.js +18 -5
- package/dist/core/credential-pool/rotation-stream.d.ts +1 -1
- package/dist/core/credential-pool/rotation-stream.js +2 -7
- package/dist/core/credential-pool/session-slot.d.ts +20 -0
- package/dist/core/credential-pool/session-slot.js +26 -0
- package/dist/core/credential-recovery-revision.d.ts +10 -0
- package/dist/core/credential-recovery-revision.js +19 -0
- package/dist/core/execution/executor-body.d.ts +22 -0
- package/dist/core/execution/executor-body.js +73 -0
- package/dist/core/execution/executor-freeze.d.ts +17 -0
- package/dist/core/execution/executor-freeze.js +53 -0
- package/dist/core/execution/executor-pipeline.d.ts +9 -0
- package/dist/core/execution/executor-pipeline.js +25 -0
- package/dist/core/execution/executor-types.d.ts +82 -0
- package/dist/core/execution/executor-types.js +2 -0
- package/dist/core/execution/executor.d.ts +8 -0
- package/dist/core/execution/executor.js +216 -0
- package/dist/core/extensions/builtin/anthropic-subscription/account-block-log.d.ts +15 -0
- package/dist/core/extensions/builtin/anthropic-subscription/account-block-log.js +18 -0
- package/dist/core/extensions/builtin/anthropic-subscription/affinity.d.ts +9 -1
- package/dist/core/extensions/builtin/anthropic-subscription/affinity.js +15 -4
- package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +7 -1
- package/dist/core/extensions/builtin/anthropic-subscription/failover.js +19 -2
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts +8 -2
- package/dist/core/extensions/builtin/anthropic-subscription/guidance.js +25 -1
- package/dist/core/extensions/builtin/anthropic-subscription/provider-diagnostic.d.ts +9 -0
- package/dist/core/extensions/builtin/anthropic-subscription/provider-diagnostic.js +59 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-registry-wiring.js +7 -0
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +5 -3
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +4 -0
- package/dist/core/extensions/builtin/btw/index.js +3 -1
- package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -1
- package/dist/core/extensions/builtin/compaction/index.js +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +3 -2
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js +3 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/native-bootstrap.js +3 -1
- package/dist/core/extensions/builtin/look-at/runner.js +3 -1
- package/dist/core/extensions/builtin/mcp/auth/callback.js +3 -4
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +2 -1
- package/dist/core/model-registry.d.ts +10 -2
- package/dist/core/model-registry.js +12 -4
- package/dist/core/model-runtime.d.ts +8 -0
- package/dist/core/model-runtime.js +38 -0
- package/dist/core/retry-fallback/controller-types.d.ts +7 -0
- package/dist/core/retry-fallback/controller.d.ts +2 -0
- package/dist/core/retry-fallback/controller.js +13 -4
- package/dist/core/retry-fallback/credential-recovery.d.ts +18 -0
- package/dist/core/retry-fallback/credential-recovery.js +32 -0
- package/dist/core/retry-fallback/settings.d.ts +8 -1
- package/dist/core/retry-fallback/settings.js +12 -6
- package/dist/core/session-log.js +1 -1
- package/dist/core/settings-manager.js +4 -1
- package/dist/modes/rpc/connection-handler.js +3 -1
- package/dist/modes/rpc/effect-commands.d.ts +1 -0
- package/dist/modes/rpc/loopless-execute-grant.d.ts +20 -0
- package/dist/modes/rpc/loopless-execute-grant.js +26 -0
- package/dist/modes/rpc/loopless-runtime-settings.d.ts +30 -0
- package/dist/modes/rpc/loopless-runtime-settings.js +69 -0
- package/dist/modes/rpc/loopless-tool-call.d.ts +12 -0
- package/dist/modes/rpc/loopless-tool-call.js +22 -0
- package/dist/modes/rpc/rpc-read-response-types.d.ts +2 -1
- package/dist/modes/rpc/rpc-session-state-types.d.ts +10 -0
- package/dist/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/modes/rpc/runtime-factory-loopless.d.ts +57 -0
- package/dist/modes/rpc/runtime-factory-loopless.js +131 -0
- package/docs/development.md +1 -1
- package/docs/rpc.md +2 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/anthropic-messages-AFWQB3KN.js +0 -10
- package/dist/bundle/chunks/chunk-3WJ2DE2G.js +0 -3
- package/dist/bundle/chunks/chunk-ESQBKTNG.js +0 -2
- package/dist/bundle/chunks/chunk-MOH5HOHX.js +0 -2
- package/dist/bundle/chunks/credential-accounts-YZYSKA7X.js +0 -2
- package/dist/bundle/chunks/host-command-PZ4VYQIW.js +0 -2
- package/dist/bundle/chunks/interactive-mode-7JSJVVIS.js +0 -2
- package/dist/bundle/chunks/main-3YGJ2O4I.js +0 -2
- package/dist/bundle/chunks/rotation-stream-WXWJKJZN.js +0 -2
- package/dist/bundle/chunks/rpc-mode-Z7MN2ZWA.js +0 -2
|
@@ -43,7 +43,14 @@ export interface ResolvedRetryFallbackSettings {
|
|
|
43
43
|
* `claude-opus-4-8` or `claude-opus-4-6` (senpi#3727).
|
|
44
44
|
*/
|
|
45
45
|
export declare const DEFAULT_FALLBACK_CHAINS: FallbackChains;
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* `sessionPolicy`: the chains were supplied by the session's opener (`open_session.retryFallback`,
|
|
48
|
+
* `set_retry_fallback`), so they are the session's whole policy and the shipped defaults are not merged
|
|
49
|
+
* back under them (senpi#4673). Settings-file chains still layer over the defaults.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveRetryFallbackSettings(settings: RetrySettings | undefined, options?: {
|
|
52
|
+
readonly sessionPolicy?: boolean;
|
|
53
|
+
}): ResolvedRetryFallbackSettings;
|
|
47
54
|
export declare function resolveAbortServerSideFallback(settings: RetrySettings | undefined): boolean;
|
|
48
55
|
export declare const DEFAULT_HINTED_WAIT_CAP_MS = 300000;
|
|
49
56
|
export declare const DEFAULT_PROBE_BACK_MAX_MS = 3600000;
|
|
@@ -46,13 +46,14 @@ function isStringArray(value) {
|
|
|
46
46
|
* Cross-scope replacement (global vs project) stays wholesale and is handled by
|
|
47
47
|
* `deepMergeSettings`; this only resolves defaults against the merged result.
|
|
48
48
|
*/
|
|
49
|
-
function resolveFallbackChains(value) {
|
|
49
|
+
function resolveFallbackChains(value, sessionPolicy) {
|
|
50
|
+
const base = () => (sessionPolicy ? {} : cloneDefaultFallbackChains());
|
|
50
51
|
if (value === undefined || !isPlainObject(value))
|
|
51
|
-
return
|
|
52
|
-
const chains =
|
|
52
|
+
return base();
|
|
53
|
+
const chains = base();
|
|
53
54
|
for (const [key, entries] of Object.entries(value)) {
|
|
54
55
|
if (!isStringArray(entries))
|
|
55
|
-
return
|
|
56
|
+
return base();
|
|
56
57
|
// An empty list stays in the map as a tombstone: canonicalization needs it to
|
|
57
58
|
// suppress the expanded default for that family or provider variant, and
|
|
58
59
|
// dropping it here would let the shipped default reappear after expansion.
|
|
@@ -60,10 +61,15 @@ function resolveFallbackChains(value) {
|
|
|
60
61
|
}
|
|
61
62
|
return chains;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
/**
|
|
65
|
+
* `sessionPolicy`: the chains were supplied by the session's opener (`open_session.retryFallback`,
|
|
66
|
+
* `set_retry_fallback`), so they are the session's whole policy and the shipped defaults are not merged
|
|
67
|
+
* back under them (senpi#4673). Settings-file chains still layer over the defaults.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveRetryFallbackSettings(settings, options = {}) {
|
|
64
70
|
return {
|
|
65
71
|
modelFallback: typeof settings?.modelFallback === "boolean" ? settings.modelFallback : true,
|
|
66
|
-
chains: resolveFallbackChains(settings?.fallbackChains),
|
|
72
|
+
chains: resolveFallbackChains(settings?.fallbackChains, options.sessionPolicy === true),
|
|
67
73
|
revertPolicy: settings?.fallbackRevertPolicy === "never" ? "never" : "cooldown-expiry",
|
|
68
74
|
};
|
|
69
75
|
}
|
package/dist/core/session-log.js
CHANGED
|
@@ -7,7 +7,7 @@ const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
|
7
7
|
const MAX_STRING_LENGTH = 200;
|
|
8
8
|
const DEBUG_PREFIX = `[${APP_NAME}-session]`;
|
|
9
9
|
const BLOCKED_KEY = /^(?:__proto__|constructor|prototype|headers?|env(?:ironment)?|authorization|credential(?:s)?|password|secret|token|api_?key|client_?secret)$/i;
|
|
10
|
-
const ALLOWED_DATA_KEY = /^(?:action|attemptId|disposition|stage|error|mode|count|willRetry|deferAdmission|delivered|restored|cause|accepted|skipped|rejectionCause|reason|durationMs|kind|retryable|phase|op|bytes|generation|requestId|tokens|tokensBefore|tokensAfter|contextWindow|attempt|aborted|sessionId|role|provider|model|source|actor|from|to|duringTurn|persistDefault|extension|handlerEvent|queuedId|ageMs)$/;
|
|
10
|
+
const ALLOWED_DATA_KEY = /^(?:action|attemptId|disposition|stage|error|mode|count|willRetry|deferAdmission|delivered|restored|cause|accepted|skipped|rejectionCause|reason|durationMs|kind|retryable|phase|op|bytes|generation|requestId|tokens|tokensBefore|tokensAfter|contextWindow|attempt|aborted|sessionId|role|provider|model|source|actor|from|to|duringTurn|persistDefault|extension|handlerEvent|queuedId|ageMs|seat|until|shared)$/;
|
|
11
11
|
const SENSITIVE_TEXT = /((?:authorization\s*[:=]\s*(?:bearer|basic)\s+)|(?:bearer\s+)|(?:[?&](?:api[_-]?key|token|secret|password|auth(?:orization)?)=))[^\s&,"'}\]]+/gi;
|
|
12
12
|
export function createSessionLogger(agentDir, options = {}) {
|
|
13
13
|
if (typeof agentDir !== "string" || agentDir.length === 0) {
|
|
@@ -1285,7 +1285,10 @@ export class SettingsManager {
|
|
|
1285
1285
|
return undefined;
|
|
1286
1286
|
}
|
|
1287
1287
|
getRetryFallbackSettings() {
|
|
1288
|
-
|
|
1288
|
+
// Chains in the session-only override layer (applyRetryFallbackProfile, or an SDK applyOverrides call) are the session's whole policy.
|
|
1289
|
+
return resolveRetryFallbackSettings(this.settings.retry, {
|
|
1290
|
+
sessionPolicy: this.runtimeOverrides.retry?.fallbackChains !== undefined,
|
|
1291
|
+
});
|
|
1289
1292
|
}
|
|
1290
1293
|
getFallbackCircuitSettings() {
|
|
1291
1294
|
return resolveFallbackCircuitSettings(this.settings.fallback);
|
|
@@ -1705,7 +1705,9 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1705
1705
|
...(origin === undefined ? {} : { origin }),
|
|
1706
1706
|
};
|
|
1707
1707
|
});
|
|
1708
|
-
|
|
1708
|
+
// Read after the rows: building them re-reads a store that failed to load (#4413).
|
|
1709
|
+
const storeError = modelRegistry.authStorage.getLoadError();
|
|
1710
|
+
return success(id, "get_auth_providers", { providers, ...(storeError ? { storeError } : {}) });
|
|
1709
1711
|
}
|
|
1710
1712
|
case "login_start": {
|
|
1711
1713
|
// Respond IMMEDIATELY: success:true means the flow has started. The
|
|
@@ -817,6 +817,7 @@ export declare function runSetThinkingLevel(ctx: EffectCommandContext, command:
|
|
|
817
817
|
success: true;
|
|
818
818
|
data: {
|
|
819
819
|
providers: import("./rpc-session-state-types.ts").RpcAuthProvider[];
|
|
820
|
+
storeError?: import("./rpc-session-state-types.ts").RpcAuthStoreError;
|
|
820
821
|
};
|
|
821
822
|
} | {
|
|
822
823
|
id?: string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who may run tool calls on a loopless runtime (senpi#5413). Until the remote tool executor runs the extensions'
|
|
3
|
+
* `tool_call`/`tool_result` handlers (permission checks among them) for a tool-execution-only process, nothing in the
|
|
4
|
+
* product may execute through a loopless runtime: the only allowed caller is the in-process test harness. Closed and
|
|
5
|
+
* fail-closed: a runtime built without a grant this module issued refuses every call.
|
|
6
|
+
*/
|
|
7
|
+
declare const LOOPLESS_EXECUTE_CALLERS: readonly ["in-process-test-harness"];
|
|
8
|
+
export type LooplessExecuteCaller = (typeof LOOPLESS_EXECUTE_CALLERS)[number];
|
|
9
|
+
export interface LooplessExecuteGrant {
|
|
10
|
+
readonly caller: LooplessExecuteCaller;
|
|
11
|
+
}
|
|
12
|
+
export declare class LooplessExecuteRefusedError extends Error {
|
|
13
|
+
constructor(reason: string);
|
|
14
|
+
}
|
|
15
|
+
/** Issues the test harness's grant; anywhere but a Vitest run it refuses. */
|
|
16
|
+
export declare function grantLooplessExecuteToTestHarness(): LooplessExecuteGrant;
|
|
17
|
+
/** True only for a grant this module issued to an allowed caller. */
|
|
18
|
+
export declare function isLooplessExecuteGranted(grant: LooplessExecuteGrant | undefined): boolean;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=loopless-execute-grant.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who may run tool calls on a loopless runtime (senpi#5413). Until the remote tool executor runs the extensions'
|
|
3
|
+
* `tool_call`/`tool_result` handlers (permission checks among them) for a tool-execution-only process, nothing in the
|
|
4
|
+
* product may execute through a loopless runtime: the only allowed caller is the in-process test harness. Closed and
|
|
5
|
+
* fail-closed: a runtime built without a grant this module issued refuses every call.
|
|
6
|
+
*/
|
|
7
|
+
const LOOPLESS_EXECUTE_CALLERS = ["in-process-test-harness"];
|
|
8
|
+
const issued = new WeakSet();
|
|
9
|
+
export class LooplessExecuteRefusedError extends Error {
|
|
10
|
+
constructor(reason) {
|
|
11
|
+
super(`loopless_execute_refused: ${reason}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Issues the test harness's grant; anywhere but a Vitest run it refuses. */
|
|
15
|
+
export function grantLooplessExecuteToTestHarness() {
|
|
16
|
+
if (process.env.VITEST !== "true")
|
|
17
|
+
throw new LooplessExecuteRefusedError("only the in-process test harness may execute");
|
|
18
|
+
const grant = Object.freeze({ caller: "in-process-test-harness" });
|
|
19
|
+
issued.add(grant);
|
|
20
|
+
return grant;
|
|
21
|
+
}
|
|
22
|
+
/** True only for a grant this module issued to an allowed caller. */
|
|
23
|
+
export function isLooplessExecuteGranted(grant) {
|
|
24
|
+
return grant !== undefined && issued.has(grant) && LOOPLESS_EXECUTE_CALLERS.includes(grant.caller);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=loopless-execute-grant.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AgentSessionRuntimeDiagnostic } from "../../core/agent-session-services.ts";
|
|
2
|
+
import type { ResourceLoaderReloadOptions } from "../../core/resource-loader.ts";
|
|
3
|
+
import { SettingsManager } from "../../core/settings-manager.ts";
|
|
4
|
+
import type { Args } from "../../cli/args.ts";
|
|
5
|
+
import type { CliRuntimeConfiguration } from "../../main.ts";
|
|
6
|
+
import type { LooplessRuntimeProfile } from "./runtime-factory-loopless.ts";
|
|
7
|
+
export interface LooplessRuntimeSettings {
|
|
8
|
+
readonly settingsManager: SettingsManager;
|
|
9
|
+
readonly flagValues: Map<string, boolean | string>;
|
|
10
|
+
readonly reloadOptions: ResourceLoaderReloadOptions | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The project trust a session opened from this configuration would get, decided without a UI: a runtime without a
|
|
14
|
+
* loop has no one to ask, exactly like an RPC worker today (`createCliRuntimeFactory`, main.ts). A `project_trust`
|
|
15
|
+
* handler's error is reported to `diagnostics`, as the CLI factory reports it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveLooplessSettings(configuration: CliRuntimeConfiguration, startupSettingsManager: SettingsManager, profile: LooplessRuntimeProfile, diagnostics: AgentSessionRuntimeDiagnostic[]): Promise<LooplessRuntimeSettings>;
|
|
18
|
+
export declare function resolveCliPaths(cwd: string, paths: string[] | undefined): string[] | undefined;
|
|
19
|
+
export interface LooplessToolFilters {
|
|
20
|
+
readonly defaultToolNames?: ReadonlySet<string>;
|
|
21
|
+
readonly allowedToolNames?: ReadonlySet<string>;
|
|
22
|
+
readonly excludedToolNames?: ReadonlySet<string>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The CLI's tool filters (`--tools`, `--no-tools`, `--no-builtin-tools`, `--exclude-tools`, settings `defaultTools`),
|
|
26
|
+
* mapped as `main.ts` maps them and resolved as `createAgentSession` resolves them (sdk.ts), so a loopless runtime
|
|
27
|
+
* registers exactly the tools a session registers for the same flags and settings.
|
|
28
|
+
*/
|
|
29
|
+
export declare function looplessToolFilters(parsed: Args, settingsManager: SettingsManager): LooplessToolFilters;
|
|
30
|
+
//# sourceMappingURL=loopless-runtime-settings.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { presetTrustsProject, resolveProjectTrusted } from "../../core/project-trust.js";
|
|
2
|
+
import { sessionExtensionFlagValues } from "../../core/session-extension-flags.js";
|
|
3
|
+
import { SettingsManager } from "../../core/settings-manager.js";
|
|
4
|
+
import { getSavedProjectTrust, hasTrustRequiringProjectResources, ProjectTrustStore, } from "../../core/trust-manager.js";
|
|
5
|
+
import { createProjectTrustContext } from "../../cli/project-trust.js";
|
|
6
|
+
import { isLocalPath, resolvePath } from "../../utils/paths.js";
|
|
7
|
+
/**
|
|
8
|
+
* The project trust a session opened from this configuration would get, decided without a UI: a runtime without a
|
|
9
|
+
* loop has no one to ask, exactly like an RPC worker today (`createCliRuntimeFactory`, main.ts). A `project_trust`
|
|
10
|
+
* handler's error is reported to `diagnostics`, as the CLI factory reports it.
|
|
11
|
+
*/
|
|
12
|
+
export async function resolveLooplessSettings(configuration, startupSettingsManager, profile, diagnostics) {
|
|
13
|
+
const { parsed, agentDir, appMode } = configuration;
|
|
14
|
+
const { cwd, launchProfile } = profile;
|
|
15
|
+
const trustStore = new ProjectTrustStore(agentDir);
|
|
16
|
+
const flagValues = sessionExtensionFlagValues(parsed.unknownFlags, launchProfile);
|
|
17
|
+
const hasTrustRequiringResources = hasTrustRequiringProjectResources(cwd);
|
|
18
|
+
const savedProjectTrust = await getSavedProjectTrust(trustStore, cwd);
|
|
19
|
+
const globalPreset = startupSettingsManager.getGlobalSettings()
|
|
20
|
+
.permissionPreset;
|
|
21
|
+
const fullAccessTrust = presetTrustsProject(flagValues.get("permission-preset"), globalPreset) && savedProjectTrust !== false;
|
|
22
|
+
const shouldResolveProjectTrust = parsed.projectTrustOverride === undefined && !fullAccessTrust && hasTrustRequiringResources;
|
|
23
|
+
const projectTrusted = shouldResolveProjectTrust
|
|
24
|
+
? false
|
|
25
|
+
: (parsed.projectTrustOverride ?? (fullAccessTrust || !hasTrustRequiringResources || savedProjectTrust === true));
|
|
26
|
+
const trustFromFullAccess = fullAccessTrust &&
|
|
27
|
+
hasTrustRequiringResources &&
|
|
28
|
+
parsed.projectTrustOverride === undefined &&
|
|
29
|
+
savedProjectTrust !== true;
|
|
30
|
+
const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted, trustFromFullAccess });
|
|
31
|
+
const reloadOptions = shouldResolveProjectTrust
|
|
32
|
+
? {
|
|
33
|
+
resolveProjectTrust: ({ extensionsResult }) => resolveProjectTrusted({
|
|
34
|
+
cwd,
|
|
35
|
+
trustStore,
|
|
36
|
+
trustOverride: parsed.projectTrustOverride,
|
|
37
|
+
defaultProjectTrust: startupSettingsManager.getDefaultProjectTrust(),
|
|
38
|
+
extensionsResult,
|
|
39
|
+
projectTrustContext: createProjectTrustContext({
|
|
40
|
+
cwd,
|
|
41
|
+
mode: appMode,
|
|
42
|
+
settingsManager: startupSettingsManager,
|
|
43
|
+
hasUI: false,
|
|
44
|
+
}),
|
|
45
|
+
onExtensionError: (message) => diagnostics.push({ type: "warning", message }),
|
|
46
|
+
}),
|
|
47
|
+
}
|
|
48
|
+
: undefined;
|
|
49
|
+
return { settingsManager, flagValues, reloadOptions };
|
|
50
|
+
}
|
|
51
|
+
export function resolveCliPaths(cwd, paths) {
|
|
52
|
+
return paths?.map((value) => (isLocalPath(value) ? resolvePath(value, cwd) : value));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The CLI's tool filters (`--tools`, `--no-tools`, `--no-builtin-tools`, `--exclude-tools`, settings `defaultTools`),
|
|
56
|
+
* mapped as `main.ts` maps them and resolved as `createAgentSession` resolves them (sdk.ts), so a loopless runtime
|
|
57
|
+
* registers exactly the tools a session registers for the same flags and settings.
|
|
58
|
+
*/
|
|
59
|
+
export function looplessToolFilters(parsed, settingsManager) {
|
|
60
|
+
const noTools = parsed.noTools ? "all" : parsed.noBuiltinTools ? "builtin" : undefined;
|
|
61
|
+
const defaultToolNames = parsed.tools === undefined && noTools === undefined ? settingsManager.getDefaultTools() : undefined;
|
|
62
|
+
const allowedToolNames = parsed.tools ?? (noTools === "all" ? [] : undefined);
|
|
63
|
+
return {
|
|
64
|
+
...(defaultToolNames === undefined ? {} : { defaultToolNames: new Set(defaultToolNames) }),
|
|
65
|
+
...(allowedToolNames === undefined ? {} : { allowedToolNames: new Set(allowedToolNames) }),
|
|
66
|
+
...(parsed.excludeTools === undefined ? {} : { excludedToolNames: new Set(parsed.excludeTools) }),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=loopless-runtime-settings.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
2
|
+
/**
|
|
3
|
+
* Runs one tool call the way the agent loop runs a model-issued one up to its hooks (`runToolCall`, agent-loop.ts):
|
|
4
|
+
* argument preparation and schema validation, then the tool. Invalid arguments and a failing tool come back as an
|
|
5
|
+
* `isError` result, never a rejection (senpi#5413).
|
|
6
|
+
*/
|
|
7
|
+
export declare function runLooplessToolCall(tool: AgentTool, call: {
|
|
8
|
+
readonly toolCallId: string;
|
|
9
|
+
readonly params: unknown;
|
|
10
|
+
readonly signal?: AbortSignal;
|
|
11
|
+
}): Promise<AgentToolResult<unknown>>;
|
|
12
|
+
//# sourceMappingURL=loopless-tool-call.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { validateToolArguments } from "@earendil-works/pi-ai";
|
|
2
|
+
function errorResult(message) {
|
|
3
|
+
return { content: [{ type: "text", text: message }], details: undefined, isError: true };
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Runs one tool call the way the agent loop runs a model-issued one up to its hooks (`runToolCall`, agent-loop.ts):
|
|
7
|
+
* argument preparation and schema validation, then the tool. Invalid arguments and a failing tool come back as an
|
|
8
|
+
* `isError` result, never a rejection (senpi#5413).
|
|
9
|
+
*/
|
|
10
|
+
export async function runLooplessToolCall(tool, call) {
|
|
11
|
+
const { toolCallId, signal } = call;
|
|
12
|
+
try {
|
|
13
|
+
const prepared = tool.prepareArguments ? tool.prepareArguments(call.params) : call.params;
|
|
14
|
+
const toolCall = { type: "toolCall", id: toolCallId, name: tool.name, arguments: prepared };
|
|
15
|
+
const args = validateToolArguments(tool, toolCall);
|
|
16
|
+
return await tool.execute(toolCallId, args, signal, undefined);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return errorResult(error instanceof Error ? error.message : String(error));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=loopless-tool-call.js.map
|
|
@@ -11,7 +11,7 @@ import type { ThinkingLevelSource } from "../../core/session-thinking-memory.ts"
|
|
|
11
11
|
import type { EffectStatus } from "./effect-record.ts";
|
|
12
12
|
import type { RpcSlashCommand } from "./rpc-command-surface.ts";
|
|
13
13
|
import type { RpcEvalCell, RpcEvalCellSnapshot } from "./rpc-result-types.ts";
|
|
14
|
-
import type { RpcAuthProvider, RpcLoadedExtension, RpcLoadedMcpServer, RpcProviderAccount, RpcSessionState } from "./rpc-session-state-types.ts";
|
|
14
|
+
import type { RpcAuthProvider, RpcAuthStoreError, RpcLoadedExtension, RpcLoadedMcpServer, RpcProviderAccount, RpcSessionState } from "./rpc-session-state-types.ts";
|
|
15
15
|
import type { RpcSnapshot } from "./snapshot-types.ts";
|
|
16
16
|
export type RpcReadResponse = {
|
|
17
17
|
id?: string;
|
|
@@ -215,6 +215,7 @@ export type RpcReadResponse = {
|
|
|
215
215
|
success: true;
|
|
216
216
|
data: {
|
|
217
217
|
providers: RpcAuthProvider[];
|
|
218
|
+
storeError?: RpcAuthStoreError;
|
|
218
219
|
};
|
|
219
220
|
} | {
|
|
220
221
|
id?: string;
|
|
@@ -27,6 +27,16 @@ export interface RpcAuthProvider {
|
|
|
27
27
|
/** Where the provider is defined, so a client can name the file or extension to edit (senpi#4328). */
|
|
28
28
|
origin?: ProviderOrigin;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* `get_auth_providers` `storeError`: auth.json exists but could not be loaded, so stored sign-ins read
|
|
32
|
+
* as unconfigured and every write is refused until it is repaired. `write_back_failed` is the exception:
|
|
33
|
+
* the file was read, so its sign-ins stay configured and writes are not refused (senpi#4413).
|
|
34
|
+
* Never a path or contents.
|
|
35
|
+
*/
|
|
36
|
+
export interface RpcAuthStoreError {
|
|
37
|
+
kind: "malformed_json" | "invalid_format" | "unreadable" | "write_back_failed";
|
|
38
|
+
reason: string;
|
|
39
|
+
}
|
|
30
40
|
/** Auth status mirror (no credential values), from getProviderAuthStatus. */
|
|
31
41
|
export interface RpcAuthStatus {
|
|
32
42
|
configured: boolean;
|
|
@@ -16,7 +16,7 @@ export type { EditAssistantMessageResult, EditUserMessageResult, NavigateTreeRes
|
|
|
16
16
|
export type { RpcWhenIdle } from "./rpc-session-command-types.ts";
|
|
17
17
|
export { STEER_WHEN_IDLE_CAPABILITY, USER_ACTIVITY_CAPABILITY } from "./rpc-session-command-types.ts";
|
|
18
18
|
export type { RpcAccountFailoverEvent, RpcAuthAccountsChangedEvent, RpcExtensionEvent, RpcHighReasoningWarningEvent, RpcLoadedSurfacesChangedEvent, RpcModelChangedEvent, RpcOpenQueuedEvent, RpcPendingInputUpdateEvent, RpcServiceTierChangedEvent, RpcSessionClosedEvent, RpcSessionClosedReason, RpcSessionOpenedEvent, RpcSessionParkedEvent, RpcSessionReplacedEvent, RpcSettingsSourceSelectedEvent, RpcSkillInvocationEvent, RpcThinkingLevelChangedEvent, } from "./rpc-session-event-types.ts";
|
|
19
|
-
export type { RpcAuthProvider, RpcAuthStatus, RpcLoadedExtension, RpcLoadedMcpServer, RpcMcpServerStatus, RpcProviderAccount, RpcSessionModelEntry, RpcSessionState, } from "./rpc-session-state-types.ts";
|
|
19
|
+
export type { RpcAuthProvider, RpcAuthStatus, RpcAuthStoreError, RpcLoadedExtension, RpcLoadedMcpServer, RpcMcpServerStatus, RpcProviderAccount, RpcSessionModelEntry, RpcSessionState, } from "./rpc-session-state-types.ts";
|
|
20
20
|
export type { SessionEffectStatusRecord as RpcEffectStatusEvent } from "./session-effect-status.ts";
|
|
21
21
|
export type { SessionStatusRecord as RpcSessionStatusEvent } from "./session-status.ts";
|
|
22
22
|
//# sourceMappingURL=rpc-types.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { AgentSessionLaunchProfile } from "../../core/agent-session-runtime.ts";
|
|
3
|
+
import { type AgentSessionRuntimeDiagnostic } from "../../core/agent-session-services.ts";
|
|
4
|
+
import type { InlineExtension } from "../../core/extensions/types.ts";
|
|
5
|
+
import { type ToolDefinitionEntry } from "../../core/runtime/bootstrap.ts";
|
|
6
|
+
import { SettingsManager } from "../../core/settings-manager.ts";
|
|
7
|
+
import type { CliRuntimeConfiguration } from "../../main.ts";
|
|
8
|
+
import { type LooplessExecuteGrant } from "./loopless-execute-grant.ts";
|
|
9
|
+
/**
|
|
10
|
+
* A session worker's runtime with the agent loop removed (brain/hand split, senpi#5413): the same
|
|
11
|
+
* built-in and extension tools a full session serves, executed one call at a time, with no agent loop,
|
|
12
|
+
* no model binding and no transcript. Its extension runtime is never bound to a loop, so an extension
|
|
13
|
+
* that tries to drive one (`pi.sendUserMessage`, `pi.setModel`, ...) is refused.
|
|
14
|
+
*/
|
|
15
|
+
export interface LooplessRuntime {
|
|
16
|
+
readonly cwd: string;
|
|
17
|
+
/** Every tool this runtime serves: built-ins first, then extension tools, a later name replacing an earlier one. */
|
|
18
|
+
readonly definitions: ReadonlyMap<string, ToolDefinitionEntry>;
|
|
19
|
+
/** What building the runtime reported: settings, extension load and `project_trust` handler diagnostics. */
|
|
20
|
+
readonly diagnostics: readonly AgentSessionRuntimeDiagnostic[];
|
|
21
|
+
/**
|
|
22
|
+
* Runs one tool call: argument preparation and schema validation as the loop does, then the tool. An unknown
|
|
23
|
+
* name is refused before anything runs; invalid arguments and a failing tool come back as an `isError` result.
|
|
24
|
+
*
|
|
25
|
+
* It does NOT run the extensions' `tool_call`/`tool_result` handlers (permission checks among them): where those
|
|
26
|
+
* run for a call that reaches a tool-execution-only process is decided by the remote tool executor that calls
|
|
27
|
+
* this runtime, including forwarding a two-sided extension's handlers to the process that owns the session.
|
|
28
|
+
* Until that executor exists, a runtime refuses every call unless it was built with a grant from the closed
|
|
29
|
+
* allowlist in `loopless-execute-grant.ts` (the in-process test harness only).
|
|
30
|
+
*/
|
|
31
|
+
execute(name: string, toolCallId: string, params: unknown, signal?: AbortSignal): Promise<AgentToolResult<unknown>>;
|
|
32
|
+
/** Marks its extension contexts stale, then closes the tool work and the tool child it still runs. */
|
|
33
|
+
dispose(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export interface LooplessRuntimeProfile {
|
|
36
|
+
cwd: string;
|
|
37
|
+
launchProfile?: Readonly<AgentSessionLaunchProfile>;
|
|
38
|
+
}
|
|
39
|
+
export type LooplessRuntimeFactory = (profile: LooplessRuntimeProfile) => Promise<LooplessRuntime>;
|
|
40
|
+
export declare class LooplessToolNotFoundError extends Error {
|
|
41
|
+
readonly toolName: string;
|
|
42
|
+
constructor(toolName: string);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Builds loopless runtimes from the same launch configuration as `createCliRuntimeFactory`: the same
|
|
46
|
+
* cwd-bound services (settings, project trust, model registry, extensions and their flags), then the tool
|
|
47
|
+
* registry through `bootstrapRuntime()` / `buildToolRegistry()` (senpi#5222) on a runner without a session
|
|
48
|
+
* (senpi#5223), so nothing is ever written to a session file. Tools are placed by the same tool-child runtime
|
|
49
|
+
* a session builds (senpi#5227), so `--tool-runtime child` runs child-placed work on this runtime's own child.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createLooplessRuntimeFactory(configuration: CliRuntimeConfiguration, local?: {
|
|
52
|
+
extensionFactories?: InlineExtension[];
|
|
53
|
+
startupSettingsManager?: SettingsManager;
|
|
54
|
+
/** Required for `execute`; see `loopless-execute-grant.ts`. */
|
|
55
|
+
executeGrant?: LooplessExecuteGrant;
|
|
56
|
+
}): LooplessRuntimeFactory;
|
|
57
|
+
//# sourceMappingURL=runtime-factory-loopless.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createAgentSessionServices } from "../../core/agent-session-services.js";
|
|
3
|
+
import { bootstrapRuntime, buildToolRegistry } from "../../core/runtime/bootstrap.js";
|
|
4
|
+
import { SettingsManager } from "../../core/settings-manager.js";
|
|
5
|
+
import { childModuleRoots } from "../../core/tool-dispatch/offload.js";
|
|
6
|
+
import { createToolChildRuntime } from "../../core/tool-dispatch/tool-child-runtime.js";
|
|
7
|
+
import { builtInExtensions } from "../../extensions/index.js";
|
|
8
|
+
import { looplessToolFilters, resolveCliPaths, resolveLooplessSettings } from "./loopless-runtime-settings.js";
|
|
9
|
+
import { isLooplessExecuteGranted, LooplessExecuteRefusedError, } from "./loopless-execute-grant.js";
|
|
10
|
+
import { runLooplessToolCall } from "./loopless-tool-call.js";
|
|
11
|
+
import { toolChildModuleRoots } from "./tool-child/session-child.js";
|
|
12
|
+
export class LooplessToolNotFoundError extends Error {
|
|
13
|
+
constructor(toolName) {
|
|
14
|
+
super(`loopless_tool_not_found: ${toolName}`);
|
|
15
|
+
this.toolName = toolName;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Builds loopless runtimes from the same launch configuration as `createCliRuntimeFactory`: the same
|
|
20
|
+
* cwd-bound services (settings, project trust, model registry, extensions and their flags), then the tool
|
|
21
|
+
* registry through `bootstrapRuntime()` / `buildToolRegistry()` (senpi#5222) on a runner without a session
|
|
22
|
+
* (senpi#5223), so nothing is ever written to a session file. Tools are placed by the same tool-child runtime
|
|
23
|
+
* a session builds (senpi#5227), so `--tool-runtime child` runs child-placed work on this runtime's own child.
|
|
24
|
+
*/
|
|
25
|
+
export function createLooplessRuntimeFactory(configuration, local = {}) {
|
|
26
|
+
const { parsed, agentDir, toolRuntime } = configuration;
|
|
27
|
+
const extensionFactories = local.extensionFactories ?? builtInExtensions;
|
|
28
|
+
const startupSettingsManager = local.startupSettingsManager ?? SettingsManager.create(configuration.cwd, configuration.agentDir);
|
|
29
|
+
return async (profile) => {
|
|
30
|
+
const { cwd } = profile;
|
|
31
|
+
const diagnostics = [];
|
|
32
|
+
const { settingsManager, flagValues, reloadOptions } = await resolveLooplessSettings(configuration, startupSettingsManager, profile, diagnostics);
|
|
33
|
+
const services = await createAgentSessionServices({
|
|
34
|
+
cwd,
|
|
35
|
+
agentDir,
|
|
36
|
+
settingsManager,
|
|
37
|
+
modelRuntimeSignal: AbortSignal.timeout(15_000),
|
|
38
|
+
extensionFlagValues: flagValues,
|
|
39
|
+
...(reloadOptions ? { resourceLoaderReloadOptions: reloadOptions } : {}),
|
|
40
|
+
resourceLoaderOptions: {
|
|
41
|
+
sessionKind: profile.launchProfile?.sessionKind,
|
|
42
|
+
sessionContext: profile.launchProfile?.sessionContext,
|
|
43
|
+
additionalExtensionPaths: resolveCliPaths(cwd, parsed.extensions),
|
|
44
|
+
additionalSkillPaths: resolveCliPaths(cwd, parsed.skills),
|
|
45
|
+
additionalPromptTemplatePaths: resolveCliPaths(cwd, parsed.promptTemplates),
|
|
46
|
+
additionalThemePaths: resolveCliPaths(cwd, parsed.themes),
|
|
47
|
+
noExtensions: parsed.noExtensions,
|
|
48
|
+
noSkills: parsed.noSkills,
|
|
49
|
+
noPromptTemplates: parsed.noPromptTemplates,
|
|
50
|
+
noThemes: parsed.noThemes,
|
|
51
|
+
noContextFiles: parsed.noContextFiles,
|
|
52
|
+
extensionFactories,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
const extensionsResult = services.resourceLoader.getExtensions();
|
|
56
|
+
diagnostics.push(...services.diagnostics, ...extensionsResult.errors.map(({ path, error }) => ({
|
|
57
|
+
type: "warning",
|
|
58
|
+
message: `Failed to load extension "${path}": ${error}`,
|
|
59
|
+
})), ...(extensionsResult.warnings ?? []).map(({ path, warning }) => ({
|
|
60
|
+
type: "warning",
|
|
61
|
+
message: `Extension package "${path}": ${warning}`,
|
|
62
|
+
})));
|
|
63
|
+
let execute = () => Promise.reject(new Error("Loopless runtime is not ready"));
|
|
64
|
+
let toolNames = () => [];
|
|
65
|
+
const toolChildRuntime = createToolChildRuntime({
|
|
66
|
+
toolRuntime: toolRuntime ?? "in-process",
|
|
67
|
+
agentDir,
|
|
68
|
+
scopeId: `loopless-${randomUUID()}`,
|
|
69
|
+
cwd: services.cwd,
|
|
70
|
+
moduleRoots: () => [
|
|
71
|
+
...toolChildModuleRoots(runtime.extensionRunner.getAllRegisteredTools()),
|
|
72
|
+
...childModuleRoots(runtime.extensionRunner.getAllChildModules()),
|
|
73
|
+
],
|
|
74
|
+
hostCallDeps: {
|
|
75
|
+
cwd: services.cwd,
|
|
76
|
+
executeNested: async (_parentToolCallId, name, args, signal) => {
|
|
77
|
+
const toolCall = { type: "toolCall", id: `nested-${randomUUID()}`, name, arguments: {} };
|
|
78
|
+
const result = await execute(name, toolCall.id, args, signal);
|
|
79
|
+
return { toolCall, result, isError: result.isError === true };
|
|
80
|
+
},
|
|
81
|
+
callableToolNames: () => toolNames(),
|
|
82
|
+
target: () => undefined,
|
|
83
|
+
filesystemPolicy: () => runtime.filesystemPolicy,
|
|
84
|
+
},
|
|
85
|
+
onRecord: () => { },
|
|
86
|
+
});
|
|
87
|
+
const { dispatcher, toolChild } = toolChildRuntime;
|
|
88
|
+
const runtime = bootstrapRuntime({
|
|
89
|
+
cwd: services.cwd,
|
|
90
|
+
settingsManager: services.settingsManager,
|
|
91
|
+
extensionsResult,
|
|
92
|
+
modelRegistry: services.modelRegistry,
|
|
93
|
+
toolDispatcher: dispatcher,
|
|
94
|
+
});
|
|
95
|
+
const registry = buildToolRegistry({
|
|
96
|
+
baseToolDefinitions: runtime.baseToolDefinitions,
|
|
97
|
+
runner: runtime.extensionRunner,
|
|
98
|
+
dispatcher,
|
|
99
|
+
...looplessToolFilters(parsed, services.settingsManager),
|
|
100
|
+
});
|
|
101
|
+
toolNames = () => [...registry.toolRegistry.keys()];
|
|
102
|
+
execute = (name, toolCallId, params, signal) => {
|
|
103
|
+
if (!isLooplessExecuteGranted(local.executeGrant))
|
|
104
|
+
return Promise.reject(new LooplessExecuteRefusedError("no executor runs the tool_call/tool_result handlers for this runtime yet"));
|
|
105
|
+
const tool = registry.toolRegistry.get(name);
|
|
106
|
+
if (tool === undefined)
|
|
107
|
+
return Promise.reject(new LooplessToolNotFoundError(name));
|
|
108
|
+
return runLooplessToolCall(tool, {
|
|
109
|
+
toolCallId,
|
|
110
|
+
params,
|
|
111
|
+
...(signal ? { signal } : {}),
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
cwd: services.cwd,
|
|
116
|
+
definitions: registry.definitions,
|
|
117
|
+
diagnostics,
|
|
118
|
+
execute: (name, toolCallId, params, signal) => execute(name, toolCallId, params, signal),
|
|
119
|
+
dispose: async () => {
|
|
120
|
+
// Stale first, as a session disposes: a close that fails must not leave the contexts usable.
|
|
121
|
+
runtime.extensionRunner.invalidate("This loopless runtime was disposed.");
|
|
122
|
+
// Both closes run to the end; the first failure is then reported to the caller.
|
|
123
|
+
const closed = await Promise.allSettled([dispatcher.dispose(), toolChild?.close()]);
|
|
124
|
+
const failure = closed.find((outcome) => outcome.status === "rejected");
|
|
125
|
+
if (failure !== undefined)
|
|
126
|
+
throw failure.reason;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=runtime-factory-loopless.js.map
|
package/docs/development.md
CHANGED
|
@@ -70,7 +70,7 @@ Never use `__dirname` directly for package assets.
|
|
|
70
70
|
```bash
|
|
71
71
|
bun run test # Vitest across workspaces (skips live-API; default test runner)
|
|
72
72
|
./pi-test.sh # Launch the CLI from source via tsx for manual testing (--no-env unsets API keys)
|
|
73
|
-
bun run check # oxlint +
|
|
73
|
+
bun run check # oxlint + bun fmt + lint ratchet + tsc + browser-smoke check (pre-commit equivalent)
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Live-API tests are env-gated vitest tests. Set `PI_ENABLE_LIVE_API_TESTS=1` (or a per-provider flag from `packages/ai/test/live-api-gates.ts`) plus the provider API keys, then run `bun run test`.
|
package/docs/rpc.md
CHANGED
|
@@ -6268,6 +6268,8 @@ A provider whose API-key login asks a region (Kimi Code: `"mainland-cn"` or `"gl
|
|
|
6268
6268
|
|
|
6269
6269
|
A provider holds logins of one kind. If it had an OAuth login, the key replaces it: `auth.json` is backed up first, and the response `data` is `{replaced: "oauth", backupPath?}`, where `backupPath` is absent when the credential store is not a file (a failed backup fails the login instead). A key added over another key has no `data`.
|
|
6270
6270
|
|
|
6271
|
+
When `auth.json` exists but cannot be loaded, `get_auth_providers` `data` also carries `storeError` `{kind, reason}`. `kind` is `malformed_json` (not valid JSON), `invalid_format` (the document is not a JSON object), `unreadable` (the file cannot be read, for example a permission error) or `write_back_failed` (the file was read, but saving its repaired or migrated copy failed; the sign-ins it read stay configured and writes are not refused for this kind). `reason` is a short message that never includes the file's path or contents. While `storeError` has any other kind, a stored sign-in reads as unconfigured because it could not be read, not because it is missing (a store that loaded earlier in the process keeps its last loaded sign-ins). Every write, such as `login_api_key`, a login, or `logout`, fails and leaves the file unchanged. A client should say the sign-in store could not be read and ask the user to repair or move `auth.json`. The next `get_auth_providers` after the file becomes readable no longer has `storeError`. A store that is only locked by another writer is not a `storeError`.
|
|
6272
|
+
|
|
6271
6273
|
`get_auth_providers` API-key rows carry `keyHint`, the last four characters of a literal key the app stored when it is at least 12 characters long. OAuth rows, `!command` and `$ENV` references, and short keys have none.
|
|
6272
6274
|
|
|
6273
6275
|
Every `get_auth_providers` row whose provider is defined carries `origin`, which says where the definition lives so a client can name what to edit when the app cannot remove the entry itself:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.11.3-
|
|
3
|
+
"version": "2026.11.3-2",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@earendil-works/chord": "0.99.1",
|
|
86
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.3-
|
|
87
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.3-
|
|
88
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.3-
|
|
86
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.3-2",
|
|
87
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.3-2",
|
|
88
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.3-2",
|
|
89
89
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
90
90
|
"ajv": "8.20.0",
|
|
91
91
|
"chalk": "6.0.0",
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
"yaml": "2.9.1",
|
|
107
107
|
"@anthropic-ai/claude-agent-sdk": "0.3.306",
|
|
108
108
|
"@anthropic-ai/sdk": "0.127.0",
|
|
109
|
-
"@code-yeongyu/senpi-codemode": "2026.11.3-
|
|
110
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.3-
|
|
109
|
+
"@code-yeongyu/senpi-codemode": "2026.11.3-2",
|
|
110
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.3-2",
|
|
111
111
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
112
112
|
"@mozilla/readability": "0.6.0",
|
|
113
113
|
"linkedom": "0.18.13",
|