@marcoscale98/pi-extensible-workflows 5.19.1-fork.1 → 6.2.1-fork.1
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 +77 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +365 -127
- package/dist/src/background-widget.d.ts +0 -1
- package/dist/src/background-widget.js +4 -9
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +33 -37
- package/dist/src/execution.d.ts +2 -0
- package/dist/src/execution.js +5 -5
- package/dist/src/host-delivery.js +4 -8
- package/dist/src/host-navigator.js +5 -2
- package/dist/src/host-phases.d.ts +1 -0
- package/dist/src/host-recovery.d.ts +6 -6
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +1 -1
- package/dist/src/host-view.js +17 -15
- package/dist/src/host.js +62 -87
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1523 -1630
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +7 -6
- package/dist/src/paths.js +24 -15
- package/dist/src/persistence.d.ts +1 -0
- package/dist/src/persistence.js +1 -0
- package/dist/src/pi-runtime-runner.js +4 -1
- package/dist/src/registry.d.ts +9 -7
- package/dist/src/registry.js +28 -70
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +6 -3
- package/dist/src/settings.js +36 -47
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +0 -1
- package/dist/src/trajectory.js +2 -3
- package/dist/src/types.d.ts +100 -44
- package/dist/src/types.js +4 -7
- package/dist/src/utils.d.ts +3 -2
- package/dist/src/utils.js +20 -10
- package/dist/src/validation.d.ts +8 -2
- package/dist/src/validation.js +122 -86
- package/dist/starter/index.js +8 -9
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.js +8 -7
- package/dist/subagents/index.js +654 -814
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +4 -3
- package/dist/subagents/src/contracts.js +2 -2
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +1 -2
- package/dist/subagents/src/manager.js +49 -70
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +0 -1
- package/dist/subagents/src/view.js +2 -16
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +8 -8
- package/dist/trajectory/index.js +487 -891
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +8 -8
- package/dist/trajectory/src/index.js +126 -86
- package/dist/trajectory/src/server.js +264 -25
- package/dist/trajectory/src/server.js.map +3 -3
- package/dist/trajectory/src/startup-mutex.d.ts +17 -0
- package/dist/trajectory/src/startup-mutex.js +172 -0
- package/examples/workflow-extension-template/README.md +4 -7
- package/examples/workflow-extension-template/extension.test.mjs +4 -8
- package/examples/workflow-extension-template/index.js +0 -3
- package/package.json +5 -5
- package/skills/pi-extensible-workflows/SKILL.md +10 -8
- package/src/agent-execution.ts +339 -127
- package/src/background-widget.ts +4 -10
- package/src/decoders.ts +31 -37
- package/src/execution.ts +5 -5
- package/src/host-delivery.ts +4 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +20 -26
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +18 -11
- package/src/host.ts +62 -113
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -12
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +27 -63
- package/src/runtime/agent-runner.ts +2 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +35 -47
- package/src/store.ts +18 -2
- package/src/trajectory.ts +2 -4
- package/src/types.ts +71 -30
- package/src/utils.ts +18 -9
- package/src/validation.ts +102 -79
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -7
- package/starter/roles/developer.md +0 -1
- package/starter/roles/oracle.md +0 -1
- package/starter/roles/researcher.md +0 -1
- package/starter/roles/reviewer.md +0 -1
- package/starter/roles/scout.md +0 -1
- package/subagents/README.md +8 -14
- package/subagents/src/contracts.ts +3 -4
- package/subagents/src/index.ts +2 -3
- package/subagents/src/manager.ts +45 -125
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +4 -14
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +8 -8
- package/trajectory/src/index.ts +106 -82
- package/trajectory/src/server.ts +44 -8
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -326
- package/dist/starter/roles/developer.md +0 -6
- package/dist/starter/roles/oracle.md +0 -18
- package/dist/starter/roles/researcher.md +0 -20
- package/dist/starter/roles/reviewer.md +0 -19
- package/dist/starter/roles/scout.md +0 -18
- package/examples/workflow-extension-template/roles/reviewer.md +0 -4
- package/src/roles.ts +0 -327
package/dist/src/paths.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export declare function safePart(value: string): string;
|
|
2
|
-
/**
|
|
3
|
-
* Resolves a path to its filesystem spelling, including symlink and platform aliases.
|
|
4
|
-
* A missing path safely falls back to its absolute lexical spelling rather than
|
|
5
|
-
* guessing at the identity of an object that does not exist.
|
|
6
|
-
*/
|
|
7
2
|
export declare function canonicalPath(path: string): string;
|
|
8
|
-
/** Pi names built-in extensions `builtin:<name>` instead of by a path; other extensions are files. */
|
|
9
3
|
export declare function extensionIdentity(path: string): string;
|
|
10
4
|
export declare function sameFilesystemPath(left: string, right: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* The path Pi's resource loader uses for a resource path an extension returns: trimmed, `~`, `~/` and `file://` expanded,
|
|
7
|
+
* relative to the loader cwd. Synthetic `<...>` and `builtin:` paths stay as they are.
|
|
8
|
+
*/
|
|
9
|
+
export declare function piResourcePath(path: string, cwd: string): string;
|
|
10
|
+
/** Pi attributes a loaded resource to a contributed path by lexical containment of resolved paths, without following symlinks. */
|
|
11
|
+
export declare function piResourceContains(root: string, path: string): boolean;
|
|
11
12
|
export declare function workflowProjectSettingsPath(cwd: string): string;
|
|
12
13
|
export declare function projectStorageKey(cwd: string): string;
|
|
13
14
|
export declare function projectSessionsDirectory(cwd: string, home?: string): string;
|
package/dist/src/paths.js
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { readdir } from "node:fs/promises";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
|
-
import { basename, join, resolve } from "node:path";
|
|
6
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
7
|
import { WorkflowError } from "./types.js";
|
|
7
8
|
import { isNodeError } from "./utils.js";
|
|
8
9
|
export function safePart(value) { return value.replace(/[^a-zA-Z0-9._-]/g, "_"); }
|
|
10
|
+
export function canonicalPath(path) { const absolute = resolve(path); try {
|
|
11
|
+
return realpathSync(absolute);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return absolute;
|
|
15
|
+
} }
|
|
16
|
+
export function extensionIdentity(path) { return path.startsWith("builtin:") ? path : canonicalPath(path.startsWith("file:") ? fileURLToPath(path) : path); }
|
|
17
|
+
export function sameFilesystemPath(left, right) { return canonicalPath(left) === canonicalPath(right); }
|
|
9
18
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* guessing at the identity of an object that does not exist.
|
|
19
|
+
* The path Pi's resource loader uses for a resource path an extension returns: trimmed, `~`, `~/` and `file://` expanded,
|
|
20
|
+
* relative to the loader cwd. Synthetic `<...>` and `builtin:` paths stay as they are.
|
|
13
21
|
*/
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
export function piResourcePath(path, cwd) {
|
|
23
|
+
//NOTE: Pi also rewrites Windows shell paths and `~\` on win32; those forms are not mirrored here and fail closed.
|
|
24
|
+
if (path.startsWith("<") || path.startsWith("builtin:"))
|
|
25
|
+
return path;
|
|
26
|
+
const trimmed = path.trim();
|
|
27
|
+
const expanded = trimmed === "~" ? homedir() : trimmed.startsWith("~/") ? join(homedir(), trimmed.slice(2)) : /^file:\/\//.test(trimmed) ? fileURLToPath(trimmed) : trimmed;
|
|
28
|
+
return isAbsolute(expanded) ? resolve(expanded) : resolve(cwd, expanded);
|
|
29
|
+
}
|
|
30
|
+
/** Pi attributes a loaded resource to a contributed path by lexical containment of resolved paths, without following symlinks. */
|
|
31
|
+
export function piResourceContains(root, path) {
|
|
32
|
+
const relativePath = relative(resolve(root), resolve(path));
|
|
33
|
+
return relativePath === "" || (relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));
|
|
22
34
|
}
|
|
23
|
-
/** Pi names built-in extensions `builtin:<name>` instead of by a path; other extensions are files. */
|
|
24
|
-
export function extensionIdentity(path) { return path.startsWith("builtin:") ? path : canonicalPath(path); }
|
|
25
|
-
export function sameFilesystemPath(left, right) { return canonicalPath(left) === canonicalPath(right); }
|
|
26
35
|
export function workflowProjectSettingsPath(cwd) { return join(cwd, ".pi", "pi-extensible-workflows", "settings.json"); }
|
|
27
36
|
export function projectStorageKey(cwd) {
|
|
28
37
|
const exact = resolve(cwd);
|
|
@@ -2,4 +2,5 @@ export { atomicJson, atomicWriteFile, json } from "./io.js";
|
|
|
2
2
|
export { projectStorageKey, projectSessionsDirectory, runsDirectory, listPersistedSessionIds, structuralPath, } from "./paths.js";
|
|
3
3
|
export { hasLiveSessionLease, SessionLease, acquireSessionLease, listRunIds, processAlive } from "./session-lease.js";
|
|
4
4
|
export { RunStore } from "./store.js";
|
|
5
|
+
export { decodePreparedAgentConfiguration } from "./decoders.js";
|
|
5
6
|
export { EffectiveSystemPrompt, PersistedRun, RunSummaryAgent, RunSummaryArtifacts, RunSummary, CompletedOperation, AwaitingCheckpoint, PendingWorkflowDecision, PersistedOwnershipNode, WorktreeReference, BorrowedWorktreeBinding, isPersistedRun, } from "./decoders.js";
|
package/dist/src/persistence.js
CHANGED
|
@@ -2,4 +2,5 @@ export { atomicJson, atomicWriteFile, json } from "./io.js";
|
|
|
2
2
|
export { projectStorageKey, projectSessionsDirectory, runsDirectory, listPersistedSessionIds, structuralPath, } from "./paths.js";
|
|
3
3
|
export { hasLiveSessionLease, SessionLease, acquireSessionLease, listRunIds, processAlive } from "./session-lease.js";
|
|
4
4
|
export { RunStore } from "./store.js";
|
|
5
|
+
export { decodePreparedAgentConfiguration } from "./decoders.js";
|
|
5
6
|
export { isPersistedRun, } from "./decoders.js";
|
|
@@ -164,7 +164,7 @@ export class PiRuntimeAgentRunner {
|
|
|
164
164
|
const resultTool = prepared.resultTool;
|
|
165
165
|
if (!resultTool)
|
|
166
166
|
throw new WorkflowError("INTERNAL_ERROR", "Workflow agent session is missing its workflow_result tool");
|
|
167
|
-
const preparedForSession = preparedForRuntimeRequest(prepared, request.customTools, request.signal, (value) => { structuredResult = value; }, resultTool);
|
|
167
|
+
const preparedForSession = Object.freeze({ ...preparedForRuntimeRequest(prepared, request.customTools, request.signal, (value) => { structuredResult = value; }, resultTool), ...(request.turnControl ? { turnControl: request.turnControl } : {}) });
|
|
168
168
|
const callbacks = this.#options.callbacks;
|
|
169
169
|
const handoff = this.#options.handoff;
|
|
170
170
|
let adapter;
|
|
@@ -423,6 +423,9 @@ export class PiRuntimeAgentRunner {
|
|
|
423
423
|
throwIfProgressFailed();
|
|
424
424
|
beginTurn(false);
|
|
425
425
|
throwIfTurnPolicyFailed();
|
|
426
|
+
// NOTE: A transport that already sent turnControl (Herdr, in its pane launch prompt) reports turnControlDelivered; steering its suspended local session would fail.
|
|
427
|
+
if (request.turnControl && !session.turnControlDelivered)
|
|
428
|
+
await activeSession.steer(request.turnControl);
|
|
426
429
|
await promptAndRecover(request.task, true);
|
|
427
430
|
completeTurn(hasResult() || !hasToolCall(lastAssistant), false, true);
|
|
428
431
|
if (turnPolicyFailure)
|
package/dist/src/registry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAttemptAction, JsonValue, RegisteredAgentSetupHook, WorkflowCatalog, WorkflowCatalogContext, WorkflowCatalogError, WorkflowCatalogFunction, WorkflowCatalogIndex, WorkflowCatalogModelAlias, WorkflowExtension, WorkflowExtensionSettings, WorkflowExtensionSettingsValidatorContext, WorkflowFunction, WorkflowFunctionContext, WorkflowFunctionSource, WorkflowJournal, WorkflowModelAlias, WorkflowModelAliasResolverContext
|
|
1
|
+
import type { AgentAttemptAction, JsonValue, RegisteredAgentSetupHook, RegisteredAgentPreparationHook, WorkflowCatalog, WorkflowCatalogContext, WorkflowCatalogError, WorkflowCatalogFunction, WorkflowCatalogIndex, WorkflowCatalogModelAlias, WorkflowExtension, WorkflowExtensionSettings, WorkflowExtensionSettingsValidatorContext, WorkflowFunction, WorkflowFunctionContext, WorkflowFunctionSource, WorkflowJournal, WorkflowModelAlias, WorkflowModelAliasResolverContext } from "./types.js";
|
|
2
2
|
import type { SubagentRunRequest, SubagentStatus } from "../subagents/src/contracts.js";
|
|
3
3
|
export type SubagentStatusObserver = (status: Readonly<SubagentStatus>, request: Readonly<SubagentRunRequest>) => void;
|
|
4
4
|
export declare class WorkflowRegistry {
|
|
@@ -8,6 +8,11 @@ export declare class WorkflowRegistry {
|
|
|
8
8
|
setSubagentStatusObserver(observer: SubagentStatusObserver | undefined): void;
|
|
9
9
|
observeSubagentStatus(status: Readonly<SubagentStatus>, request: Readonly<SubagentRunRequest>): void;
|
|
10
10
|
register(extension: WorkflowExtension): void;
|
|
11
|
+
/** Every registration's headline and declared module provenance, for hosts that must prove which extension made it. */
|
|
12
|
+
registrations(): ReadonlyArray<Readonly<{
|
|
13
|
+
headline: string;
|
|
14
|
+
source?: string;
|
|
15
|
+
}>>;
|
|
11
16
|
function(name: string): WorkflowFunction;
|
|
12
17
|
functions(): Readonly<Record<string, WorkflowFunction>>;
|
|
13
18
|
functionSources(): Readonly<Record<string, WorkflowFunctionSource>>;
|
|
@@ -19,10 +24,9 @@ export declare class WorkflowRegistry {
|
|
|
19
24
|
}>>;
|
|
20
25
|
validateExtensionSettings(settings: Readonly<WorkflowExtensionSettings> | undefined, context: WorkflowExtensionSettingsValidatorContext): void;
|
|
21
26
|
invokeFunction(name: string, input: unknown, context: Readonly<WorkflowFunctionContext>, path: string, journal: WorkflowJournal): Promise<JsonValue>;
|
|
27
|
+
agentPreparationHooks(): readonly RegisteredAgentPreparationHook[];
|
|
22
28
|
agentSetupHooks(): readonly RegisteredAgentSetupHook[];
|
|
23
29
|
agentAttemptActions(): Readonly<Record<string, AgentAttemptAction>>;
|
|
24
|
-
roleDirectories(): readonly string[];
|
|
25
|
-
roleDirectoryRegistrations(): readonly WorkflowRoleDirectoryRegistration[];
|
|
26
30
|
modelAliases(): readonly {
|
|
27
31
|
name: string;
|
|
28
32
|
version: string;
|
|
@@ -31,7 +35,7 @@ export declare class WorkflowRegistry {
|
|
|
31
35
|
}[];
|
|
32
36
|
resolveModelAliases(context: Readonly<WorkflowModelAliasResolverContext>, shadowed?: ReadonlySet<string>): Promise<Readonly<Record<string, string>>>;
|
|
33
37
|
}
|
|
34
|
-
export type WorkflowRegistryApi = Pick<WorkflowRegistry, "frozen" | "freeze" | "register" | "function" | "functions" | "functionSources" | "catalog" | "catalogIndex" | "catalogDetail" | "globals" | "invokeFunction" | "validateExtensionSettings" | "modelAliases" | "resolveModelAliases" | "
|
|
38
|
+
export type WorkflowRegistryApi = Pick<WorkflowRegistry, "frozen" | "freeze" | "register" | "function" | "functions" | "functionSources" | "catalog" | "catalogIndex" | "catalogDetail" | "globals" | "invokeFunction" | "validateExtensionSettings" | "modelAliases" | "resolveModelAliases" | "agentPreparationHooks" | "agentSetupHooks" | "agentAttemptActions" | "setSubagentStatusObserver" | "observeSubagentStatus" | "registrations">;
|
|
35
39
|
export declare function resetWorkflowRegistry(): void;
|
|
36
40
|
export declare function resetWorkflowRegistryIfIdle(): void;
|
|
37
41
|
export declare function beginWorkflowExtensionLoading(): void;
|
|
@@ -43,6 +47,4 @@ export declare function workflowCatalogIndex(context?: WorkflowCatalogContext):
|
|
|
43
47
|
export declare function workflowCatalogDetail(name: string, context?: WorkflowCatalogContext): WorkflowCatalogFunction | WorkflowCatalogModelAlias | WorkflowCatalogError;
|
|
44
48
|
export declare function registeredWorkflowFunctions(): Readonly<Record<string, WorkflowFunction>>;
|
|
45
49
|
export declare function registeredWorkflowFunctionSources(): Readonly<Record<string, WorkflowFunctionSource>>;
|
|
46
|
-
export
|
|
47
|
-
export declare function registeredWorkflowRoleDirectoryRegistrations(): readonly WorkflowRoleDirectoryRegistration[];
|
|
48
|
-
export type { WorkflowCatalog, WorkflowCatalogContext, WorkflowCatalogError, WorkflowCatalogFunction, WorkflowCatalogIndex, WorkflowCatalogIndexFunction, WorkflowCatalogModelAlias, WorkflowCatalogSettings, WorkflowRoleDirectoryRegistration } from "./types.js";
|
|
50
|
+
export type { WorkflowCatalog, WorkflowCatalogContext, WorkflowCatalogError, WorkflowCatalogFunction, WorkflowCatalogIndex, WorkflowCatalogIndexFunction, WorkflowCatalogModelAlias, WorkflowCatalogSettings } from "./types.js";
|
package/dist/src/registry.js
CHANGED
|
@@ -1,50 +1,11 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
1
|
import { Value } from "typebox/value";
|
|
5
2
|
import { byPriorityThenName, deepFreeze, errorCode, errorText, fail, jsonValue, MODEL_ALIAS_NAME, object, validWorkflowExtensionNamespace } from "./utils.js";
|
|
6
3
|
import { loadSettings, resolveWorkflowSettings } from "./settings.js";
|
|
7
4
|
import { validateSchema } from "./utils.js";
|
|
8
|
-
import { canonicalPath } from "./paths.js";
|
|
9
|
-
import { isCorePackageName } from "./package-identity.js";
|
|
10
5
|
const RESERVED_GLOBALS = new Set(["agent", "shell", "tools", "prompt", "checkpoint", "parallel", "pipeline", "phase", "withWorktree", "log", "args", "Promise", "JSON", "Math", "Date", "eval", "Function", "WebAssembly", "process", "require", "module", "exports", "console", "fetch", "XMLHttpRequest", "WebSocket", "performance", "crypto", "setTimeout", "setInterval", "setImmediate", "queueMicrotask", "Intl", "SharedArrayBuffer", "Atomics", "globalThis", "global", "undefined", "NaN", "Infinity", "extensions", "workflow_catalog"]);
|
|
11
6
|
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
12
7
|
const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
13
8
|
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
14
|
-
function normalizeRoleDirectory(value) {
|
|
15
|
-
try {
|
|
16
|
-
if (value instanceof URL) {
|
|
17
|
-
if (value.protocol !== "file:")
|
|
18
|
-
fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
19
|
-
return fileURLToPath(value);
|
|
20
|
-
}
|
|
21
|
-
if (typeof value === "string") {
|
|
22
|
-
if (value.startsWith("file:"))
|
|
23
|
-
return fileURLToPath(value);
|
|
24
|
-
if (isAbsolute(value))
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
fail("INVALID_METADATA", `Invalid workflow role directory: ${errorText(error)}`);
|
|
30
|
-
}
|
|
31
|
-
fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
32
|
-
}
|
|
33
|
-
function isBuiltinRoleDirectory(path) {
|
|
34
|
-
const canonical = canonicalPath(path);
|
|
35
|
-
const starterDirectory = dirname(canonical);
|
|
36
|
-
if (basename(canonical) !== "roles" || basename(starterDirectory) !== "starter")
|
|
37
|
-
return false;
|
|
38
|
-
const distributionDirectory = dirname(starterDirectory);
|
|
39
|
-
const packageRoot = basename(distributionDirectory) === "dist" ? dirname(distributionDirectory) : distributionDirectory;
|
|
40
|
-
try {
|
|
41
|
-
const metadata = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
|
42
|
-
return object(metadata) && isCorePackageName(metadata.name);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
9
|
function catalogSchema(schema, at) {
|
|
49
10
|
validateSchema(schema, at);
|
|
50
11
|
return structuredClone(schema);
|
|
@@ -53,9 +14,9 @@ export class WorkflowRegistry {
|
|
|
53
14
|
#extensions = new Set();
|
|
54
15
|
#functionSources = new Map();
|
|
55
16
|
#globals = new Map();
|
|
17
|
+
#preparationHooks = new Map();
|
|
56
18
|
#hooks = new Map();
|
|
57
19
|
#agentAttemptActions = new Map();
|
|
58
|
-
#roleDirectories = new Map();
|
|
59
20
|
#modelAliases = new Map();
|
|
60
21
|
#settingsValidators = new Set();
|
|
61
22
|
#frozen = false;
|
|
@@ -69,10 +30,11 @@ export class WorkflowRegistry {
|
|
|
69
30
|
fail("REGISTRY_FROZEN", "Workflow extension registration is closed after session_start");
|
|
70
31
|
if (object(extension) && Object.prototype.hasOwnProperty.call(extension, "workflows"))
|
|
71
32
|
fail("INVALID_METADATA", "Separate registered workflow definitions were removed; register a function with input and output schemas instead");
|
|
72
|
-
if (!object(extension) || Object.keys(extension).some((key) => !["version", "headline", "description", "source", "dependencies", "validateSettings", "functions", "modelAliases", "
|
|
33
|
+
if (!object(extension) || Object.keys(extension).some((key) => !["version", "headline", "description", "source", "dependencies", "validateSettings", "functions", "modelAliases", "agentPreparationHooks", "agentSetupHooks", "agentAttemptActions"].includes(key)) || typeof extension.version !== "string" || !SEMVER.test(extension.version) || typeof extension.headline !== "string" || !extension.headline.trim())
|
|
73
34
|
fail("INVALID_METADATA", "Workflow extensions require a semantic version and non-empty headline");
|
|
74
35
|
const functions = extension.functions ?? {};
|
|
75
36
|
const modelAliases = extension.modelAliases ?? {};
|
|
37
|
+
const agentPreparationHooks = extension.agentPreparationHooks ?? {};
|
|
76
38
|
const agentSetupHooks = extension.agentSetupHooks ?? {};
|
|
77
39
|
const agentAttemptActions = extension.agentAttemptActions ?? {};
|
|
78
40
|
const source = extension.source;
|
|
@@ -85,12 +47,8 @@ export class WorkflowRegistry {
|
|
|
85
47
|
fail("INVALID_METADATA", "Workflow extension source must be a non-empty module URL");
|
|
86
48
|
if (dependencyValues !== undefined && (!Array.isArray(dependencyValues) || dependencyValues.some((dependency) => typeof dependency !== "string" || dependency.trim() !== dependency || !PACKAGE_NAME.test(dependency)) || new Set(dependencyValues).size !== dependencyValues.length))
|
|
87
49
|
fail("INVALID_METADATA", "Workflow extension dependencies must be unique, non-empty package names");
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
fail("INVALID_METADATA", "Workflow extension roleDirectories must be an array");
|
|
91
|
-
const roleDirectories = [...new Set(Array.from(roleDirectoryValues, (value) => normalizeRoleDirectory(value)))];
|
|
92
|
-
if (!object(functions) || !object(modelAliases) || !object(agentSetupHooks) || !object(agentAttemptActions) || (Object.keys(functions).length === 0 && Object.keys(modelAliases).length === 0 && Object.keys(agentSetupHooks).length === 0 && Object.keys(agentAttemptActions).length === 0 && roleDirectories.length === 0 && validateSettings === undefined))
|
|
93
|
-
fail("INVALID_METADATA", "Workflow extensions require functions, model aliases, agent setup hooks, agent attempt actions, role directories, or a settings validator");
|
|
50
|
+
if (!object(functions) || !object(modelAliases) || !object(agentPreparationHooks) || !object(agentSetupHooks) || !object(agentAttemptActions) || (Object.keys(functions).length === 0 && Object.keys(modelAliases).length === 0 && Object.keys(agentPreparationHooks).length === 0 && Object.keys(agentSetupHooks).length === 0 && Object.keys(agentAttemptActions).length === 0 && validateSettings === undefined))
|
|
51
|
+
fail("INVALID_METADATA", "Workflow extensions require functions, model aliases, agent preparation hooks, agent setup hooks, agent attempt actions, or a settings validator");
|
|
94
52
|
const names = Object.keys(functions);
|
|
95
53
|
if (new Set(names).size !== names.length)
|
|
96
54
|
fail("GLOBAL_COLLISION", "Global name collision inside extension");
|
|
@@ -118,6 +76,14 @@ export class WorkflowRegistry {
|
|
|
118
76
|
if (this.#modelAliases.has(name))
|
|
119
77
|
fail("DUPLICATE_NAME", `Model alias already registered: ${name}`);
|
|
120
78
|
}
|
|
79
|
+
for (const [name, hook] of Object.entries(agentPreparationHooks)) {
|
|
80
|
+
if (!IDENTIFIER.test(name) || !object(hook) || Object.keys(hook).some((key) => !["priority", "optionsSchema", "prepare"].includes(key)) || typeof hook.prepare !== "function" || hook.priority !== undefined && (typeof hook.priority !== "number" || !Number.isFinite(hook.priority)))
|
|
81
|
+
fail("INVALID_METADATA", `Invalid agent preparation hook: ${name}`);
|
|
82
|
+
if (this.#preparationHooks.has(name))
|
|
83
|
+
fail("DUPLICATE_NAME", `Agent preparation hook already registered: ${name}`);
|
|
84
|
+
if (hook.optionsSchema !== undefined)
|
|
85
|
+
validateSchema(hook.optionsSchema, `${name} options schema`);
|
|
86
|
+
}
|
|
121
87
|
for (const [name, hook] of Object.entries(agentSetupHooks)) {
|
|
122
88
|
if (!IDENTIFIER.test(name) || !object(hook) || Object.keys(hook).some((key) => !["priority", "setup"].includes(key)) || typeof hook.setup !== "function" || hook.priority !== undefined && (typeof hook.priority !== "number" || !Number.isFinite(hook.priority)))
|
|
123
89
|
fail("INVALID_METADATA", `Invalid agent setup hook: ${name}`);
|
|
@@ -132,18 +98,17 @@ export class WorkflowRegistry {
|
|
|
132
98
|
if (this.#agentAttemptActions.has(name))
|
|
133
99
|
fail("DUPLICATE_NAME", `Agent attempt action already registered: ${name}`);
|
|
134
100
|
}
|
|
135
|
-
const stored = deepFreeze({ ...extension, functions, modelAliases, agentSetupHooks, agentAttemptActions
|
|
101
|
+
const stored = deepFreeze({ ...extension, functions, modelAliases, agentPreparationHooks, agentSetupHooks, agentAttemptActions });
|
|
136
102
|
if (source !== undefined)
|
|
137
103
|
for (const name of names)
|
|
138
104
|
this.#functionSources.set(name, Object.freeze({ module: source, export: "default", dependencies: Object.freeze([...dependencies]) }));
|
|
139
105
|
this.#extensions.add(stored);
|
|
140
|
-
for (const directory of roleDirectories)
|
|
141
|
-
if (!this.#roleDirectories.has(directory))
|
|
142
|
-
this.#roleDirectories.set(directory, deepFreeze({ path: directory, extension: { version: extension.version, headline: extension.headline }, ...(isBuiltinRoleDirectory(directory) ? { builtin: true } : {}) }));
|
|
143
106
|
for (const name of names)
|
|
144
107
|
this.#globals.set(name, name);
|
|
145
108
|
for (const [name, alias] of Object.entries(modelAliases))
|
|
146
109
|
this.#modelAliases.set(name, { name, version: extension.version, headline: extension.headline, resolve: alias.resolve });
|
|
110
|
+
for (const [name, hook] of Object.entries(agentPreparationHooks))
|
|
111
|
+
this.#preparationHooks.set(name, { name, priority: hook.priority ?? 10, prepare: hook.prepare, ...(hook.optionsSchema === undefined ? {} : { optionsSchema: structuredClone(hook.optionsSchema) }), ...(source === undefined ? {} : { source }) });
|
|
147
112
|
for (const [name, hook] of Object.entries(agentSetupHooks))
|
|
148
113
|
this.#hooks.set(name, { name, priority: hook.priority ?? 10, setup: hook.setup });
|
|
149
114
|
for (const [name, action] of Object.entries(agentAttemptActions))
|
|
@@ -151,6 +116,10 @@ export class WorkflowRegistry {
|
|
|
151
116
|
if (validateSettings !== undefined)
|
|
152
117
|
this.#settingsValidators.add({ headline: extension.headline, validate: validateSettings });
|
|
153
118
|
}
|
|
119
|
+
/** Every registration's headline and declared module provenance, for hosts that must prove which extension made it. */
|
|
120
|
+
registrations() {
|
|
121
|
+
return [...this.#extensions].map(({ headline, source }) => Object.freeze({ headline, ...(source === undefined ? {} : { source }) }));
|
|
122
|
+
}
|
|
154
123
|
function(name) {
|
|
155
124
|
if (!IDENTIFIER.test(name))
|
|
156
125
|
fail("MISSING_WORKFLOW", `Registered functions require an unqualified name: ${name}`);
|
|
@@ -176,7 +145,7 @@ export class WorkflowRegistry {
|
|
|
176
145
|
let source = "global settings";
|
|
177
146
|
try {
|
|
178
147
|
const resolved = context ? resolveWorkflowSettings(context.cwd, context.projectTrusted, context.globalSettingsPath) : undefined;
|
|
179
|
-
aliases = resolved?.effective.modelAliases ?? loadSettings().modelAliases;
|
|
148
|
+
aliases = resolved?.effective.modelAliases ?? (context ? loadSettings().modelAliases : resolveWorkflowSettings(process.cwd(), false).effective.modelAliases);
|
|
180
149
|
if (resolved) {
|
|
181
150
|
source = resolved.projectTrusted && [resolved.sources.modelAliases, resolved.sources.skills, resolved.sources.extensions, resolved.sources.tools].some((path) => path === resolved.projectSettingsPath) ? "trusted project settings" : "global settings";
|
|
182
151
|
settings = { concurrency: resolved.effective.concurrency, backgroundWidget: resolved.effective.backgroundWidget ?? true, modelAliases: resolved.effective.modelAliases ?? {}, skills: resolved.effective.skills ?? [], extensions: Array.isArray(resolved.effective.extensions) ? resolved.effective.extensions : [], tools: resolved.effective.tools ?? [], ...(resolved.effective.extensionSettings === undefined ? {} : { extensionSettings: resolved.effective.extensionSettings }), ...(resolved.effective.worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand: resolved.effective.worktreePostCreateCommand }), globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted: resolved.projectTrusted, sources: resolved.sources };
|
|
@@ -246,7 +215,7 @@ export class WorkflowRegistry {
|
|
|
246
215
|
validate(frozenSettings, Object.freeze({ ...context }));
|
|
247
216
|
}
|
|
248
217
|
catch (error) {
|
|
249
|
-
const location = context.source
|
|
218
|
+
const location = context.source;
|
|
250
219
|
fail("INVALID_SETTINGS", `Invalid extension settings (${headline}; ${location}): ${errorText(error)}`);
|
|
251
220
|
}
|
|
252
221
|
}
|
|
@@ -268,16 +237,11 @@ export class WorkflowRegistry {
|
|
|
268
237
|
journal.put(path, stored);
|
|
269
238
|
return structuredClone(stored);
|
|
270
239
|
}
|
|
240
|
+
agentPreparationHooks() { return [...this.#preparationHooks.values()].sort(byPriorityThenName); }
|
|
271
241
|
agentSetupHooks() {
|
|
272
242
|
return [...this.#hooks.values()].sort(byPriorityThenName);
|
|
273
243
|
}
|
|
274
244
|
agentAttemptActions() { return Object.freeze(Object.fromEntries(this.#agentAttemptActions.entries())); }
|
|
275
|
-
roleDirectories() {
|
|
276
|
-
return [...this.#roleDirectories.keys()];
|
|
277
|
-
}
|
|
278
|
-
roleDirectoryRegistrations() {
|
|
279
|
-
return [...this.#roleDirectories.values()];
|
|
280
|
-
}
|
|
281
245
|
modelAliases() { return [...this.#modelAliases.values()].sort((left, right) => left.name.localeCompare(right.name)); }
|
|
282
246
|
async resolveModelAliases(context, shadowed = new Set()) {
|
|
283
247
|
const resolved = {};
|
|
@@ -325,10 +289,10 @@ function createWorkflowRegistryApi(registry) {
|
|
|
325
289
|
validateExtensionSettings: (...args) => { registry.validateExtensionSettings(...args); },
|
|
326
290
|
modelAliases: () => registry.modelAliases(),
|
|
327
291
|
resolveModelAliases: (...args) => registry.resolveModelAliases(...args),
|
|
328
|
-
|
|
329
|
-
roleDirectoryRegistrations: () => registry.roleDirectoryRegistrations(),
|
|
292
|
+
agentPreparationHooks: () => registry.agentPreparationHooks(),
|
|
330
293
|
agentSetupHooks: () => registry.agentSetupHooks(),
|
|
331
294
|
agentAttemptActions: () => registry.agentAttemptActions(),
|
|
295
|
+
registrations: () => registry.registrations(),
|
|
332
296
|
};
|
|
333
297
|
}
|
|
334
298
|
function workflowRegistryHost() {
|
|
@@ -361,7 +325,9 @@ export function retainWorkflowRegistry() {
|
|
|
361
325
|
}
|
|
362
326
|
export function loadingRegistry() { return workflowRegistryHost().api; }
|
|
363
327
|
beginWorkflowExtensionLoading();
|
|
364
|
-
export function registerWorkflowExtension(extension) {
|
|
328
|
+
export function registerWorkflowExtension(extension) {
|
|
329
|
+
loadingRegistry().register(extension);
|
|
330
|
+
}
|
|
365
331
|
export function workflowCatalog(context) { return loadingRegistry().catalog(context); }
|
|
366
332
|
export function workflowCatalogIndex(context) { return loadingRegistry().catalogIndex(context); }
|
|
367
333
|
export function workflowCatalogDetail(name, context) { return loadingRegistry().catalogDetail(name, context); }
|
|
@@ -370,11 +336,3 @@ export function registeredWorkflowFunctionSources() {
|
|
|
370
336
|
const sources = loadingRegistry().functionSources;
|
|
371
337
|
return typeof sources === "function" ? sources() : {};
|
|
372
338
|
}
|
|
373
|
-
export function registeredWorkflowRoleDirectories() {
|
|
374
|
-
const directories = loadingRegistry().roleDirectories;
|
|
375
|
-
return typeof directories === "function" ? directories() : [];
|
|
376
|
-
}
|
|
377
|
-
export function registeredWorkflowRoleDirectoryRegistrations() {
|
|
378
|
-
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
379
|
-
return typeof registrations === "function" ? registrations() : [];
|
|
380
|
-
}
|
|
@@ -109,6 +109,8 @@ export interface RuntimeAgentRunRequest {
|
|
|
109
109
|
readonly customTools: readonly RuntimeTool[];
|
|
110
110
|
/** Explicit result schema; omitted agents use the default string workflow_result schema. */
|
|
111
111
|
readonly resultSchema?: RuntimeJsonSchema;
|
|
112
|
+
/** Transient retry or budget text delivered as a separate steered message after the task, never inside it. */
|
|
113
|
+
readonly turnControl?: string;
|
|
112
114
|
readonly run: RuntimeRunIdentity;
|
|
113
115
|
readonly agent: RuntimeAgentIdentity;
|
|
114
116
|
readonly signal: AbortSignal;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function processAlive(pid: number, startedAt?: number): Promise<boolean>;
|
|
1
|
+
export declare function processAlive(pid: number, startedAt?: number, procRoot?: string): Promise<boolean>;
|
|
2
2
|
export declare function hasLiveSessionLease(cwd: string, sessionId: string, home?: string): Promise<boolean>;
|
|
3
3
|
export declare class SessionLease {
|
|
4
4
|
#private;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { link, mkdir, open, readFile, readdir, rename, rm, stat } from "node:fs/promises";
|
|
3
4
|
import { homedir } from "node:os";
|
|
@@ -10,22 +11,55 @@ import { runsDirectory } from "./paths.js";
|
|
|
10
11
|
const SESSION_OWNER_FILE = "owner.json";
|
|
11
12
|
const SESSION_OWNER_WRITE_GRACE_MS = 30_000;
|
|
12
13
|
const RUN_CREATE_TEMP = /^\.([a-zA-Z0-9._-]+)\.(\d+)\.[0-9a-f-]+\.tmp$/;
|
|
13
|
-
|
|
14
|
+
const START_MARGIN_MS = 60_000;
|
|
15
|
+
let clockTicks;
|
|
16
|
+
function clockTickRate() {
|
|
17
|
+
if (clockTicks !== undefined)
|
|
18
|
+
return clockTicks;
|
|
19
|
+
try {
|
|
20
|
+
const rate = Number(execFileSync("getconf", ["CLK_TCK"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim());
|
|
21
|
+
clockTicks = Number.isInteger(rate) && rate > 0 ? rate : 100;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
clockTicks = 100;
|
|
25
|
+
}
|
|
26
|
+
return clockTicks;
|
|
27
|
+
}
|
|
28
|
+
// Process start in epoch ms from /proc/<pid>/stat field 22; undefined when unreadable or unparsable.
|
|
29
|
+
async function linuxProcessStart(pid, procRoot) {
|
|
30
|
+
try {
|
|
31
|
+
const text = await readFile(join(procRoot, String(pid), "stat"), "utf8");
|
|
32
|
+
// Field 2 (command name) may contain spaces and parentheses, so count fields after the last ")".
|
|
33
|
+
const close = text.lastIndexOf(")");
|
|
34
|
+
const ticks = close < 0 ? Number.NaN : Number(text.slice(close + 2).split(" ")[19]);
|
|
35
|
+
const btime = /^btime (\d+)$/m.exec(await readFile(join(procRoot, "stat"), "utf8"))?.[1];
|
|
36
|
+
if (btime === undefined || !Number.isInteger(ticks) || ticks < 0)
|
|
37
|
+
return undefined;
|
|
38
|
+
return Number(btime) * 1000 + (ticks / clockTickRate()) * 1000;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// The /proc/<pid> ctime is not a start time: procfs rebuilds the inode after cache reclaim.
|
|
45
|
+
// A live owner never starts after its stamp; a reused pid starts later.
|
|
46
|
+
export async function processAlive(pid, startedAt, procRoot = "/proc") {
|
|
14
47
|
try {
|
|
15
48
|
process.kill(pid, 0);
|
|
16
49
|
}
|
|
17
50
|
catch (error) {
|
|
18
51
|
return !isNodeError(error, "ESRCH");
|
|
19
52
|
}
|
|
20
|
-
if (startedAt
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
if (startedAt === undefined || process.platform !== "linux")
|
|
54
|
+
return true;
|
|
55
|
+
const start = await linuxProcessStart(pid, procRoot);
|
|
56
|
+
if (start !== undefined)
|
|
57
|
+
return start <= startedAt + START_MARGIN_MS;
|
|
58
|
+
try {
|
|
59
|
+
process.kill(pid, 0);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return !isNodeError(error, "ESRCH");
|
|
29
63
|
}
|
|
30
64
|
return true;
|
|
31
65
|
}
|
|
@@ -98,7 +132,7 @@ export async function acquireSessionLease(cwd, sessionId, home = homedir()) {
|
|
|
98
132
|
const path = join(directory, SESSION_OWNER_FILE);
|
|
99
133
|
for (;;) {
|
|
100
134
|
const token = randomUUID();
|
|
101
|
-
const owner = { pid: process.pid, token, startedAt:
|
|
135
|
+
const owner = { pid: process.pid, token, startedAt: Date.now() };
|
|
102
136
|
try {
|
|
103
137
|
const handle = await open(path, "wx", 0o600);
|
|
104
138
|
try {
|
package/dist/src/settings.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type ToolAnnotations } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { AgentResourcePolicy, ContextFileScope, WorkflowExtensionSettings, WorkflowSettings, WorkflowSettingsOverrides, WorkflowSettingsResolution } from "./types.js";
|
|
2
|
+
import type { AgentResourcePolicy, AgentResourceSelectorSources, ContextFileScope, WorkflowExtensionSettings, WorkflowSettings, WorkflowSettingsOverrides, WorkflowSettingsResolution } from "./types.js";
|
|
3
3
|
export { workflowProjectSettingsPath } from "./paths.js";
|
|
4
4
|
export declare const DEFAULT_SETTINGS: Readonly<WorkflowSettings>;
|
|
5
5
|
export declare function workflowSettingsPath(agentDir?: string): string;
|
|
6
6
|
export declare function validateSelectorList(value: unknown, path: string, kind: "skills" | "extensions" | "tools", errorCode?: "INVALID_SETTINGS" | "INVALID_METADATA", normalizeExtensions?: boolean): readonly string[] | undefined;
|
|
7
|
-
export declare function validateContextFileScopes(value: unknown,
|
|
7
|
+
export declare function validateContextFileScopes(value: unknown, sourcePath: string): readonly ContextFileScope[] | undefined;
|
|
8
8
|
export declare function validateWorkflowExtensionSettings(value: unknown, settingsPath: string, errorCode?: "INVALID_SETTINGS" | "INVALID_METADATA"): WorkflowExtensionSettings | undefined;
|
|
9
9
|
export declare function loadSettings(path?: string): Readonly<WorkflowSettings>;
|
|
10
10
|
/**
|
|
@@ -20,7 +20,10 @@ export declare function workflowToolExposure(name: string, codemodeTools: Workfl
|
|
|
20
20
|
/** The `codemodeTools` setting at load time. Invalid settings keep the default; launches report them. */
|
|
21
21
|
export declare function loadCodemodeToolsSetting(agentDir?: string): WorkflowSettings["codemodeTools"];
|
|
22
22
|
export declare function loadSettingsOverrides(path: string): Readonly<WorkflowSettingsOverrides>;
|
|
23
|
-
export declare function resolveWorkflowSettings(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): WorkflowSettingsResolution
|
|
23
|
+
export declare function resolveWorkflowSettings(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): WorkflowSettingsResolution & {
|
|
24
|
+
selectorSources: AgentResourceSelectorSources;
|
|
25
|
+
};
|
|
24
26
|
export declare function validateModelAliasAvailability(aliases: Readonly<Record<string, string>>, names: readonly string[], availableModels: ReadonlySet<string>, knownModels: ReadonlySet<string>, settingsPath?: string): void;
|
|
25
27
|
export declare function resolveAgentResourcePolicy(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): AgentResourcePolicy;
|
|
26
28
|
export declare function saveModelAliases(path?: string, aliases?: Readonly<Record<string, string>>): void;
|
|
29
|
+
export declare function canonicalExtensionSelector(selector: string, cwd: string): string;
|