@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
|
@@ -4,6 +4,7 @@ import type { RetryPolicyProfile } from "@earendil-works/pi-ai/utils/retry-profi
|
|
|
4
4
|
import type { TuiMode as RendererTuiMode, ScrollViewScrollbar, TerminalCapabilities, WheelScrollLines } from "@earendil-works/pi-tui";
|
|
5
5
|
import type { CompactionModelSelector, CompactionSettings } from "./compaction-settings-access.ts";
|
|
6
6
|
import { type ResolvedCompactionSettings } from "./compaction-settings-resolver.ts";
|
|
7
|
+
import type { ContextFileLimits } from "./context-files.ts";
|
|
7
8
|
import { type FallbackCircuitSettings, type ResolvedFallbackCircuitSettings } from "./retry-fallback/circuit.ts";
|
|
8
9
|
import { type ResolvedHintPolicySettings, type ResolvedRetryFallbackSettings, type RetrySettings as RetrySettingsConfig } from "./retry-fallback/settings.ts";
|
|
9
10
|
import { parseSettingsJson } from "./settings-json.ts";
|
|
@@ -68,6 +69,7 @@ export interface Settings {
|
|
|
68
69
|
compaction?: CompactionSettings & {
|
|
69
70
|
model?: string;
|
|
70
71
|
};
|
|
72
|
+
contextFiles?: ContextFileLimits;
|
|
71
73
|
branchSummary?: BranchSummarySettings;
|
|
72
74
|
retry?: RetrySettingsConfig;
|
|
73
75
|
fallback?: FallbackCircuitSettings;
|
|
@@ -108,6 +110,8 @@ export interface Settings {
|
|
|
108
110
|
maxPerUserInput?: number;
|
|
109
111
|
maxToolFreePerMinute?: number;
|
|
110
112
|
};
|
|
113
|
+
turn_inactivity_ms?: number;
|
|
114
|
+
turn_max_runtime_ms?: number;
|
|
111
115
|
todo?: TodoSettings;
|
|
112
116
|
recommendedModels?: string[];
|
|
113
117
|
favoriteModels?: string[];
|
|
@@ -165,6 +169,8 @@ export declare function getSettingsPath(cwd: string, agentDir: string, scope: Se
|
|
|
165
169
|
export declare function getInMemorySettingsPath(scope: SettingsScope): string;
|
|
166
170
|
export interface SettingsManagerCreateOptions {
|
|
167
171
|
projectTrusted?: boolean;
|
|
172
|
+
/** The project trust exists only because the session runs Full access (senpi#3414): leaving it withdraws the trust. */
|
|
173
|
+
trustFromFullAccess?: boolean;
|
|
168
174
|
}
|
|
169
175
|
export interface SettingsStorage {
|
|
170
176
|
withLock(scope: SettingsScope, fn: (current: string | undefined) => string | undefined): void;
|
|
@@ -274,9 +280,22 @@ export declare class SettingsManager {
|
|
|
274
280
|
maxPerUserInput: number;
|
|
275
281
|
maxToolFreePerMinute: number;
|
|
276
282
|
};
|
|
283
|
+
getTurnInactivityMs(): number | undefined;
|
|
284
|
+
getTurnMaxRuntimeMs(): number | undefined;
|
|
277
285
|
getTodoFirstTurnPlan(): TodoFirstTurnPlan;
|
|
278
286
|
getTodoTurnEndBackstop(): boolean;
|
|
279
287
|
isProjectTrusted(): boolean;
|
|
288
|
+
/**
|
|
289
|
+
* True when this session's project trust came only from its Full access preset (never asked, never
|
|
290
|
+
* saved): leaving Full access withdraws it, and going back restores it (senpi#3414, #3420).
|
|
291
|
+
*/
|
|
292
|
+
private trustFromFullAccess;
|
|
293
|
+
isTrustFromFullAccess(): boolean;
|
|
294
|
+
/** True when a Full-access-only trust was withdrawn in this session and Full access may restore it. */
|
|
295
|
+
canRestoreFullAccessTrust(): boolean;
|
|
296
|
+
/** Counts project-trust changes; a reload compares it before installing what it loaded (#3420). */
|
|
297
|
+
private trustEpoch;
|
|
298
|
+
getTrustEpoch(): number;
|
|
280
299
|
setProjectTrusted(trusted: boolean): void;
|
|
281
300
|
reload(): Promise<void>;
|
|
282
301
|
getSelectedSettingsSources(): SettingsSourceSelection[];
|
|
@@ -465,6 +465,13 @@ export class SettingsManager {
|
|
|
465
465
|
this.selectedSources = new Map();
|
|
466
466
|
this.sourceListeners = [];
|
|
467
467
|
this.providerSettingsListeners = new Set();
|
|
468
|
+
/**
|
|
469
|
+
* True when this session's project trust came only from its Full access preset (never asked, never
|
|
470
|
+
* saved): leaving Full access withdraws it, and going back restores it (senpi#3414, #3420).
|
|
471
|
+
*/
|
|
472
|
+
this.trustFromFullAccess = false;
|
|
473
|
+
/** Counts project-trust changes; a reload compares it before installing what it loaded (#3420). */
|
|
474
|
+
this.trustEpoch = 0;
|
|
468
475
|
this.storage = storage;
|
|
469
476
|
this.globalSettings = initialGlobal;
|
|
470
477
|
this.projectSettings = initialProject;
|
|
@@ -506,7 +513,9 @@ export class SettingsManager {
|
|
|
506
513
|
if (projectLoad.error) {
|
|
507
514
|
initialErrors.push(toSettingsError("project", projectLoad.error, settingsPaths.project));
|
|
508
515
|
}
|
|
509
|
-
|
|
516
|
+
const manager = new SettingsManager(storage, globalLoad.settings, projectLoad.settings, globalLoad.error, projectLoad.error, initialErrors, projectTrusted, initialSources, settingsPaths);
|
|
517
|
+
manager.trustFromFullAccess = options.trustFromFullAccess === true;
|
|
518
|
+
return manager;
|
|
510
519
|
}
|
|
511
520
|
/** Create an in-memory SettingsManager (no file I/O) */
|
|
512
521
|
static inMemory(settings = {}, options = {}) {
|
|
@@ -717,6 +726,12 @@ export class SettingsManager {
|
|
|
717
726
|
maxToolFreePerMinute: limit(configured?.maxToolFreePerMinute, MAX_TOOL_FREE_ENGINE_TURNS_PER_WINDOW),
|
|
718
727
|
};
|
|
719
728
|
}
|
|
729
|
+
getTurnInactivityMs() {
|
|
730
|
+
return this.settings.turn_inactivity_ms;
|
|
731
|
+
}
|
|
732
|
+
getTurnMaxRuntimeMs() {
|
|
733
|
+
return this.settings.turn_max_runtime_ms;
|
|
734
|
+
}
|
|
720
735
|
getTodoFirstTurnPlan() {
|
|
721
736
|
const configured = this.settings.todo?.firstTurnPlan;
|
|
722
737
|
return configured === "remind" || configured === "off" ? configured : "force";
|
|
@@ -728,11 +743,22 @@ export class SettingsManager {
|
|
|
728
743
|
isProjectTrusted() {
|
|
729
744
|
return this.projectTrusted;
|
|
730
745
|
}
|
|
746
|
+
isTrustFromFullAccess() {
|
|
747
|
+
return this.trustFromFullAccess && this.projectTrusted;
|
|
748
|
+
}
|
|
749
|
+
/** True when a Full-access-only trust was withdrawn in this session and Full access may restore it. */
|
|
750
|
+
canRestoreFullAccessTrust() {
|
|
751
|
+
return this.trustFromFullAccess && !this.projectTrusted;
|
|
752
|
+
}
|
|
753
|
+
getTrustEpoch() {
|
|
754
|
+
return this.trustEpoch;
|
|
755
|
+
}
|
|
731
756
|
setProjectTrusted(trusted) {
|
|
732
757
|
if (this.projectTrusted === trusted) {
|
|
733
758
|
return;
|
|
734
759
|
}
|
|
735
760
|
this.projectTrusted = trusted;
|
|
761
|
+
this.trustEpoch += 1;
|
|
736
762
|
this.modifiedProjectFields.clear();
|
|
737
763
|
this.modifiedProjectNestedFields.clear();
|
|
738
764
|
if (!trusted) {
|
|
@@ -49,4 +49,8 @@ export interface OpenAISettings {
|
|
|
49
49
|
export interface ProviderConcurrencySettings {
|
|
50
50
|
maxConcurrency?: number;
|
|
51
51
|
}
|
|
52
|
+
export interface TurnDeadlineSettings {
|
|
53
|
+
turn_inactivity_ms?: number;
|
|
54
|
+
turn_max_runtime_ms?: number;
|
|
55
|
+
}
|
|
52
56
|
//# sourceMappingURL=settings-shapes.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ResourceDiagnostic } from "./diagnostics.ts";
|
|
2
|
+
import type { Skill } from "./skills.ts";
|
|
3
|
+
export declare const COMPACT_SKILL_CATALOG_COUNT = 100;
|
|
4
|
+
export declare const COMPACT_SKILL_CATALOG_DESCRIPTION_BYTES: number;
|
|
5
|
+
export declare const MAX_SKILL_CATALOG_BYTES: number;
|
|
6
|
+
/** A catalog this large is listed by name plus a one-line description, since it rides on every request. */
|
|
7
|
+
export declare function isLargeSkillCatalog(skills: readonly Skill[]): boolean;
|
|
8
|
+
export declare function warnIfLargeSkillCatalog(skills: readonly Skill[]): ResourceDiagnostic[];
|
|
9
|
+
/**
|
|
10
|
+
* Format skills for inclusion in a system prompt.
|
|
11
|
+
* Uses XML format per Agent Skills standard.
|
|
12
|
+
* See: https://agentskills.io/integrate-skills
|
|
13
|
+
*
|
|
14
|
+
* Skills with disableModelInvocation=true are excluded from the prompt
|
|
15
|
+
* (they can only be invoked explicitly via /skill:name commands).
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatSkillsForPrompt(skills: Skill[], fileReadTool?: "read" | "bash"): string;
|
|
18
|
+
//# sourceMappingURL=skills-prompt.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const COMPACT_SKILL_CATALOG_COUNT = 100;
|
|
2
|
+
export const COMPACT_SKILL_CATALOG_DESCRIPTION_BYTES = 64 * 1024;
|
|
3
|
+
export const MAX_SKILL_CATALOG_BYTES = 48 * 1024;
|
|
4
|
+
const ONE_LINE_DESCRIPTION_MAX_CHARS = 160;
|
|
5
|
+
/** A catalog this large is listed by name plus a one-line description, since it rides on every request. */
|
|
6
|
+
export function isLargeSkillCatalog(skills) {
|
|
7
|
+
if (skills.length > COMPACT_SKILL_CATALOG_COUNT)
|
|
8
|
+
return true;
|
|
9
|
+
const bytes = skills.reduce((sum, skill) => sum + Buffer.byteLength(skill.description, "utf8"), 0);
|
|
10
|
+
return bytes > COMPACT_SKILL_CATALOG_DESCRIPTION_BYTES;
|
|
11
|
+
}
|
|
12
|
+
export function warnIfLargeSkillCatalog(skills) {
|
|
13
|
+
const catalog = buildSkillCatalog(skills, "read");
|
|
14
|
+
if (!catalog.compact && !catalog.namesOnly)
|
|
15
|
+
return [];
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
type: "warning",
|
|
19
|
+
category: "size-limit",
|
|
20
|
+
message: catalog.namesOnly
|
|
21
|
+
? `The skills section is limited to ${MAX_SKILL_CATALOG_BYTES / 1024} KiB: descriptions were dropped and ${catalog.omitted} skills were omitted. Remove unused skills to shrink every request.`
|
|
22
|
+
: `The skills catalog uses one-line descriptions to limit request size (above ${COMPACT_SKILL_CATALOG_COUNT} skills, ${COMPACT_SKILL_CATALOG_DESCRIPTION_BYTES / 1024} KiB of descriptions, or ${MAX_SKILL_CATALOG_BYTES / 1024} KiB rendered). Remove unused skills to shrink every request.`,
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
function buildSkillCatalog(skills, fileReadTool) {
|
|
27
|
+
const visible = skills.filter((skill) => !skill.disableModelInvocation);
|
|
28
|
+
const compact = isLargeSkillCatalog(visible);
|
|
29
|
+
const options = { fileReadTool, compact, namesOnly: false, omitted: 0 };
|
|
30
|
+
const prompt = renderSkills(visible, options);
|
|
31
|
+
if (Buffer.byteLength(prompt) <= MAX_SKILL_CATALOG_BYTES)
|
|
32
|
+
return { prompt, compact, namesOnly: false, omitted: 0 };
|
|
33
|
+
const compactPrompt = compact ? prompt : renderSkills(visible, { ...options, compact: true });
|
|
34
|
+
if (Buffer.byteLength(compactPrompt) <= MAX_SKILL_CATALOG_BYTES) {
|
|
35
|
+
return { prompt: compactPrompt, compact: true, namesOnly: false, omitted: 0 };
|
|
36
|
+
}
|
|
37
|
+
const minimal = { ...options, namesOnly: true };
|
|
38
|
+
const namesPrompt = renderSkills(visible, minimal);
|
|
39
|
+
if (Buffer.byteLength(namesPrompt) <= MAX_SKILL_CATALOG_BYTES) {
|
|
40
|
+
return { prompt: namesPrompt, compact, namesOnly: true, omitted: 0 };
|
|
41
|
+
}
|
|
42
|
+
let low = 0;
|
|
43
|
+
let high = visible.length;
|
|
44
|
+
while (low < high) {
|
|
45
|
+
const count = Math.ceil((low + high) / 2);
|
|
46
|
+
const candidate = renderSkills(visible.slice(0, count), { ...minimal, omitted: visible.length - count });
|
|
47
|
+
if (Buffer.byteLength(candidate) <= MAX_SKILL_CATALOG_BYTES)
|
|
48
|
+
low = count;
|
|
49
|
+
else
|
|
50
|
+
high = count - 1;
|
|
51
|
+
}
|
|
52
|
+
const omitted = visible.length - low;
|
|
53
|
+
return {
|
|
54
|
+
prompt: renderSkills(visible.slice(0, low), { ...minimal, omitted }),
|
|
55
|
+
compact,
|
|
56
|
+
namesOnly: true,
|
|
57
|
+
omitted,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function oneLineDescription(description) {
|
|
61
|
+
const firstLine = description.trim().split("\n", 1)[0]?.trim() ?? "";
|
|
62
|
+
return firstLine.length <= ONE_LINE_DESCRIPTION_MAX_CHARS
|
|
63
|
+
? firstLine
|
|
64
|
+
: `${firstLine.slice(0, ONE_LINE_DESCRIPTION_MAX_CHARS - 1).trimEnd()}…`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Format skills for inclusion in a system prompt.
|
|
68
|
+
* Uses XML format per Agent Skills standard.
|
|
69
|
+
* See: https://agentskills.io/integrate-skills
|
|
70
|
+
*
|
|
71
|
+
* Skills with disableModelInvocation=true are excluded from the prompt
|
|
72
|
+
* (they can only be invoked explicitly via /skill:name commands).
|
|
73
|
+
*/
|
|
74
|
+
// Skill roots share long absolute prefixes across every skill under them; rendering
|
|
75
|
+
// that prefix per skill bills it once per skill. The roots table pays it once per
|
|
76
|
+
// distinct root and each location becomes a short alias/relative path. A one-line
|
|
77
|
+
// rule tells the model to expand aliases by joining them back to the root.
|
|
78
|
+
// `fileReadTool` lets a bash-only tool set still load skills: the loading rule names
|
|
79
|
+
// the tool the session actually has.
|
|
80
|
+
export function formatSkillsForPrompt(skills, fileReadTool = "read") {
|
|
81
|
+
return buildSkillCatalog(skills, fileReadTool).prompt;
|
|
82
|
+
}
|
|
83
|
+
function renderSkills(visibleSkills, options) {
|
|
84
|
+
if (visibleSkills.length === 0 && options.omitted === 0) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
const rootByPath = new Map();
|
|
88
|
+
for (const skill of visibleSkills) {
|
|
89
|
+
const dir = skill.filePath.split("/").slice(0, -2).join("/"); // parent of <name>/SKILL.md
|
|
90
|
+
if (!rootByPath.has(dir))
|
|
91
|
+
rootByPath.set(dir, `r${rootByPath.size}`);
|
|
92
|
+
}
|
|
93
|
+
const lines = [
|
|
94
|
+
"\n\nThe following skills provide specialized instructions for specific tasks.",
|
|
95
|
+
options.fileReadTool === "read"
|
|
96
|
+
? "Use the read tool to load a skill's file when its description matches the task and its instructions would change the work; keyword overlap or mere availability is not a reason."
|
|
97
|
+
: "Use bash to load a skill's file when its description matches the task and its instructions would change the work; keyword overlap or mere availability is not a reason.",
|
|
98
|
+
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
|
|
99
|
+
"",
|
|
100
|
+
"<skill_roots>",
|
|
101
|
+
];
|
|
102
|
+
for (const [dir, alias] of rootByPath) {
|
|
103
|
+
lines.push(` <${alias}>${escapeXml(dir)}</${alias}>`);
|
|
104
|
+
}
|
|
105
|
+
lines.push("</skill_roots>", "A location's `rN/` prefix expands to the matching root above.", "", "<available_skills>");
|
|
106
|
+
for (const skill of visibleSkills) {
|
|
107
|
+
const dir = skill.filePath.split("/").slice(0, -2).join("/");
|
|
108
|
+
const alias = rootByPath.get(dir);
|
|
109
|
+
const relative = skill.filePath.startsWith(`${dir}/`) ? skill.filePath.slice(dir.length + 1) : skill.filePath;
|
|
110
|
+
lines.push(" <skill>");
|
|
111
|
+
lines.push(` <name>${escapeXml(skill.name)}</name>`);
|
|
112
|
+
if (!options.namesOnly) {
|
|
113
|
+
lines.push(` <description>${escapeXml(options.compact ? oneLineDescription(skill.description) : skill.description)}</description>`);
|
|
114
|
+
}
|
|
115
|
+
lines.push(` <location>${escapeXml(`${alias}/${relative}`)}</location>`);
|
|
116
|
+
lines.push(" </skill>");
|
|
117
|
+
}
|
|
118
|
+
if (options.omitted > 0)
|
|
119
|
+
lines.push(` <omitted_skills>${options.omitted}</omitted_skills>`);
|
|
120
|
+
lines.push("</available_skills>");
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
function escapeXml(str) {
|
|
124
|
+
return str
|
|
125
|
+
.replace(/&/g, "&")
|
|
126
|
+
.replace(/</g, "<")
|
|
127
|
+
.replace(/>/g, ">")
|
|
128
|
+
.replace(/"/g, """)
|
|
129
|
+
.replace(/'/g, "'");
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=skills-prompt.js.map
|
package/dist/core/skills.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ResourceDiagnostic } from "./diagnostics.ts";
|
|
2
2
|
import { type SourceInfo } from "./source-info.ts";
|
|
3
|
+
export { formatSkillsForPrompt } from "./skills-prompt.ts";
|
|
3
4
|
export interface SkillFrontmatter {
|
|
4
5
|
name?: string;
|
|
5
6
|
description?: string;
|
|
@@ -39,15 +40,6 @@ export interface LoadSkillsFromDirOptions {
|
|
|
39
40
|
* - recurse into subdirectories to find SKILL.md
|
|
40
41
|
*/
|
|
41
42
|
export declare function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkillsResult;
|
|
42
|
-
/**
|
|
43
|
-
* Format skills for inclusion in a system prompt.
|
|
44
|
-
* Uses XML format per Agent Skills standard.
|
|
45
|
-
* See: https://agentskills.io/integrate-skills
|
|
46
|
-
*
|
|
47
|
-
* Skills with disableModelInvocation=true are excluded from the prompt
|
|
48
|
-
* (they can only be invoked explicitly via /skill:name commands).
|
|
49
|
-
*/
|
|
50
|
-
export declare function formatSkillsForPrompt(skills: Skill[], fileReadTool?: "read" | "bash"): string;
|
|
51
43
|
export interface LoadSkillsOptions {
|
|
52
44
|
/** Working directory for project-local skills. */
|
|
53
45
|
cwd: string;
|
package/dist/core/skills.js
CHANGED
|
@@ -6,6 +6,7 @@ import { parseFrontmatter } from "../utils/frontmatter.js";
|
|
|
6
6
|
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
7
7
|
import { readSkillMarkdownSource, shouldSkipSkillWalkDirectoryName } from "./skill-discovery.js";
|
|
8
8
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
9
|
+
export { formatSkillsForPrompt } from "./skills-prompt.js";
|
|
9
10
|
/** Max name length per spec */
|
|
10
11
|
const MAX_NAME_LENGTH = 64;
|
|
11
12
|
/** Max description length per spec */
|
|
@@ -266,65 +267,6 @@ function loadSkillFromFile(filePath, source) {
|
|
|
266
267
|
diagnostics,
|
|
267
268
|
};
|
|
268
269
|
}
|
|
269
|
-
/**
|
|
270
|
-
* Format skills for inclusion in a system prompt.
|
|
271
|
-
* Uses XML format per Agent Skills standard.
|
|
272
|
-
* See: https://agentskills.io/integrate-skills
|
|
273
|
-
*
|
|
274
|
-
* Skills with disableModelInvocation=true are excluded from the prompt
|
|
275
|
-
* (they can only be invoked explicitly via /skill:name commands).
|
|
276
|
-
*/
|
|
277
|
-
// Skill roots share long absolute prefixes across every skill under them; rendering
|
|
278
|
-
// that prefix per skill bills it once per skill. The roots table pays it once per
|
|
279
|
-
// distinct root and each location becomes a short alias/relative path. A one-line
|
|
280
|
-
// rule tells the model to expand aliases by joining them back to the root.
|
|
281
|
-
// `fileReadTool` lets a bash-only tool set still load skills: the loading rule names
|
|
282
|
-
// the tool the session actually has.
|
|
283
|
-
export function formatSkillsForPrompt(skills, fileReadTool = "read") {
|
|
284
|
-
const visibleSkills = skills.filter((s) => !s.disableModelInvocation);
|
|
285
|
-
if (visibleSkills.length === 0) {
|
|
286
|
-
return "";
|
|
287
|
-
}
|
|
288
|
-
const rootByPath = new Map();
|
|
289
|
-
for (const skill of visibleSkills) {
|
|
290
|
-
const dir = skill.filePath.split("/").slice(0, -2).join("/"); // parent of <name>/SKILL.md
|
|
291
|
-
if (!rootByPath.has(dir))
|
|
292
|
-
rootByPath.set(dir, `r${rootByPath.size}`);
|
|
293
|
-
}
|
|
294
|
-
const lines = [
|
|
295
|
-
"\n\nThe following skills provide specialized instructions for specific tasks.",
|
|
296
|
-
fileReadTool === "read"
|
|
297
|
-
? "Use the read tool to load a skill's file when its description matches the task and its instructions would change the work; keyword overlap or mere availability is not a reason."
|
|
298
|
-
: "Use bash to load a skill's file when its description matches the task and its instructions would change the work; keyword overlap or mere availability is not a reason.",
|
|
299
|
-
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
|
|
300
|
-
"",
|
|
301
|
-
"<skill_roots>",
|
|
302
|
-
];
|
|
303
|
-
for (const [dir, alias] of rootByPath) {
|
|
304
|
-
lines.push(` <${alias}>${escapeXml(dir)}</${alias}>`);
|
|
305
|
-
}
|
|
306
|
-
lines.push("</skill_roots>", "A location's `rN/` prefix expands to the matching root above.", "", "<available_skills>");
|
|
307
|
-
for (const skill of visibleSkills) {
|
|
308
|
-
const dir = skill.filePath.split("/").slice(0, -2).join("/");
|
|
309
|
-
const alias = rootByPath.get(dir);
|
|
310
|
-
const relative = skill.filePath.startsWith(`${dir}/`) ? skill.filePath.slice(dir.length + 1) : skill.filePath;
|
|
311
|
-
lines.push(" <skill>");
|
|
312
|
-
lines.push(` <name>${escapeXml(skill.name)}</name>`);
|
|
313
|
-
lines.push(` <description>${escapeXml(skill.description)}</description>`);
|
|
314
|
-
lines.push(` <location>${escapeXml(`${alias}/${relative}`)}</location>`);
|
|
315
|
-
lines.push(" </skill>");
|
|
316
|
-
}
|
|
317
|
-
lines.push("</available_skills>");
|
|
318
|
-
return lines.join("\n");
|
|
319
|
-
}
|
|
320
|
-
function escapeXml(str) {
|
|
321
|
-
return str
|
|
322
|
-
.replace(/&/g, "&")
|
|
323
|
-
.replace(/</g, "<")
|
|
324
|
-
.replace(/>/g, ">")
|
|
325
|
-
.replace(/"/g, """)
|
|
326
|
-
.replace(/'/g, "'");
|
|
327
|
-
}
|
|
328
270
|
/**
|
|
329
271
|
* Load skills from all configured locations.
|
|
330
272
|
* Returns skills and any validation diagnostics.
|
|
@@ -17,13 +17,22 @@ export declare function getProjectTrustParentPath(cwd: string): string | undefin
|
|
|
17
17
|
export declare function getProjectTrustOptions(cwd: string, options?: {
|
|
18
18
|
includeSessionOnly?: boolean;
|
|
19
19
|
}): ProjectTrustOption[];
|
|
20
|
+
/**
|
|
21
|
+
* The project `.agents/skills` directories for `cwd`, nearest first: one per folder from cwd up to
|
|
22
|
+
* the git root (or the filesystem root), stopping below the user's home. Home and every folder
|
|
23
|
+
* above it are never a project; `~/.agents/skills` is the user's own global skills. The ONE walk
|
|
24
|
+
* both the trust check and the loader use, over the same spelling and the same git root (#3420
|
|
25
|
+
* review): paths stay as spelled (resources are compared as strings), and each folder's real path
|
|
26
|
+
* decides whether it is home or above, so a symlink can't carry the walk past home either way.
|
|
27
|
+
*/
|
|
28
|
+
export declare function projectAgentsSkillDirs(cwd: string): string[];
|
|
20
29
|
/**
|
|
21
30
|
* Returns true when cwd has project-local resources that must be gated by
|
|
22
31
|
* project trust: trust-requiring entries under the project config dir or the
|
|
23
|
-
* legacy cwd/.pi dir (whose resources are also discovered), or
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
32
|
+
* legacy cwd/.pi dir (whose resources are also discovered), or a project
|
|
33
|
+
* `.agents/skills` (`projectAgentsSkillDirs`, the same walk the loader uses).
|
|
34
|
+
* The user's home and every folder above it are never a project: their config
|
|
35
|
+
* dirs and .agents/skills are the user's own global resources (#2465).
|
|
27
36
|
*/
|
|
28
37
|
export declare function hasTrustRequiringProjectResources(cwd: string): boolean;
|
|
29
38
|
export declare class ProjectTrustStore {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { dirname, join, sep } from "node:path";
|
|
4
4
|
import lockfile from "proper-lockfile";
|
|
5
5
|
import { CONFIG_DIR_NAME } from "../config.js";
|
|
6
6
|
import { canonicalizePath, canonicalizePathStrict, resolvePath } from "../utils/paths.js";
|
|
@@ -201,35 +201,66 @@ function withTrustFileLock(path, fn) {
|
|
|
201
201
|
release();
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
+
/** True when `dir` is the user's home or a folder above it: never a project (#2465). */
|
|
205
|
+
function isHomeOrAboveDir(dir, homeDir) {
|
|
206
|
+
return dir === homeDir || homeDir.startsWith(dir.endsWith(sep) ? dir : `${dir}${sep}`);
|
|
207
|
+
}
|
|
208
|
+
/** The nearest folder at or above `dir` that holds `.git`, as spelled; undefined outside a repository. */
|
|
209
|
+
function findGitRepoRoot(dir) {
|
|
210
|
+
let currentDir = dir;
|
|
211
|
+
while (!existsSync(join(currentDir, ".git"))) {
|
|
212
|
+
const parentDir = dirname(currentDir);
|
|
213
|
+
if (parentDir === currentDir)
|
|
214
|
+
return undefined;
|
|
215
|
+
currentDir = parentDir;
|
|
216
|
+
}
|
|
217
|
+
return currentDir;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The project `.agents/skills` directories for `cwd`, nearest first: one per folder from cwd up to
|
|
221
|
+
* the git root (or the filesystem root), stopping below the user's home. Home and every folder
|
|
222
|
+
* above it are never a project; `~/.agents/skills` is the user's own global skills. The ONE walk
|
|
223
|
+
* both the trust check and the loader use, over the same spelling and the same git root (#3420
|
|
224
|
+
* review): paths stay as spelled (resources are compared as strings), and each folder's real path
|
|
225
|
+
* decides whether it is home or above, so a symlink can't carry the walk past home either way.
|
|
226
|
+
*/
|
|
227
|
+
export function projectAgentsSkillDirs(cwd) {
|
|
228
|
+
const homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));
|
|
229
|
+
const startDir = resolvePath(cwd);
|
|
230
|
+
const gitRepoRoot = findGitRepoRoot(startDir);
|
|
231
|
+
const dirs = [];
|
|
232
|
+
let currentDir = startDir;
|
|
233
|
+
while (!isHomeOrAboveDir(canonicalizePath(currentDir), homeDir)) {
|
|
234
|
+
dirs.push(join(currentDir, ".agents", "skills"));
|
|
235
|
+
if (currentDir === gitRepoRoot)
|
|
236
|
+
break;
|
|
237
|
+
const parentDir = dirname(currentDir);
|
|
238
|
+
if (parentDir === currentDir)
|
|
239
|
+
break;
|
|
240
|
+
currentDir = parentDir;
|
|
241
|
+
}
|
|
242
|
+
return dirs;
|
|
243
|
+
}
|
|
204
244
|
/**
|
|
205
245
|
* Returns true when cwd has project-local resources that must be gated by
|
|
206
246
|
* project trust: trust-requiring entries under the project config dir or the
|
|
207
|
-
* legacy cwd/.pi dir (whose resources are also discovered), or
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
247
|
+
* legacy cwd/.pi dir (whose resources are also discovered), or a project
|
|
248
|
+
* `.agents/skills` (`projectAgentsSkillDirs`, the same walk the loader uses).
|
|
249
|
+
* The user's home and every folder above it are never a project: their config
|
|
250
|
+
* dirs and .agents/skills are the user's own global resources (#2465).
|
|
211
251
|
*/
|
|
212
252
|
export function hasTrustRequiringProjectResources(cwd) {
|
|
213
253
|
const homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
if (projectCodemodeNamesExecutable(join(currentDir, CONFIG_DIR_NAME)))
|
|
221
|
-
return true;
|
|
222
|
-
while (true) {
|
|
223
|
-
const agentsSkillsDir = join(currentDir, ".agents", "skills");
|
|
224
|
-
if (agentsSkillsDir !== userAgentsSkillsDir && existsSync(agentsSkillsDir)) {
|
|
254
|
+
const currentDir = canonicalizePath(resolvePath(cwd));
|
|
255
|
+
if (!isHomeOrAboveDir(currentDir, homeDir)) {
|
|
256
|
+
const configDirs = [join(currentDir, CONFIG_DIR_NAME), join(currentDir, LEGACY_PROJECT_CONFIG_DIR_NAME)];
|
|
257
|
+
if (configDirs.some((configDir) => TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry))))) {
|
|
225
258
|
return true;
|
|
226
259
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
currentDir = parentDir;
|
|
260
|
+
if (projectCodemodeNamesExecutable(join(currentDir, CONFIG_DIR_NAME)))
|
|
261
|
+
return true;
|
|
232
262
|
}
|
|
263
|
+
return projectAgentsSkillDirs(cwd).some((dir) => existsSync(dir));
|
|
233
264
|
}
|
|
234
265
|
export class ProjectTrustStore {
|
|
235
266
|
constructor(agentDir) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export declare const TURN_DEADLINE_ENTRY_TYPE = "deadline-stop";
|
|
2
|
+
export interface TurnDeadlineSettings {
|
|
3
|
+
readonly inactivityMs: number;
|
|
4
|
+
readonly maxRuntimeMs: number;
|
|
5
|
+
}
|
|
6
|
+
/** Milliseconds the abort is given to terminalize before the lease forces settlement. */
|
|
7
|
+
export declare const TURN_DEADLINE_GRACE_MS = 10000;
|
|
8
|
+
export declare const DEFAULT_TURN_INACTIVITY_MS = 3600000;
|
|
9
|
+
export declare const MIN_TURN_INACTIVITY_MS = 60000;
|
|
10
|
+
export declare const DEFAULT_TURN_MAX_RUNTIME_MS = 21600000;
|
|
11
|
+
export type TurnDeadlineKind = "inactivity" | "max_runtime";
|
|
12
|
+
export interface TurnDeadline {
|
|
13
|
+
readonly kind: TurnDeadlineKind;
|
|
14
|
+
readonly limitMs: number;
|
|
15
|
+
readonly elapsedMs: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The recorded form of a deadline stop, durable on the session branch so a config reload
|
|
19
|
+
* or session reopen keeps suppressing goal continuations until admitted user input.
|
|
20
|
+
*/
|
|
21
|
+
export interface DeadlineStopData {
|
|
22
|
+
readonly kind: TurnDeadlineKind;
|
|
23
|
+
readonly limitMs: number;
|
|
24
|
+
readonly elapsedMs: number;
|
|
25
|
+
readonly at: number;
|
|
26
|
+
}
|
|
27
|
+
interface DeadlineStopEntry {
|
|
28
|
+
readonly type: string;
|
|
29
|
+
readonly customType?: string;
|
|
30
|
+
readonly data?: unknown;
|
|
31
|
+
readonly message?: {
|
|
32
|
+
readonly role?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The latest deadline stop on the branch, or undefined when none is pending. A
|
|
37
|
+
* deadline-stopped session suppresses every automatic continuation until admitted user
|
|
38
|
+
* input starts a new turn; the entry is read fresh so the hold survives reload and reopen.
|
|
39
|
+
* Only a user message releases the hold: the aborted assistant message of the stopped
|
|
40
|
+
* turn is persisted after the stop entry and must not clear it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function deadlineStopPendingSinceLastTurn(entries: readonly DeadlineStopEntry[]): DeadlineStopData | undefined;
|
|
43
|
+
export declare function resolveTurnDeadlineSettings(configured: {
|
|
44
|
+
readonly turn_inactivity_ms?: unknown;
|
|
45
|
+
readonly turn_max_runtime_ms?: unknown;
|
|
46
|
+
}): TurnDeadlineSettings;
|
|
47
|
+
/**
|
|
48
|
+
* The narrow AgentSession surface the tracker needs, declared here so the tracker lives
|
|
49
|
+
* with the rest of the deadline contract instead of inside agent-session.ts.
|
|
50
|
+
*/
|
|
51
|
+
export type TurnDeadlineWarning = "turn_deadline_record_failed" | "turn_deadline_abort_failed" | "turn_deadline_unsettled" | "turn_deadline_failed";
|
|
52
|
+
export interface DeadlineSessionOwner {
|
|
53
|
+
_recordDeadlineStop(deadline: TurnDeadline): void;
|
|
54
|
+
/** End the turn through the normal system abort (tools see the abort signal); resolves once the session is idle. */
|
|
55
|
+
abortTurnForDeadline(): Promise<void>;
|
|
56
|
+
/** True while a provider retry is in flight; the engine is actively recovering the turn. */
|
|
57
|
+
readonly isRetryInFlight: boolean;
|
|
58
|
+
readonly isIdle: boolean;
|
|
59
|
+
/** A deadline stop that could not complete (the entry write failed, or the turn outlived the grace). */
|
|
60
|
+
_warnDeadline(event: TurnDeadlineWarning, detail: string): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Owns the inactivity lease and the hard max-runtime cap for one turn. Armed at prompt
|
|
64
|
+
* accept, renewed only by tool execution progress and admitted user input, never by text
|
|
65
|
+
* deltas. On expiry it records the durable stop entry and ends the run through the normal
|
|
66
|
+
* system-abort path. This is the single implementation of senpi#3227 for every surface.
|
|
67
|
+
*/
|
|
68
|
+
export declare class AgentDeadlineTracker {
|
|
69
|
+
private inactivityTimer;
|
|
70
|
+
private maxRuntimeTimer;
|
|
71
|
+
private deadlineValue;
|
|
72
|
+
private startedAtMs;
|
|
73
|
+
private readonly session;
|
|
74
|
+
private readonly settings;
|
|
75
|
+
private readonly now;
|
|
76
|
+
get current(): TurnDeadline | undefined;
|
|
77
|
+
constructor(session: DeadlineSessionOwner, settings: () => TurnDeadlineSettings, now?: () => number);
|
|
78
|
+
arm(): void;
|
|
79
|
+
renew(): void;
|
|
80
|
+
disarm(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Admitted user input releases the deadline-stopped hold. Input steered into a live run counts as
|
|
83
|
+
* activity (the inactivity lease restarts) but never resets the max-runtime clock of that run.
|
|
84
|
+
*/
|
|
85
|
+
clearHold(): void;
|
|
86
|
+
private clearTimers;
|
|
87
|
+
private fire;
|
|
88
|
+
private expire;
|
|
89
|
+
}
|
|
90
|
+
export declare function buildDeadlineStopNotice(deadline: TurnDeadline): string;
|
|
91
|
+
export {};
|
|
92
|
+
//# sourceMappingURL=turn-deadline.d.ts.map
|