@code-yeongyu/senpi 2026.10.16-2 → 2026.10.16-4
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 +48 -0
- package/dist/bundle/chunks/{chunk-QQUHMCOM.js → chunk-2NO5ZOYE.js} +6 -6
- package/dist/bundle/chunks/chunk-74YFRVGQ.js +2 -0
- package/dist/bundle/chunks/{chunk-4R4PRTF5.js → chunk-CJRE5LLR.js} +1 -1
- package/dist/bundle/chunks/{chunk-Q7RD3NOC.js → chunk-GY5O7NIQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-KRAABTZY.js → chunk-JD4CV3GH.js} +2 -2
- package/dist/bundle/chunks/chunk-K2EVQGGT.js +31 -0
- package/dist/bundle/chunks/{chunk-SSEGIXRL.js → chunk-KUQ3K33L.js} +1 -1
- package/dist/bundle/chunks/{chunk-V5JFWWGJ.js → chunk-KWC3MCLP.js} +1 -1
- package/dist/bundle/chunks/chunk-MLL7AGVM.js +3 -0
- package/dist/bundle/chunks/chunk-PAJKEG2L.js +8 -0
- package/dist/bundle/chunks/{chunk-DPRV3FR7.js → chunk-PWHAOWNH.js} +1 -1
- package/dist/bundle/chunks/chunk-WL4Y6XN6.js +18 -0
- package/dist/bundle/chunks/{chunk-VY43SSYL.js → chunk-XCRGEMY2.js} +144 -134
- package/dist/bundle/chunks/{cli-main-JDL52ADK.js → cli-main-I6JF2GPS.js} +1 -1
- package/dist/bundle/chunks/google-generative-ai-AI6PAUPA.js +2 -0
- package/dist/bundle/chunks/google-vertex-USNRIFF7.js +2 -0
- package/dist/bundle/chunks/google-vertex-adc-JZURL4XM.js +2 -0
- package/dist/bundle/chunks/{help-fast-path-SVCMHCYG.js → help-fast-path-APSHUDY6.js} +1 -1
- package/dist/bundle/chunks/{host-session-command-46PTZGIB.js → host-session-command-BUJUJHDA.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-7IM7YJYH.js → interactive-mode-4Q3TIXOZ.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-3MEEUMYU.js → multi-session-host-XEZB2XVR.js} +6 -5
- package/dist/bundle/chunks/{openai-codex-responses-T26WW2RE.js → openai-codex-responses-NV27Q6VV.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-UCHXVBQ2.js → package-manager-cli-YWZHOZOY.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-QX7YSKDU.js → rotation-stream-HY3JPQCX.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-D4BJKWFL.js → rpc-mode-SLWXVXXE.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-2RHSWBQO.js → schedule-command-JRWZGEDU.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-NBSRSAJS.js → session-control-endpoint-E2ZSEWXW.js} +1 -1
- package/dist/bundle/chunks/session-picker-5IZY7E3G.js +2 -0
- package/dist/bundle/chunks/session-worker.js +266 -255
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session-runtime.d.ts +15 -0
- package/dist/core/agent-session-runtime.js +53 -2
- package/dist/core/agent-session.d.ts +28 -0
- package/dist/core/agent-session.js +88 -3
- package/dist/core/context-files.d.ts +21 -0
- package/dist/core/context-files.js +175 -0
- package/dist/core/diagnostics.d.ts +1 -0
- package/dist/core/dynamic-prompt/intent-gate.d.ts +3 -0
- package/dist/core/dynamic-prompt/intent-gate.js +4 -3
- package/dist/core/dynamic-prompt/policies.d.ts +4 -1
- package/dist/core/dynamic-prompt/policies.js +3 -2
- package/dist/core/dynamic-prompt/working-task.d.ts +4 -1
- package/dist/core/dynamic-prompt/working-task.js +3 -2
- package/dist/core/dynamic-prompt/workstation.d.ts +4 -3
- package/dist/core/dynamic-prompt/workstation.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/account-command.js +26 -3
- package/dist/core/extensions/builtin/anthropic-subscription/account-management.d.ts +5 -0
- package/dist/core/extensions/builtin/anthropic-subscription/account-management.js +40 -0
- package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/accounts.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/affinity.js +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +10 -5
- package/dist/core/extensions/builtin/anthropic-subscription/failover.js +158 -17
- package/dist/core/extensions/builtin/anthropic-subscription/usage-limit-block.d.ts +13 -0
- package/dist/core/extensions/builtin/anthropic-subscription/usage-limit-block.js +89 -0
- package/dist/core/extensions/builtin/goal/index.js +18 -8
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +7 -0
- package/dist/core/extensions/builtin/goal/reload-reengagement.js +5 -0
- package/dist/core/extensions/builtin/index.js +3 -0
- package/dist/core/extensions/builtin/permission-system/always-scope.d.ts +34 -0
- package/dist/core/extensions/builtin/permission-system/always-scope.js +77 -0
- package/dist/core/extensions/builtin/permission-system/index.js +3 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +24 -3
- package/dist/core/extensions/builtin/permission-system/prompt.js +5 -1
- package/dist/core/extensions/builtin/permission-system/service.js +1 -1
- package/dist/core/extensions/builtin/permission-system/types.d.ts +7 -0
- package/dist/core/extensions/builtin/prompt-preset/contract.js +6 -1
- package/dist/core/extensions/builtin/prompt-preset/gemini-4-argon.d.ts +7 -0
- package/dist/core/extensions/builtin/prompt-preset/gemini-4-argon.js +83 -0
- package/dist/core/extensions/builtin/prompt-preset/matchers.d.ts +2 -0
- package/dist/core/extensions/builtin/prompt-preset/matchers.js +12 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js +3 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
- package/dist/core/extensions/builtin/vertex-model-garden/auth.d.ts +16 -0
- package/dist/core/extensions/builtin/vertex-model-garden/auth.js +19 -0
- package/dist/core/extensions/builtin/vertex-model-garden/index.d.ts +6 -0
- package/dist/core/extensions/builtin/vertex-model-garden/index.js +27 -0
- package/dist/core/extensions/builtin/vertex-model-garden/models.d.ts +3 -0
- package/dist/core/extensions/builtin/vertex-model-garden/models.js +34 -0
- package/dist/core/extensions/builtin/vertex-model-garden/transport.d.ts +14 -0
- package/dist/core/extensions/builtin/vertex-model-garden/transport.js +35 -0
- package/dist/core/extensions/types.d.ts +32 -1
- package/dist/core/package-manager.js +4 -32
- package/dist/core/project-trust.d.ts +8 -0
- package/dist/core/project-trust.js +12 -0
- package/dist/core/provider-api-key-auth.d.ts +9 -1
- package/dist/core/provider-api-key-auth.js +34 -0
- package/dist/core/provider-composer.d.ts +23 -0
- package/dist/core/resource-loader.d.ts +5 -7
- package/dist/core/resource-loader.js +11 -84
- package/dist/core/retry-fallback/circuit-probes.d.ts +1 -0
- package/dist/core/retry-fallback/circuit-probes.js +3 -0
- package/dist/core/retry-fallback/circuit.d.ts +5 -0
- package/dist/core/retry-fallback/circuit.js +27 -4
- package/dist/core/retry-fallback/controller.d.ts +9 -4
- package/dist/core/retry-fallback/controller.js +21 -4
- package/dist/core/settings-manager.d.ts +19 -0
- package/dist/core/settings-manager.js +27 -1
- package/dist/core/settings-shapes.d.ts +4 -0
- package/dist/core/skills-prompt.d.ts +18 -0
- package/dist/core/skills-prompt.js +131 -0
- package/dist/core/skills.d.ts +1 -9
- package/dist/core/skills.js +1 -59
- package/dist/core/trust-manager.d.ts +13 -4
- package/dist/core/trust-manager.js +52 -21
- package/dist/core/turn-deadline.d.ts +92 -0
- package/dist/core/turn-deadline.js +179 -0
- package/dist/main.js +23 -4
- package/dist/modes/interactive/interactive-mode.js +10 -3
- package/dist/modes/rpc/abort-grace.d.ts +9 -0
- package/dist/modes/rpc/abort-grace.js +11 -0
- package/dist/modes/rpc/client-admission-record.d.ts +1 -0
- package/dist/modes/rpc/client-admissions.d.ts +6 -3
- package/dist/modes/rpc/client-admissions.js +9 -4
- package/dist/modes/rpc/connection-handler.js +8 -3
- package/dist/modes/rpc/effect-commands.d.ts +1 -3
- package/dist/modes/rpc/effect-commands.js +4 -5
- package/dist/modes/rpc/effect-outcome-copy.d.ts +4 -4
- package/dist/modes/rpc/effect-outcome-copy.js +4 -4
- package/dist/modes/rpc/effect-record.d.ts +2 -0
- package/dist/modes/rpc/effect-record.js +2 -0
- package/dist/modes/rpc/effect-status.js +1 -1
- package/dist/modes/rpc/host-handover-parked.d.ts +6 -0
- package/dist/modes/rpc/host-handover-parked.js +1 -0
- package/dist/modes/rpc/host-handover-reopen.js +11 -2
- package/dist/modes/rpc/rpc-client.d.ts +28 -2
- package/dist/modes/rpc/rpc-client.js +24 -11
- package/dist/modes/rpc/rpc-request-deadline.d.ts +12 -0
- package/dist/modes/rpc/rpc-request-deadline.js +15 -0
- package/dist/modes/rpc/session-command-router.js +7 -4
- package/dist/modes/rpc/session-registry-attach.js +6 -1
- package/dist/modes/rpc/session-registry-claims.d.ts +22 -2
- package/dist/modes/rpc/session-registry-claims.js +68 -0
- package/dist/modes/rpc/session-registry-opening.d.ts +23 -0
- package/dist/modes/rpc/session-registry-opening.js +57 -0
- package/dist/modes/rpc/session-registry.d.ts +1 -0
- package/dist/modes/rpc/session-registry.js +48 -60
- package/dist/modes/rpc/session-unload.js +4 -1
- package/docs/rpc.md +10 -3
- package/docs/settings.md +16 -1
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-6MEETY7N.js +0 -8
- package/dist/bundle/chunks/chunk-7ASXJ4AD.js +0 -47
- package/dist/bundle/chunks/chunk-TEGEPQIA.js +0 -3
- package/dist/bundle/chunks/google-generative-ai-YNIOL5FT.js +0 -2
- package/dist/bundle/chunks/google-vertex-CF6AYVXL.js +0 -2
- package/dist/bundle/chunks/session-picker-6TSBGWOA.js +0 -2
|
@@ -122,6 +122,7 @@ import { composeFilesystemPolicies } from "./tools/filesystem-policy.js";
|
|
|
122
122
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
123
123
|
import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
|
|
124
124
|
import { TranscriptWriteFailures } from "./transcript-write-failures.js";
|
|
125
|
+
import { AgentDeadlineTracker, resolveTurnDeadlineSettings, TURN_DEADLINE_ENTRY_TYPE, } from "./turn-deadline.js";
|
|
125
126
|
import { commandShapedName, findUnknownCommand } from "./unknown-command.js";
|
|
126
127
|
import { addUsageToTotals, combineUsage, createUsageTotals } from "./usage-totals.js";
|
|
127
128
|
import { keepsTextBlocksVerbatim, userTextContent } from "./user-text-blocks.js";
|
|
@@ -324,6 +325,8 @@ export class AgentSession {
|
|
|
324
325
|
this._continueWithQueuedInput = false;
|
|
325
326
|
/** True while a reload's `session_start` handlers run; a nested reload would retire the runner they run on. */
|
|
326
327
|
this._sessionStartDispatching = false;
|
|
328
|
+
/** Waiters for the moment no prompt start is pending and no session start is dispatching (applyProjectTrustChange). */
|
|
329
|
+
this._admissionIdleWaiters = [];
|
|
327
330
|
this._engineTurnStarts = [];
|
|
328
331
|
this._engineTurnPauseRecorded = false;
|
|
329
332
|
this._nextInputId = 0;
|
|
@@ -659,6 +662,10 @@ export class AgentSession {
|
|
|
659
662
|
activeToolNames: this._initialActiveToolNames,
|
|
660
663
|
includeAllExtensionTools: true,
|
|
661
664
|
});
|
|
665
|
+
this._deadlineTracker = new AgentDeadlineTracker(this, () => resolveTurnDeadlineSettings({
|
|
666
|
+
turn_inactivity_ms: this.settingsManager.getTurnInactivityMs(),
|
|
667
|
+
turn_max_runtime_ms: this.settingsManager.getTurnMaxRuntimeMs(),
|
|
668
|
+
}));
|
|
662
669
|
// Last, so a construction that throws never leaves a hold only dispose() could release.
|
|
663
670
|
this._fallbackCircuitsLease = acquireFallbackCircuits(this._agentDir);
|
|
664
671
|
}
|
|
@@ -1788,6 +1795,7 @@ export class AgentSession {
|
|
|
1788
1795
|
}
|
|
1789
1796
|
}
|
|
1790
1797
|
this.releaseSettledSessionMemory();
|
|
1798
|
+
this._deadlineTracker.disarm();
|
|
1791
1799
|
this._emit({ type: "agent_idle" });
|
|
1792
1800
|
}
|
|
1793
1801
|
async _promptAgent(messages, deferredTurnClaim, onQueuedInput) {
|
|
@@ -1795,6 +1803,8 @@ export class AgentSession {
|
|
|
1795
1803
|
if (!this._isAgentRunActive)
|
|
1796
1804
|
this.externalAdmission.beginTurn();
|
|
1797
1805
|
this._isAgentRunActive = true;
|
|
1806
|
+
// A new turn admitted: arm the inactivity lease and the hard max-runtime cap.
|
|
1807
|
+
this._deadlineTracker.arm();
|
|
1798
1808
|
// A new prompt replaces any retry that compaction before it scheduled.
|
|
1799
1809
|
this._failedResponse = undefined;
|
|
1800
1810
|
this._recordSelection();
|
|
@@ -2188,6 +2198,9 @@ export class AgentSession {
|
|
|
2188
2198
|
? withProviderFailure({
|
|
2189
2199
|
...event,
|
|
2190
2200
|
willRetry: agentEndWillRetry,
|
|
2201
|
+
...(this._deadlineTracker.current !== undefined
|
|
2202
|
+
? { aborted: true, abortSource: "system", deadline: this._deadlineTracker.current }
|
|
2203
|
+
: {}),
|
|
2191
2204
|
...(abortCause === undefined ? {} : { abortCause }),
|
|
2192
2205
|
})
|
|
2193
2206
|
: event.type === "message_update"
|
|
@@ -2245,7 +2258,7 @@ export class AgentSession {
|
|
|
2245
2258
|
assistantMsg.stopReason !== "aborted" &&
|
|
2246
2259
|
!isClassifierRefusal(assistantMsg);
|
|
2247
2260
|
if (succeeded)
|
|
2248
|
-
this._retryFallback.
|
|
2261
|
+
this._retryFallback.noteServed(`${assistantMsg.provider}/${assistantMsg.model}`);
|
|
2249
2262
|
if (succeeded && assistantMsg.stopReason !== "length") {
|
|
2250
2263
|
this._overflowRecoveryRungs = 0;
|
|
2251
2264
|
}
|
|
@@ -2755,6 +2768,7 @@ export class AgentSession {
|
|
|
2755
2768
|
}
|
|
2756
2769
|
}
|
|
2757
2770
|
else if (event.type === "tool_execution_start") {
|
|
2771
|
+
this._deadlineTracker.renew();
|
|
2758
2772
|
const extensionEvent = {
|
|
2759
2773
|
type: "tool_execution_start",
|
|
2760
2774
|
toolCallId: event.toolCallId,
|
|
@@ -2764,6 +2778,7 @@ export class AgentSession {
|
|
|
2764
2778
|
await this._extensionRunner.emit(extensionEvent);
|
|
2765
2779
|
}
|
|
2766
2780
|
else if (event.type === "tool_execution_update") {
|
|
2781
|
+
this._deadlineTracker.renew();
|
|
2767
2782
|
const extensionEvent = {
|
|
2768
2783
|
type: "tool_execution_update",
|
|
2769
2784
|
toolCallId: event.toolCallId,
|
|
@@ -2774,6 +2789,7 @@ export class AgentSession {
|
|
|
2774
2789
|
await this._extensionRunner.emit(extensionEvent);
|
|
2775
2790
|
}
|
|
2776
2791
|
else if (event.type === "tool_execution_end") {
|
|
2792
|
+
this._deadlineTracker.renew();
|
|
2777
2793
|
const extensionEvent = {
|
|
2778
2794
|
type: "tool_execution_end",
|
|
2779
2795
|
toolCallId: event.toolCallId,
|
|
@@ -2838,6 +2854,7 @@ export class AgentSession {
|
|
|
2838
2854
|
*/
|
|
2839
2855
|
dispose() {
|
|
2840
2856
|
this._disposed = true;
|
|
2857
|
+
this._deadlineTracker.disarm();
|
|
2841
2858
|
for (const dispose of this._toolContextDisposers)
|
|
2842
2859
|
dispose();
|
|
2843
2860
|
try {
|
|
@@ -3008,6 +3025,10 @@ export class AgentSession {
|
|
|
3008
3025
|
get isStreaming() {
|
|
3009
3026
|
return this._isAgentRunActive;
|
|
3010
3027
|
}
|
|
3028
|
+
/** True while a provider retry is in flight; the deadline lease treats it as turn progress. */
|
|
3029
|
+
get isRetryInFlight() {
|
|
3030
|
+
return this._retryPromise !== undefined;
|
|
3031
|
+
}
|
|
3011
3032
|
/** Whether the session has no active agent run, retry, or queued continuation. */
|
|
3012
3033
|
get isIdle() {
|
|
3013
3034
|
return !this._isAgentRunActive;
|
|
@@ -3043,6 +3064,27 @@ export class AgentSession {
|
|
|
3043
3064
|
get currentAbortSource() {
|
|
3044
3065
|
return this._abortProvenance.currentSource;
|
|
3045
3066
|
}
|
|
3067
|
+
/** The deadline that stopped the current turn, when the lease fired (senpi#3227). */
|
|
3068
|
+
get currentDeadline() {
|
|
3069
|
+
return this._deadlineTracker.current;
|
|
3070
|
+
}
|
|
3071
|
+
/** Adapter for the deadline owner interface: a deadline stop that could not complete. */
|
|
3072
|
+
_warnDeadline(event, detail) {
|
|
3073
|
+
this._sessionLogger.warn(event, { detail });
|
|
3074
|
+
}
|
|
3075
|
+
/** Adapter for the deadline owner interface: end the turn through the normal system abort. */
|
|
3076
|
+
async abortTurnForDeadline() {
|
|
3077
|
+
await this._abortActiveAgentAndRetry("system");
|
|
3078
|
+
}
|
|
3079
|
+
/** Record a deadline stop durably so the goal engine holds off until admitted user input. */
|
|
3080
|
+
_recordDeadlineStop(deadline) {
|
|
3081
|
+
this._emitEntryAppended(this.sessionManager.appendCustomEntry(TURN_DEADLINE_ENTRY_TYPE, {
|
|
3082
|
+
kind: deadline.kind,
|
|
3083
|
+
limitMs: deadline.limitMs,
|
|
3084
|
+
elapsedMs: deadline.elapsedMs,
|
|
3085
|
+
at: Date.now(),
|
|
3086
|
+
}));
|
|
3087
|
+
}
|
|
3046
3088
|
/**
|
|
3047
3089
|
* Get the names of currently active tools.
|
|
3048
3090
|
* Returns the names of tools currently set on the agent. Tools with `search` or `eval` exposure
|
|
@@ -3557,8 +3599,10 @@ export class AgentSession {
|
|
|
3557
3599
|
submittedByCommand: options?.source === "extension",
|
|
3558
3600
|
});
|
|
3559
3601
|
try {
|
|
3560
|
-
if (options?.source !== "extension")
|
|
3602
|
+
if (options?.source !== "extension") {
|
|
3561
3603
|
emitUserActivity(this, "prompt");
|
|
3604
|
+
this._deadlineTracker.clearHold();
|
|
3605
|
+
}
|
|
3562
3606
|
await this._prompt(text, {
|
|
3563
3607
|
...options,
|
|
3564
3608
|
promptDisposition: (disposition) => {
|
|
@@ -3643,6 +3687,7 @@ export class AgentSession {
|
|
|
3643
3687
|
return;
|
|
3644
3688
|
claim.started = started;
|
|
3645
3689
|
this._promptStartPending = false;
|
|
3690
|
+
this._notifyAdmissionIdle();
|
|
3646
3691
|
if (!started && claim.joined === true && !this.isStreaming)
|
|
3647
3692
|
this._drainJoinedInput();
|
|
3648
3693
|
};
|
|
@@ -4209,8 +4254,10 @@ export class AgentSession {
|
|
|
4209
4254
|
* `input` handlers, skill/template expansion, then the recovery-ordered enqueue.
|
|
4210
4255
|
*/
|
|
4211
4256
|
async _queueUserInput(text, images, behavior, options) {
|
|
4212
|
-
if (options?.source !== "extension")
|
|
4257
|
+
if (options?.source !== "extension") {
|
|
4213
4258
|
emitUserActivity(this, "prompt");
|
|
4259
|
+
this._deadlineTracker.clearHold();
|
|
4260
|
+
}
|
|
4214
4261
|
// Check for extension commands (cannot be queued)
|
|
4215
4262
|
if (text.startsWith("/")) {
|
|
4216
4263
|
this._throwIfExtensionCommand(text);
|
|
@@ -8058,6 +8105,34 @@ export class AgentSession {
|
|
|
8058
8105
|
finishReloadWork();
|
|
8059
8106
|
}
|
|
8060
8107
|
}
|
|
8108
|
+
/**
|
|
8109
|
+
* Loads or unloads the project's code after the session's project trust changed (Full access left or
|
|
8110
|
+
* re-entered, senpi#3420): rebuilds the runtime exactly like reload(), but nothing can cancel it. No
|
|
8111
|
+
* `session_before_reload` handler runs (a project extension must not keep itself loaded); a prompt
|
|
8112
|
+
* that is being admitted, or a session start in dispatch, is waited out instead of cancelling, and
|
|
8113
|
+
* prompts admitted after that wait for the rebuilt runtime. A reload already in flight sees the trust
|
|
8114
|
+
* epoch move and loads again before it installs anything.
|
|
8115
|
+
*/
|
|
8116
|
+
async applyProjectTrustChange() {
|
|
8117
|
+
while (this._promptStartPending || this._sessionStartDispatching) {
|
|
8118
|
+
await new Promise((resolve) => this._admissionIdleWaiters.push(resolve));
|
|
8119
|
+
}
|
|
8120
|
+
const finishReloadWork = this._sessionWorkBarrier.begin();
|
|
8121
|
+
try {
|
|
8122
|
+
await this._rebuildRuntimeForReload();
|
|
8123
|
+
}
|
|
8124
|
+
finally {
|
|
8125
|
+
finishReloadWork();
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
_notifyAdmissionIdle() {
|
|
8129
|
+
if (this._promptStartPending || this._sessionStartDispatching)
|
|
8130
|
+
return;
|
|
8131
|
+
const waiters = this._admissionIdleWaiters;
|
|
8132
|
+
this._admissionIdleWaiters = [];
|
|
8133
|
+
for (const resolve of waiters)
|
|
8134
|
+
resolve();
|
|
8135
|
+
}
|
|
8061
8136
|
async _rebuildRuntimeForReload(options) {
|
|
8062
8137
|
resetTimings("reload");
|
|
8063
8138
|
const oldExtensionRunner = this._extensionRunner;
|
|
@@ -8076,6 +8151,7 @@ export class AgentSession {
|
|
|
8076
8151
|
});
|
|
8077
8152
|
time("shutdown", "reload");
|
|
8078
8153
|
const previousDefaultTools = new Set(this._usesDefaultTools ? (this.settingsManager.getDefaultTools() ?? DEFAULT_TOOL_NAMES) : []);
|
|
8154
|
+
let trustEpoch = this.settingsManager.getTrustEpoch();
|
|
8079
8155
|
await this.settingsManager.reload();
|
|
8080
8156
|
this._delegatedCompactionKey = undefined;
|
|
8081
8157
|
// Activate tools newly added to defaultTools. Removed ones stay active, and tools disabled
|
|
@@ -8114,6 +8190,14 @@ export class AgentSession {
|
|
|
8114
8190
|
await this._resourceLoader.reload({
|
|
8115
8191
|
settingsAlreadyReloadedFor: this.settingsManager,
|
|
8116
8192
|
});
|
|
8193
|
+
// The project trust changed while this rebuild ran (senpi#3420): what it loaded matches the old
|
|
8194
|
+
// trust, so it loads again under the current one. Nothing is installed until a load matches the
|
|
8195
|
+
// trust at install time; the check and the install below run in one synchronous step.
|
|
8196
|
+
while (this.settingsManager.getTrustEpoch() !== trustEpoch) {
|
|
8197
|
+
trustEpoch = this.settingsManager.getTrustEpoch();
|
|
8198
|
+
await this.settingsManager.reload();
|
|
8199
|
+
await this._resourceLoader.reload({ settingsAlreadyReloadedFor: this.settingsManager });
|
|
8200
|
+
}
|
|
8117
8201
|
time("resources", "reload");
|
|
8118
8202
|
try {
|
|
8119
8203
|
this._buildRuntime({
|
|
@@ -8167,6 +8251,7 @@ export class AgentSession {
|
|
|
8167
8251
|
finally {
|
|
8168
8252
|
this._sessionStartDispatching = false;
|
|
8169
8253
|
settleSessionStart();
|
|
8254
|
+
this._notifyAdmissionIdle();
|
|
8170
8255
|
}
|
|
8171
8256
|
}
|
|
8172
8257
|
time("lifecycle", "reload");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ResourceDiagnostic } from "./diagnostics.ts";
|
|
2
|
+
export interface ContextFile {
|
|
3
|
+
path: string;
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
6
|
+
/** Preserve the public array API while returning diagnostics to the owning mode. */
|
|
7
|
+
export type ContextFiles = ContextFile[] & {
|
|
8
|
+
readonly diagnostics: ResourceDiagnostic[];
|
|
9
|
+
};
|
|
10
|
+
/** Size limits for AGENTS.md / CLAUDE.md context files, which ride along on every model request. */
|
|
11
|
+
export interface ContextFileLimits {
|
|
12
|
+
maxBytesPerFile?: number;
|
|
13
|
+
maxTotalBytes?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_CONTEXT_FILE_MAX_BYTES: number;
|
|
16
|
+
export declare const DEFAULT_CONTEXT_FILES_MAX_TOTAL_BYTES: number;
|
|
17
|
+
export declare function loadProjectContextFiles(options: {
|
|
18
|
+
cwd: string;
|
|
19
|
+
agentDir: string;
|
|
20
|
+
} & ContextFileLimits): ContextFiles;
|
|
21
|
+
//# sourceMappingURL=context-files.d.ts.map
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, sep } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
5
|
+
import { stripBom } from "../utils/text.js";
|
|
6
|
+
import { findGitPaths } from "./footer-data-provider.js";
|
|
7
|
+
export const DEFAULT_CONTEXT_FILE_MAX_BYTES = 64 * 1024;
|
|
8
|
+
export const DEFAULT_CONTEXT_FILES_MAX_TOTAL_BYTES = 128 * 1024;
|
|
9
|
+
const MIN_USEFUL_BYTES = 1024;
|
|
10
|
+
function loadContextFileFromDir(dir) {
|
|
11
|
+
const candidates = ["AGENTS.override.md", "AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD"];
|
|
12
|
+
for (const filename of candidates) {
|
|
13
|
+
const filePath = join(dir, filename);
|
|
14
|
+
if (existsSync(filePath)) {
|
|
15
|
+
try {
|
|
16
|
+
if (!statSync(filePath).isFile()) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
path: filePath,
|
|
21
|
+
content: stripBom(readFileSync(filePath, "utf-8")),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The main repo's context file that a nested linked worktree's own copy shadows: both
|
|
33
|
+
* occupy the same logical repository scope, so loading both applies that context twice. Returns
|
|
34
|
+
* undefined when nothing is shadowed, leaving normal ancestor inheritance alone.
|
|
35
|
+
*
|
|
36
|
+
* Returned canonicalized (realpath), because `git worktree add` writes the `.git`
|
|
37
|
+
* file's `gitdir:` target in realpath form while cwd may still be symlinked
|
|
38
|
+
* (macOS `/tmp` -> `/private/tmp`).
|
|
39
|
+
*/
|
|
40
|
+
function findShadowedContextFile(cwd) {
|
|
41
|
+
const gitPaths = findGitPaths(cwd);
|
|
42
|
+
if (!gitPaths)
|
|
43
|
+
return undefined;
|
|
44
|
+
const commonGitDir = canonicalizePath(gitPaths.commonGitDir);
|
|
45
|
+
const worktreeRoot = canonicalizePath(gitPaths.repoDir);
|
|
46
|
+
const mainRepoRoot = dirname(commonGitDir);
|
|
47
|
+
// False for an ordinary repo, where the two are the same dir, and for a sibling
|
|
48
|
+
// worktree (`git worktree add ../feat`), whose main repo is not an ancestor.
|
|
49
|
+
if (!worktreeRoot.startsWith(`${mainRepoRoot}${sep}`))
|
|
50
|
+
return undefined;
|
|
51
|
+
// dirname of the common git dir is the main worktree root only when that dir is
|
|
52
|
+
// itself checked out from the same repo. In a bare layout (`proj/.bare` +
|
|
53
|
+
// `proj/main`) it is just the directory holding `.bare`, which tracks nothing; a
|
|
54
|
+
// submodule's gitdir has no `commondir`, so it lands under `.git/modules`.
|
|
55
|
+
if (canonicalizePath(join(mainRepoRoot, ".git")) !== commonGitDir)
|
|
56
|
+
return undefined;
|
|
57
|
+
const worktreeContextFile = loadContextFileFromDir(worktreeRoot);
|
|
58
|
+
return worktreeContextFile ? join(mainRepoRoot, basename(worktreeContextFile.path)) : undefined;
|
|
59
|
+
}
|
|
60
|
+
function kib(bytes) {
|
|
61
|
+
const value = bytes / 1024;
|
|
62
|
+
return Number.isInteger(value) ? `${value} KiB` : `${value.toFixed(1)} KiB`;
|
|
63
|
+
}
|
|
64
|
+
function utf8Prefix(content, maxBytes) {
|
|
65
|
+
if (maxBytes <= 0)
|
|
66
|
+
return "";
|
|
67
|
+
const bytes = Buffer.from(content, "utf8");
|
|
68
|
+
let end = Math.min(maxBytes, bytes.length);
|
|
69
|
+
while (end > 0 && end < bytes.length && ((bytes[end] ?? 0) & 0xc0) === 0x80)
|
|
70
|
+
end--;
|
|
71
|
+
const newline = bytes.lastIndexOf(10, end - 1);
|
|
72
|
+
if (newline >= maxBytes * 0.75)
|
|
73
|
+
end = newline + 1;
|
|
74
|
+
return bytes.subarray(0, end).toString("utf8");
|
|
75
|
+
}
|
|
76
|
+
function positiveLimit(value, fallback) {
|
|
77
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Bound what the context files add to every request. Files are given budget nearest-first
|
|
81
|
+
* (the cwd's file before its ancestors, the global file last), so the most specific
|
|
82
|
+
* instructions survive; every cut or skipped file is named in a warning.
|
|
83
|
+
*/
|
|
84
|
+
function applyContextFileLimits(files, limits) {
|
|
85
|
+
const perFile = positiveLimit(limits.maxBytesPerFile, DEFAULT_CONTEXT_FILE_MAX_BYTES);
|
|
86
|
+
const total = positiveLimit(limits.maxTotalBytes, DEFAULT_CONTEXT_FILES_MAX_TOTAL_BYTES);
|
|
87
|
+
const limitText = `limit ${kib(perFile)} per file, ${kib(total)} total; raise contextFiles.maxBytesPerFile / contextFiles.maxTotalBytes in settings to load more`;
|
|
88
|
+
const kept = [];
|
|
89
|
+
const diagnostics = [];
|
|
90
|
+
let remaining = total;
|
|
91
|
+
for (const file of [...files].reverse()) {
|
|
92
|
+
const size = Buffer.byteLength(file.content, "utf8");
|
|
93
|
+
const allowed = Math.min(perFile, remaining);
|
|
94
|
+
if (size <= allowed) {
|
|
95
|
+
kept.unshift(file);
|
|
96
|
+
remaining -= size;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (allowed < MIN_USEFUL_BYTES) {
|
|
100
|
+
const reason = perFile < MIN_USEFUL_BYTES ? "the per-file limit is too small" : "nearer context files used the budget";
|
|
101
|
+
diagnostics.push({
|
|
102
|
+
type: "warning",
|
|
103
|
+
category: "size-limit",
|
|
104
|
+
path: file.path,
|
|
105
|
+
message: `Context file (${kib(size)}) was not loaded: ${reason} (${limitText}).`,
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const note = `\n\n[This file was truncated from ${kib(size)}.]`;
|
|
110
|
+
const contentBudget = allowed - Buffer.byteLength(note);
|
|
111
|
+
let prefix = utf8Prefix(file.content, contentBudget);
|
|
112
|
+
let content;
|
|
113
|
+
while (true) {
|
|
114
|
+
let openFence;
|
|
115
|
+
for (const match of prefix.matchAll(/^[ \t]*(`{3,}|~{3,})([^\n]*)$/gm)) {
|
|
116
|
+
const marker = match[1];
|
|
117
|
+
if (!marker)
|
|
118
|
+
continue;
|
|
119
|
+
if (openFence === undefined)
|
|
120
|
+
openFence = marker;
|
|
121
|
+
else if (marker[0] === openFence[0] && marker.length >= openFence.length && match[2]?.trim() === "") {
|
|
122
|
+
openFence = undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const closingFence = openFence ? `${prefix.endsWith("\n") ? "" : "\n"}${openFence}\n` : "";
|
|
126
|
+
content = `${prefix}${closingFence}${note}`;
|
|
127
|
+
if (Buffer.byteLength(content) <= allowed)
|
|
128
|
+
break;
|
|
129
|
+
// A longer closing marker can expose another opener when the prefix shrinks.
|
|
130
|
+
prefix = utf8Prefix(prefix, Math.max(0, contentBudget - Buffer.byteLength(closingFence)));
|
|
131
|
+
}
|
|
132
|
+
const loaded = Buffer.byteLength(prefix, "utf8");
|
|
133
|
+
diagnostics.push({
|
|
134
|
+
type: "warning",
|
|
135
|
+
category: "size-limit",
|
|
136
|
+
path: file.path,
|
|
137
|
+
message: `Context file is ${kib(size)}; only the first ${kib(loaded)} was loaded (${limitText}).`,
|
|
138
|
+
});
|
|
139
|
+
kept.unshift({
|
|
140
|
+
path: file.path,
|
|
141
|
+
content,
|
|
142
|
+
});
|
|
143
|
+
remaining -= Buffer.byteLength(content);
|
|
144
|
+
}
|
|
145
|
+
return Object.assign(kept, { diagnostics });
|
|
146
|
+
}
|
|
147
|
+
export function loadProjectContextFiles(options) {
|
|
148
|
+
const resolvedCwd = resolvePath(options.cwd);
|
|
149
|
+
const resolvedAgentDir = resolvePath(options.agentDir);
|
|
150
|
+
const contextFiles = [];
|
|
151
|
+
const seenPaths = new Set();
|
|
152
|
+
const globalContext = loadContextFileFromDir(resolvedAgentDir);
|
|
153
|
+
if (globalContext) {
|
|
154
|
+
contextFiles.push(globalContext);
|
|
155
|
+
seenPaths.add(globalContext.path);
|
|
156
|
+
}
|
|
157
|
+
const ancestorContextFiles = [];
|
|
158
|
+
const shadowedContextFile = findShadowedContextFile(resolvedCwd);
|
|
159
|
+
let currentDir = resolvedCwd;
|
|
160
|
+
while (true) {
|
|
161
|
+
const contextFile = loadContextFileFromDir(currentDir);
|
|
162
|
+
const isShadowed = shadowedContextFile !== undefined && canonicalizePath(contextFile?.path ?? "") === shadowedContextFile;
|
|
163
|
+
if (contextFile && !isShadowed && !seenPaths.has(contextFile.path)) {
|
|
164
|
+
ancestorContextFiles.unshift(contextFile);
|
|
165
|
+
seenPaths.add(contextFile.path);
|
|
166
|
+
}
|
|
167
|
+
const parentDir = dirname(currentDir);
|
|
168
|
+
if (parentDir === currentDir)
|
|
169
|
+
break;
|
|
170
|
+
currentDir = parentDir;
|
|
171
|
+
}
|
|
172
|
+
contextFiles.push(...ancestorContextFiles);
|
|
173
|
+
return applyContextFileLimits(contextFiles, options);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=context-files.js.map
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { AvailableTool, PromptSurface } from "./types.ts";
|
|
2
|
+
export declare const DEFAULT_ROUTING_LINE_RULE = "The line keeps your reading transparent";
|
|
2
3
|
export declare function buildIntentGate(config: {
|
|
3
4
|
tools: AvailableTool[];
|
|
4
5
|
surface?: PromptSurface;
|
|
6
|
+
/** Terminal-only: the clause before "only the user's explicit request commits you"; presets replace it. */
|
|
7
|
+
routingLineRule?: string;
|
|
5
8
|
}): string;
|
|
6
9
|
//# sourceMappingURL=intent-gate.d.ts.map
|
|
@@ -6,16 +6,17 @@ function buildKeyTriggers(tools) {
|
|
|
6
6
|
}
|
|
7
7
|
return `\nSpecialized search available this turn: ${triggerTools}. Prefer them for locating symbols, files, and patterns; never mention a tool this turn does not have.\n`;
|
|
8
8
|
}
|
|
9
|
-
const
|
|
9
|
+
export const DEFAULT_ROUTING_LINE_RULE = "The line keeps your reading transparent";
|
|
10
|
+
const terminalRouting = (routingLineRule) => `Open every turn with one short routing line:
|
|
10
11
|
|
|
11
12
|
I read this as [intent] - [plan]. I'll stop when [the observable condition that ends this turn].
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
${routingLineRule}; only the user's explicit request commits you to implementation. Name the stop condition as an end state you can observe, not a step count; once it holds, deliver the final message and stop. Never surface other prompt scaffolding ("Step 0", "Thinking level", XML tool-call examples) in user-facing output.`;
|
|
14
15
|
const APP_ROUTING = `Only the user's explicit request commits you to implementation. Before acting, decide the stop condition - an end state you can observe, not a step count; once it holds, deliver the final message and stop. Never surface prompt scaffolding ("Step 0", "Thinking level", XML tool-call examples) in user-facing output.`;
|
|
15
16
|
export function buildIntentGate(config) {
|
|
16
17
|
return `## Intent Gate
|
|
17
18
|
|
|
18
|
-
${(config.surface ?? "terminal") === "terminal" ?
|
|
19
|
+
${(config.surface ?? "terminal") === "terminal" ? terminalRouting(config.routingLineRule ?? DEFAULT_ROUTING_LINE_RULE) : APP_ROUTING}
|
|
19
20
|
${buildKeyTriggers(config.tools)}
|
|
20
21
|
Route by true intent, not surface form:
|
|
21
22
|
- Information asks (explain, look into, investigate): read the code, report the answer or findings - no edits, no fixes yet.
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare const DEFAULT_ERROR_RULE = "Never silently swallow errors; never shotgun-debug with unrelated edits or blind retries.";
|
|
2
|
+
export declare function buildPoliciesSection(options?: {
|
|
3
|
+
errorRule?: string;
|
|
4
|
+
}): string;
|
|
2
5
|
//# sourceMappingURL=policies.d.ts.map
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const DEFAULT_ERROR_RULE = "Never silently swallow errors; never shotgun-debug with unrelated edits or blind retries.";
|
|
2
|
+
export function buildPoliciesSection(options = {}) {
|
|
2
3
|
return `## Policies
|
|
3
4
|
|
|
4
5
|
### Hard Blocks
|
|
5
6
|
- Never create a git commit unless the user explicitly requested it.
|
|
6
7
|
- Never present unread code or unrun commands as verified fact.
|
|
7
8
|
- Never suppress type errors, lint warnings, or test failures, and never delete or skip failing tests to go green.
|
|
8
|
-
-
|
|
9
|
+
- ${options.errorRule ?? DEFAULT_ERROR_RULE}
|
|
9
10
|
- Never present partial work as complete or deliver a stub, placeholder, or no-op as the feature; say what is done, what is not, and why you stopped.`;
|
|
10
11
|
}
|
|
11
12
|
//# sourceMappingURL=policies.js.map
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare const DEFAULT_WORKING_TASK_OPENING = "Fire independent tool calls as one parallel wave - reads, searches, listings, diagnostics - and bias toward breadth when context is thin: pull in anything even loosely relevant now instead of serially later. Wasted reads cost almost nothing; stale assumptions cost the turn. Edits and result-dependent calls go one at a time, each compared with the state you meant to produce; when the result must be seen rather than read, render after each change and look before the next. Never fill missing parameters with placeholders.";
|
|
2
|
+
export declare function buildWorkingTaskSection(options?: {
|
|
3
|
+
opening?: string;
|
|
4
|
+
}): string;
|
|
2
5
|
//# sourceMappingURL=working-task.d.ts.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const DEFAULT_WORKING_TASK_OPENING = "Fire independent tool calls as one parallel wave - reads, searches, listings, diagnostics - and bias toward breadth when context is thin: pull in anything even loosely relevant now instead of serially later. Wasted reads cost almost nothing; stale assumptions cost the turn. Edits and result-dependent calls go one at a time, each compared with the state you meant to produce; when the result must be seen rather than read, render after each change and look before the next. Never fill missing parameters with placeholders.";
|
|
2
|
+
export function buildWorkingTaskSection(options = {}) {
|
|
2
3
|
return `## Working the Task
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
${options.opening ?? DEFAULT_WORKING_TASK_OPENING}
|
|
5
6
|
|
|
6
7
|
Memory of file contents is unreliable - read before claiming, re-read before editing. Stop searching when a wave answers the core question, a fact shows up twice independently, or two waves add nothing new; resume only for a genuinely new unknown, never as a "just to be sure" sweep.
|
|
7
8
|
|
|
@@ -18,10 +18,11 @@ export interface WorkstationFacts {
|
|
|
18
18
|
* Families follow the prompt-preset conventions: Claude/GLM take direct
|
|
19
19
|
* imperatives in a tagged block, GPT (codex) takes terse bounded rules, Kimi
|
|
20
20
|
* takes positive operational constraints (all-caps directives make K2.x
|
|
21
|
-
* overthink),
|
|
22
|
-
*
|
|
21
|
+
* overthink), the `gemini` dialect takes one plain sentence pair (the
|
|
22
|
+
* provider's guidance: direct instructions, no persuasive emphasis), and `default` is
|
|
23
|
+
* the maximum-emphasis fallback for unmatched models.
|
|
23
24
|
*/
|
|
24
|
-
export type WorkstationDialect = "default" | "claude" | "codex" | "kimi";
|
|
25
|
+
export type WorkstationDialect = "default" | "claude" | "codex" | "kimi" | "gemini";
|
|
25
26
|
/** Collect host facts once per process; every call after the first is free. */
|
|
26
27
|
export declare function collectWorkstationFacts(): WorkstationFacts;
|
|
27
28
|
export interface BuildWorkstationSectionOptions {
|
|
@@ -62,6 +62,7 @@ ${executors} on THIS workstation. Match commands, paths, package managers, and p
|
|
|
62
62
|
</execution_context>`,
|
|
63
63
|
codex: (executors) => `${executors} on this workstation; match commands, paths, and parallelism to it. Written code may target other platforms — executed code runs here.`,
|
|
64
64
|
kimi: (executors) => `${executors} on this workstation — choose commands, paths, and parallelism that fit it. When writing code for another target platform, keep the code portable; only the execution happens here.`,
|
|
65
|
+
gemini: (executors) => `${executors} on this workstation; match commands, paths, package managers, and parallel pool sizes to it. Code you write may target another machine; code you run always runs here.`,
|
|
65
66
|
default: (executors) => `**EXECUTION HAPPENS HERE.** ${executors} on THIS machine — you MUST match commands, paths, package managers, and parallel pool sizes to the workstation above. Code you WRITE may target a different machine; anything you RUN executes HERE.`,
|
|
66
67
|
};
|
|
67
68
|
/** Render the `<workstation>` facts block plus the dialect-tuned execution-context instruction. */
|
|
@@ -3,7 +3,7 @@ import { describeModelBlocks } from "../../../credential-pool/model-scope.js";
|
|
|
3
3
|
import { accountDisplayNameCommand, promptAccountDisplayName } from "../account-display-name.js";
|
|
4
4
|
import { createExtensionLoginInteraction, LOGIN_CANCELLED_MESSAGE } from "../oauth-login-interaction.js";
|
|
5
5
|
import { emitProviderAccountsChanged } from "./account-events.js";
|
|
6
|
-
import { ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, pinProviderAccount, removeProviderAccount } from "./account-management.js";
|
|
6
|
+
import { ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, pinProviderAccount, removeProviderAccount, unblockProviderAccount, } from "./account-management.js";
|
|
7
7
|
import { emptyCredential, listAccounts } from "./accounts.js";
|
|
8
8
|
import { selectAccount } from "./affinity.js";
|
|
9
9
|
import { loadAnthropicSubscriptionProviderSettingsFromDisk, } from "./settings.js";
|
|
@@ -65,7 +65,7 @@ export function registerClaudeAccountCommand(pi, deps = {}) {
|
|
|
65
65
|
});
|
|
66
66
|
pi.registerCommand("claude-account", {
|
|
67
67
|
description: "List and manage Anthropic Subscription accounts.",
|
|
68
|
-
argumentHint: "[add | remove <id> | pin <id> | unpin | rename <id> <display name...> | clear-name <id>]",
|
|
68
|
+
argumentHint: "[add | remove <id> | pin <id> | unpin | unblock <id> | rename <id> <display name...> | clear-name <id>]",
|
|
69
69
|
requiresArguments: false,
|
|
70
70
|
handler: async (rawArgs, ctx) => {
|
|
71
71
|
if (await accountDisplayNameCommand(ctx, ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, rawArgs))
|
|
@@ -92,7 +92,11 @@ export function registerClaudeAccountCommand(pi, deps = {}) {
|
|
|
92
92
|
await unpinAccount(ctx);
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
95
|
-
|
|
95
|
+
if (action === "unblock") {
|
|
96
|
+
await unblockNamedAccount(ctx, args[1], environment);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
ctx.ui.notify("Usage: /claude-account [add | remove <id> | pin <id> | unpin | unblock <id> | rename <id> <display name...> | clear-name <id>]", "error");
|
|
96
100
|
},
|
|
97
101
|
});
|
|
98
102
|
}
|
|
@@ -194,6 +198,25 @@ async function pinNamedAccount(ctx, name, environment) {
|
|
|
194
198
|
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
195
199
|
}
|
|
196
200
|
}
|
|
201
|
+
async function unblockNamedAccount(ctx, name, environment) {
|
|
202
|
+
if (!name) {
|
|
203
|
+
ctx.ui.notify("Usage: /claude-account unblock <name>", "error");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (!readAccounts(ctx, environment).accounts.some((account) => account.name === name)) {
|
|
207
|
+
return accountError(ctx, name);
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const env = new Proxy({}, { get: (_target, key) => environment(String(key)) });
|
|
211
|
+
const lifted = await unblockProviderAccount(ctx.modelRegistry.authStorage, ANTHROPIC_SUBSCRIPTION_PROVIDER_ID, name, env);
|
|
212
|
+
ctx.ui.notify(lifted
|
|
213
|
+
? `Unblocked Anthropic Subscription account: ${name}. The next turn may use it.`
|
|
214
|
+
: `Anthropic Subscription account '${name}' has no usage block to lift.`, "info");
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
197
220
|
async function unpinAccount(ctx) {
|
|
198
221
|
const credential = asCredential(ctx.modelRegistry.authStorage.get(ANTHROPIC_SUBSCRIPTION_PROVIDER_ID));
|
|
199
222
|
if (!credential?.pinned) {
|
|
@@ -11,4 +11,9 @@ export type ProviderAccountSummary = {
|
|
|
11
11
|
export declare function getProviderAccounts(storage: AuthStorage, provider: string, env?: NodeJS.ProcessEnv): ProviderAccountSummary[];
|
|
12
12
|
export declare function pinProviderAccount(storage: AuthStorage, provider: string, name: string | null, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
13
13
|
export declare function removeProviderAccount(storage: AuthStorage, provider: string, name: string): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Lifts a usage or rate-limit block the user knows is wrong (extra usage bought, a misread reset).
|
|
16
|
+
* Auth blocks stay: only a login or the re-probe can lift those. Returns whether a block was lifted.
|
|
17
|
+
*/
|
|
18
|
+
export declare function unblockProviderAccount(storage: AuthStorage, provider: string, name: string, env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
14
19
|
//# sourceMappingURL=account-management.d.ts.map
|