@hasna/loops 0.4.28 → 0.4.29
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 +84 -1
- package/README.md +226 -49
- package/dist/api/index.d.ts +20 -19
- package/dist/api/index.js +6846 -1087
- package/dist/cli/index.js +2471 -885
- package/dist/cli/safe-error-context.d.ts +1 -0
- package/dist/daemon/daemon.d.ts +1 -0
- package/dist/daemon/index.js +1786 -493
- package/dist/generated/storage-kit/index.d.ts +1 -1
- package/dist/generated/storage-kit/mode.d.ts +6 -12
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4778 -1016
- package/dist/lib/advancement.d.ts +52 -0
- package/dist/lib/agent-adapter.d.ts +20 -2
- package/dist/lib/auth/route-policy.d.ts +14 -0
- package/dist/lib/auth/tenant-auth.d.ts +38 -0
- package/dist/lib/cloud/mode.d.ts +2 -8
- package/dist/lib/cloud/storage.d.ts +1 -2
- package/dist/lib/cloud/transport.d.ts +4 -18
- package/dist/lib/errors.d.ts +43 -1
- package/dist/lib/format.d.ts +2 -2
- package/dist/lib/goal/runner.d.ts +36 -3
- package/dist/lib/health.d.ts +1 -1
- package/dist/lib/labels.d.ts +4 -0
- package/dist/lib/loop-status.d.ts +4 -0
- package/dist/lib/migration.d.ts +4 -17
- package/dist/lib/mode.d.ts +2 -5
- package/dist/lib/mode.js +27 -36
- package/dist/lib/route/index.d.ts +2 -2
- package/dist/lib/route/throttle.d.ts +7 -0
- package/dist/lib/route/types.d.ts +3 -0
- package/dist/lib/run-completion.d.ts +18 -0
- package/dist/lib/scheduler.d.ts +5 -11
- package/dist/lib/storage/contract.d.ts +15 -1
- package/dist/lib/storage/index.d.ts +1 -1
- package/dist/lib/storage/index.js +3864 -457
- package/dist/lib/storage/pg-executor.d.ts +10 -5
- package/dist/lib/storage/postgres-loop-storage.d.ts +50 -24
- package/dist/lib/storage/postgres-schema.d.ts +8 -0
- package/dist/lib/storage/postgres-schema.js +1323 -1
- package/dist/lib/storage/postgres.d.ts +3 -1
- package/dist/lib/storage/postgres.js +1353 -27
- package/dist/lib/storage/provider-credentials.d.ts +84 -0
- package/dist/lib/storage/shared-database-transfer.d.ts +136 -0
- package/dist/lib/storage/sqlite.d.ts +15 -2
- package/dist/lib/storage/sqlite.js +1299 -217
- package/dist/lib/storage/tenant-backfill-s3.d.ts +53 -0
- package/dist/lib/storage/tenant-backfill.d.ts +40 -0
- package/dist/lib/store/index.d.ts +11 -8
- package/dist/lib/store.d.ts +52 -7
- package/dist/lib/store.js +1266 -217
- package/dist/lib/templates.d.ts +11 -0
- package/dist/lib/workflow-events.d.ts +6 -0
- package/dist/lib/workflow-provenance.d.ts +8 -0
- package/dist/lib/workflow-runner.d.ts +52 -4
- package/dist/mcp/index.js +1961 -651
- package/dist/runner/index.d.ts +20 -2
- package/dist/runner/index.js +2113 -177
- package/dist/sdk/http.d.ts +211 -3
- package/dist/sdk/http.js +301 -0
- package/dist/sdk/index.d.ts +7 -2
- package/dist/sdk/index.js +1959 -711
- package/dist/serve/index.d.ts +14 -0
- package/dist/serve/index.js +12323 -1743
- package/dist/types.d.ts +64 -3
- package/docs/AUTOMATION_RUNTIME_DESIGN.md +32 -32
- package/docs/CUTOVER-RUNBOOK.md +158 -31
- package/docs/DEPLOYMENT_MODES.md +78 -56
- package/docs/RUNTIME_BOUNDARY.md +26 -26
- package/docs/SHARED-DATABASE-TRANSFER.md +95 -0
- package/docs/SHARED_KIT_EXTRACTION_INVENTORY.md +631 -0
- package/docs/TRANSCRIPT_LOOP_PATTERNS.md +3 -3
- package/docs/UNIFIED_PRODUCT_CONTRACT.md +365 -0
- package/docs/USAGE.md +143 -52
- package/docs/workflows/transcript-feedback-to-loops.json +2 -2
- package/package.json +7 -3
- package/dist/lib/storage/pg-runner-claim.d.ts +0 -40
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Loop, LoopRun } from "../types.js";
|
|
2
|
+
export declare const MAX_RETRY_DELAY_MS: number;
|
|
3
|
+
export declare const DEFAULT_CIRCUIT_BREAKER_THRESHOLD = 5;
|
|
4
|
+
export declare const CIRCUIT_BREAKER_REASON_PREFIX = "circuit breaker open";
|
|
5
|
+
export type CircuitBreakerThreshold = number | ((loop: Loop) => number | undefined);
|
|
6
|
+
export type LoopAdvancementPatch = Partial<Pick<Loop, "status" | "nextRunAt" | "retryScheduledFor">>;
|
|
7
|
+
export type LoopAdvancementPlan = {
|
|
8
|
+
kind: "none";
|
|
9
|
+
reason: "running" | "missing" | "inactive" | "archived" | "stale" | "already_applied";
|
|
10
|
+
} | {
|
|
11
|
+
kind: "update";
|
|
12
|
+
reason: "retry" | "deferred_retry" | "recurrence";
|
|
13
|
+
patch: LoopAdvancementPatch;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "circuit_breaker";
|
|
16
|
+
failures: number;
|
|
17
|
+
reason: string;
|
|
18
|
+
markerScheduledFor: string;
|
|
19
|
+
patch: LoopAdvancementPatch;
|
|
20
|
+
};
|
|
21
|
+
export declare function loopAdvancementPatchMatchesCurrent(current: Loop, patch: LoopAdvancementPatch): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Exponential retry backoff with jitter:
|
|
24
|
+
* delay = retryDelayMs * 2^(attempt-1) * (0.5 + random), capped at 6h.
|
|
25
|
+
* Provider-gated failures back off 4x harder.
|
|
26
|
+
*/
|
|
27
|
+
export declare function retryBackoffDelayMs(loop: Loop, run: LoopRun, random?: () => number): number;
|
|
28
|
+
export declare function resolveBreakerThreshold(loop: Loop, override?: CircuitBreakerThreshold): number;
|
|
29
|
+
/**
|
|
30
|
+
* Count consecutive final failures from newest-first run history.
|
|
31
|
+
*
|
|
32
|
+
* Retryable failures are neutral, a success resets the streak, and the newest
|
|
33
|
+
* circuit-breaker marker is a watermark so a manual resume starts a fresh
|
|
34
|
+
* streak.
|
|
35
|
+
*/
|
|
36
|
+
export declare function consecutiveFailureCountFromRuns(runs: readonly LoopRun[], maxAttempts?: number): number;
|
|
37
|
+
/**
|
|
38
|
+
* Pure scheduling policy shared by the local scheduler and hosted API.
|
|
39
|
+
* Callers gather current loop state, deferred retry state, and recent history,
|
|
40
|
+
* then apply the returned storage mutations using their native sync/async API.
|
|
41
|
+
*/
|
|
42
|
+
export declare function planLoopAdvancement(input: {
|
|
43
|
+
current: Loop | undefined;
|
|
44
|
+
run: LoopRun;
|
|
45
|
+
finishedAt: Date;
|
|
46
|
+
succeeded: boolean;
|
|
47
|
+
deferredRetry?: LoopRun;
|
|
48
|
+
retryIntentRun?: LoopRun;
|
|
49
|
+
recentRuns?: readonly LoopRun[];
|
|
50
|
+
retryRandom?: number;
|
|
51
|
+
circuitBreakerThreshold?: CircuitBreakerThreshold;
|
|
52
|
+
}): LoopAdvancementPlan;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type { AgentProvider, AgentSandbox, AgentTarget } from "../types.js";
|
|
1
|
+
import type { AgentAllowlistEnforcement, AgentProvider, AgentSandbox, AgentSessionContract, AgentTarget, WorkflowStep } from "../types.js";
|
|
2
2
|
export type ProviderPromptChannel = "stdin" | "argv";
|
|
3
3
|
export interface ProviderCapabilities {
|
|
4
4
|
/** Sandbox values the provider CLI accepts; empty when sandboxing is unsupported. */
|
|
5
5
|
sandbox: readonly AgentSandbox[];
|
|
6
|
+
/** Tool/command restrictions exposed by this adapter. Metadata-only means advisory, not provider-enforced. */
|
|
7
|
+
allowlist: ProviderAllowlistCapabilities;
|
|
6
8
|
/** Whether the provider runs as a durable background agent instead of a one-shot process. */
|
|
7
9
|
durable: boolean;
|
|
8
10
|
/** Whether the provider can be dispatched to a remote machine transport. */
|
|
@@ -10,6 +12,10 @@ export interface ProviderCapabilities {
|
|
|
10
12
|
/** How the prompt reaches the provider process. */
|
|
11
13
|
promptChannel: ProviderPromptChannel;
|
|
12
14
|
}
|
|
15
|
+
export interface ProviderAllowlistCapabilities {
|
|
16
|
+
tools: AgentAllowlistEnforcement;
|
|
17
|
+
commands: AgentAllowlistEnforcement;
|
|
18
|
+
}
|
|
13
19
|
export interface AgentInvocation {
|
|
14
20
|
command: string;
|
|
15
21
|
args: string[];
|
|
@@ -18,16 +24,28 @@ export interface AgentInvocation {
|
|
|
18
24
|
/** Executables of which at least one must exist besides the command itself. */
|
|
19
25
|
preflightAnyOf?: string[];
|
|
20
26
|
}
|
|
27
|
+
export interface PreparedAgentInvocation {
|
|
28
|
+
/** Invocation built from the first validated snapshot. */
|
|
29
|
+
invocation: AgentInvocation;
|
|
30
|
+
/** Rebuild only cwd-dependent fields while reusing that same snapshot. */
|
|
31
|
+
forCwd(cwd: string): AgentInvocation;
|
|
32
|
+
}
|
|
21
33
|
export interface ProviderAdapter {
|
|
22
34
|
provider: AgentProvider;
|
|
23
35
|
capabilities: ProviderCapabilities;
|
|
24
36
|
validate(target: AgentTarget, label?: string): void;
|
|
25
37
|
buildInvocation(target: AgentTarget): AgentInvocation;
|
|
38
|
+
prepareInvocation(target: AgentTarget): PreparedAgentInvocation;
|
|
26
39
|
}
|
|
27
|
-
export declare
|
|
40
|
+
export declare function effectiveAgentSandbox(target: AgentTarget): AgentSandbox | "provider-default";
|
|
41
|
+
export declare function agentSessionContract(target: AgentTarget, cwd?: string | undefined): AgentSessionContract | undefined;
|
|
42
|
+
export declare function workflowStepAgentSessionContract(step: WorkflowStep): AgentSessionContract | undefined;
|
|
43
|
+
export declare function agentSessionContractPrompt(target: AgentTarget, cwd?: string | undefined): string | undefined;
|
|
28
44
|
export declare const PROVIDER_ADAPTERS: Record<AgentProvider, ProviderAdapter>;
|
|
29
45
|
export declare const AGENT_PROVIDERS: AgentProvider[];
|
|
30
46
|
export declare function providerAdapter(provider: AgentProvider): ProviderAdapter;
|
|
47
|
+
/** Validate an untrusted or persisted agent target without relying on TypeScript-only shape guarantees. */
|
|
48
|
+
export declare function validateAgentTarget(target: unknown, label?: string): asserts target is AgentTarget;
|
|
31
49
|
export interface SpawnCaptureOptions {
|
|
32
50
|
cwd?: string;
|
|
33
51
|
env?: NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type TenantRole = "admin" | "operator" | "member" | "readonly" | "service" | "worker";
|
|
2
|
+
export type TokenKind = "api_key" | "service" | "machine";
|
|
3
|
+
export interface RoutePolicy {
|
|
4
|
+
operationId: string;
|
|
5
|
+
method: string;
|
|
6
|
+
pathTemplate: string;
|
|
7
|
+
path: RegExp;
|
|
8
|
+
scopes: readonly string[];
|
|
9
|
+
roles: readonly TenantRole[];
|
|
10
|
+
tokenKinds: readonly TokenKind[];
|
|
11
|
+
risk: "read" | "write" | "destructive" | "runner";
|
|
12
|
+
}
|
|
13
|
+
export declare const ROUTE_POLICIES: readonly RoutePolicy[];
|
|
14
|
+
export declare function routePolicy(method: string, path: string): RoutePolicy | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ApiKeyClaims } from "@hasna/contracts/auth";
|
|
2
|
+
import type { TypedQueryClient } from "../../generated/storage-kit/query.js";
|
|
3
|
+
import type { RoutePolicy, TenantRole, TokenKind } from "./route-policy.js";
|
|
4
|
+
export interface TenantAuthContext {
|
|
5
|
+
tenantId: string;
|
|
6
|
+
principalId: string;
|
|
7
|
+
requestId: string;
|
|
8
|
+
kid: string;
|
|
9
|
+
agent: string | null;
|
|
10
|
+
scopes: string[];
|
|
11
|
+
roles: TenantRole[];
|
|
12
|
+
tokenKind: TokenKind;
|
|
13
|
+
claims: ApiKeyClaims;
|
|
14
|
+
}
|
|
15
|
+
export type TenantAuthDecision = {
|
|
16
|
+
ok: true;
|
|
17
|
+
status: 200;
|
|
18
|
+
principal: TenantAuthContext;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
status: 401 | 403 | 503;
|
|
22
|
+
reason: string;
|
|
23
|
+
message: string;
|
|
24
|
+
requestId: string;
|
|
25
|
+
};
|
|
26
|
+
export declare class TenantApiAuthenticator {
|
|
27
|
+
private readonly client;
|
|
28
|
+
private readonly signingSecret;
|
|
29
|
+
private readonly now;
|
|
30
|
+
constructor(client: TypedQueryClient, signingSecret: string | Buffer, now?: () => number);
|
|
31
|
+
authenticate(headers: Headers, context: {
|
|
32
|
+
method: string;
|
|
33
|
+
path: string;
|
|
34
|
+
policy: RoutePolicy;
|
|
35
|
+
}): Promise<TenantAuthDecision>;
|
|
36
|
+
private deny;
|
|
37
|
+
private audit;
|
|
38
|
+
}
|
package/dist/lib/cloud/mode.d.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
export type StorageMode = "local" | "cloud";
|
|
2
|
-
/** Deprecated mode aliases that all normalize to `cloud`. */
|
|
3
|
-
export declare const DEPRECATED_STORAGE_MODE_ALIASES: readonly ["remote", "hybrid", "self_hosted"];
|
|
1
|
+
export type StorageMode = "local" | "self_hosted" | "cloud";
|
|
4
2
|
export type Env = Record<string, string | undefined>;
|
|
5
3
|
export interface StorageModeNormalization {
|
|
6
4
|
mode: StorageMode;
|
|
7
|
-
/** The deprecated alias that was normalized to `cloud`, if any. */
|
|
8
|
-
deprecatedAlias: string | null;
|
|
9
5
|
}
|
|
10
6
|
/**
|
|
11
|
-
* Normalize a raw storage-mode string to the
|
|
12
|
-
* Accepts deprecated aliases (`remote`, `hybrid`, `self_hosted`) and maps them
|
|
13
|
-
* to `cloud`. Throws on any other value.
|
|
7
|
+
* Normalize a raw storage-mode string to the canonical deployment vocabulary.
|
|
14
8
|
*/
|
|
15
9
|
export declare function normalizeStorageMode(value: string): StorageModeNormalization;
|
|
16
10
|
/** Upper-snake env token for an app name, e.g. `todos` -> `TODOS`. */
|
|
@@ -76,7 +76,6 @@ export type ResolveStorageClientResult = {
|
|
|
76
76
|
* `name`; when it resolves to `cloud-http` (mode=cloud/self_hosted + API_URL +
|
|
77
77
|
* API_KEY), returns a ready {@link HasnaStorageClient}. Otherwise returns
|
|
78
78
|
* `{ transport: 'local', client: null }` so the app uses its local store.
|
|
79
|
-
*
|
|
80
|
-
* read the wrong dataset).
|
|
79
|
+
* Incomplete remote configuration throws so callers never read the wrong dataset.
|
|
81
80
|
*/
|
|
82
81
|
export declare function resolveStorageClient(name: string, env?: Env, overrides?: Parameters<typeof createClientTransport>[2]): ResolveStorageClientResult;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { type Env } from "./mode.js";
|
|
2
2
|
import type { StorageMode } from "./mode.js";
|
|
3
|
-
/** Default cloud host template. `<app>` is the app slug. */
|
|
4
|
-
export declare function defaultCloudBaseUrl(name: string): string;
|
|
5
3
|
export interface ClientTransportEnvKeys {
|
|
6
4
|
/** Mode keys, in precedence order. */
|
|
7
5
|
modeKeys: string[];
|
|
@@ -18,10 +16,8 @@ export type ClientTransportKind = "local" | "cloud-http";
|
|
|
18
16
|
export interface ClientTransportResolution {
|
|
19
17
|
/** Where the client should read/write from. */
|
|
20
18
|
transport: ClientTransportKind;
|
|
21
|
-
/** Resolved
|
|
19
|
+
/** Resolved canonical deployment mode. */
|
|
22
20
|
mode: StorageMode;
|
|
23
|
-
/** Deprecated mode alias that was normalized (e.g. `self_hosted`), if any. */
|
|
24
|
-
deprecatedAlias: string | null;
|
|
25
21
|
/** Env key the mode was read from, or `"default"`. */
|
|
26
22
|
modeSource: string;
|
|
27
23
|
/** `<origin>/v1` base for the cloud API when transport is cloud-http, else null. */
|
|
@@ -32,20 +28,11 @@ export interface ClientTransportResolution {
|
|
|
32
28
|
apiKeyPresent: boolean;
|
|
33
29
|
/** Env key the API key came from, or null. */
|
|
34
30
|
apiKeySource: string | null;
|
|
35
|
-
/**
|
|
36
|
-
* True when the operator asked for cloud but the config is incomplete (no API
|
|
37
|
-
* key), so we fell back to local. Callers SHOULD treat this as an error rather
|
|
38
|
-
* than silently reading local data.
|
|
39
|
-
*/
|
|
40
|
-
misconfigured: boolean;
|
|
41
|
-
/** Human-readable warning, or null. Never contains secret values. */
|
|
42
|
-
warning: string | null;
|
|
43
31
|
}
|
|
44
32
|
/**
|
|
45
33
|
* Resolve how a client should reach an app's data given the environment.
|
|
46
34
|
*
|
|
47
|
-
*
|
|
48
|
-
* `HASNA_<NAME>_MODE`, `<NAME>_STORAGE_MODE`, `<NAME>_MODE`, else `local`.
|
|
35
|
+
* The only mode key is `HASNA_<NAME>_STORAGE_MODE`; unset means `local`.
|
|
49
36
|
*/
|
|
50
37
|
export declare function resolveClientTransport(name: string, env?: Env): ClientTransportResolution;
|
|
51
38
|
/** Thrown when a cloud HTTP request returns a non-2xx status. */
|
|
@@ -132,9 +119,8 @@ export declare function createHasnaHttpTransport(options: HasnaHttpTransportOpti
|
|
|
132
119
|
/**
|
|
133
120
|
* Convenience: resolve transport from env and, when cloud-http, build the HTTP
|
|
134
121
|
* client in one call. Returns `{ transport: 'local', resolution }` for local, or
|
|
135
|
-
* `{ transport: 'cloud-http', client, resolution }` for cloud.
|
|
136
|
-
*
|
|
137
|
-
* onto local data by accident.
|
|
122
|
+
* `{ transport: 'cloud-http', client, resolution }` for self_hosted/cloud.
|
|
123
|
+
* Incomplete remote configuration throws before a client is built.
|
|
138
124
|
*/
|
|
139
125
|
export declare function createClientTransport(name: string, env?: Env, overrides?: Partial<Pick<HasnaHttpTransportOptions, "fetchImpl" | "headers" | "timeoutMs" | "retry" | "sleepImpl">>): {
|
|
140
126
|
transport: "local";
|
package/dist/lib/errors.d.ts
CHANGED
|
@@ -13,9 +13,51 @@ export declare class LoopNotFoundError extends CodedError {
|
|
|
13
13
|
export declare class LoopArchivedError extends CodedError {
|
|
14
14
|
constructor(idOrName: string);
|
|
15
15
|
}
|
|
16
|
+
export declare class LoopAdvancementConflictError extends CodedError {
|
|
17
|
+
constructor(loopId: string, runId: string);
|
|
18
|
+
}
|
|
19
|
+
export type RunFinalizationConflictReason = "stale_claim" | "run_not_running";
|
|
20
|
+
export declare class RunFinalizationConflictError extends CodedError {
|
|
21
|
+
readonly reason: RunFinalizationConflictReason;
|
|
22
|
+
constructor(reason: RunFinalizationConflictReason, runId: string);
|
|
23
|
+
}
|
|
16
24
|
export declare class AmbiguousNameError extends CodedError {
|
|
17
25
|
constructor(name: string);
|
|
18
26
|
}
|
|
27
|
+
export type AgentExtraArgsValidationReason = "not_array" | "invalid_array" | "invalid_item" | "option_not_allowed";
|
|
28
|
+
/**
|
|
29
|
+
* Deliberately public, bounded validation metadata. API boundaries may expose
|
|
30
|
+
* this object, but must never expose the accompanying Error.message.
|
|
31
|
+
*/
|
|
32
|
+
export interface PublicValidationDetails {
|
|
33
|
+
code: "agent_extra_args_invalid";
|
|
34
|
+
reason: AgentExtraArgsValidationReason;
|
|
35
|
+
path: string;
|
|
36
|
+
index?: number;
|
|
37
|
+
option?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function publicValidationDetails(value: unknown): Readonly<PublicValidationDetails> | undefined;
|
|
19
40
|
export declare class ValidationError extends CodedError {
|
|
20
|
-
|
|
41
|
+
readonly publicDetails?: Readonly<PublicValidationDetails>;
|
|
42
|
+
constructor(message: string, publicDetails?: PublicValidationDetails);
|
|
43
|
+
}
|
|
44
|
+
/** Safely capture and re-project even forged/subclass validation errors. */
|
|
45
|
+
export declare function validationErrorPublicDetails(error: ValidationError): Readonly<PublicValidationDetails> | undefined;
|
|
46
|
+
export declare class DuplicateWorkflowEventError extends CodedError {
|
|
47
|
+
constructor(workflowRunId: string, eventType: string, stepId?: string);
|
|
48
|
+
}
|
|
49
|
+
export declare class LegacyWorkflowRunProvenanceError extends CodedError {
|
|
50
|
+
constructor(workflowRunId: string);
|
|
51
|
+
}
|
|
52
|
+
export declare class WorkflowRunDefinitionConflictError extends CodedError {
|
|
53
|
+
constructor(workflowRunId: string);
|
|
54
|
+
}
|
|
55
|
+
export declare class WorkflowRunHasLiveStepsError extends CodedError {
|
|
56
|
+
constructor();
|
|
57
|
+
}
|
|
58
|
+
export declare class WorkflowRunStepOwnershipUnverifiableError extends CodedError {
|
|
59
|
+
constructor();
|
|
60
|
+
}
|
|
61
|
+
export declare class WorkflowRunNotRunningError extends CodedError {
|
|
62
|
+
constructor();
|
|
21
63
|
}
|
package/dist/lib/format.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExecutorResult, Goal, GoalRun, Loop, LoopRun, RunReceipt,
|
|
1
|
+
import type { ExecutorResult, Goal, GoalRun, Loop, LoopRun, RunReceipt, PublicWorkflowEvent, StoredWorkflowEvent, WorkflowInvocation, WorkflowRun, WorkflowSpec, WorkflowStepRun, WorkflowWorkItem } from "../types.js";
|
|
2
2
|
export declare function redact(value: string | undefined, visible?: number): string | undefined;
|
|
3
3
|
export declare function textOutputBlocks(value: Pick<LoopRun | WorkflowStepRun, "stdout" | "stderr">, opts?: {
|
|
4
4
|
indent?: string;
|
|
@@ -14,6 +14,6 @@ export declare function publicWorkflowRun(run: WorkflowRun): Record<string, unkn
|
|
|
14
14
|
export declare function publicWorkflowInvocation(invocation: WorkflowInvocation): Record<string, unknown>;
|
|
15
15
|
export declare function publicWorkflowWorkItem(item: WorkflowWorkItem): Record<string, unknown>;
|
|
16
16
|
export declare function publicWorkflowStepRun(run: WorkflowStepRun, showOutput?: boolean): Record<string, unknown>;
|
|
17
|
-
export declare function publicWorkflowEvent(event:
|
|
17
|
+
export declare function publicWorkflowEvent(event: StoredWorkflowEvent | PublicWorkflowEvent): Record<string, unknown>;
|
|
18
18
|
export declare function publicGoal(goal: Goal): Record<string, unknown>;
|
|
19
19
|
export declare function publicGoalRun(run: GoalRun): Record<string, unknown>;
|
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { GoalExecutorResult, GoalSpec, RunGoalOptions } from "./types.js";
|
|
3
|
-
|
|
1
|
+
import type { CreateGoalInput, CreateGoalPlanNodeInput, RecordGoalEventInput } from "../store.js";
|
|
2
|
+
import type { Goal, GoalExecutorResult, GoalPlanNode, GoalRun, GoalSpec, GoalStatus, RunGoalOptions } from "./types.js";
|
|
3
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
+
export interface GoalRunnerStore {
|
|
5
|
+
findGoalByContext(context: {
|
|
6
|
+
loopRunId?: string;
|
|
7
|
+
workflowRunId?: string;
|
|
8
|
+
workflowStepId?: string;
|
|
9
|
+
sourceType?: string;
|
|
10
|
+
sourceId?: string;
|
|
11
|
+
}): MaybePromise<Goal | undefined>;
|
|
12
|
+
createGoal(input: CreateGoalInput, opts?: {
|
|
13
|
+
daemonLeaseId?: string;
|
|
14
|
+
now?: Date;
|
|
15
|
+
}): MaybePromise<Goal>;
|
|
16
|
+
requireGoal(id: string): MaybePromise<Goal>;
|
|
17
|
+
createGoalPlanNodes(goalId: string, nodes: CreateGoalPlanNodeInput[], opts?: {
|
|
18
|
+
daemonLeaseId?: string;
|
|
19
|
+
now?: Date;
|
|
20
|
+
}): MaybePromise<GoalPlanNode[]>;
|
|
21
|
+
listGoalPlanNodes(goalIdOrPlanId: string): MaybePromise<GoalPlanNode[]>;
|
|
22
|
+
updateGoalStatus(goalId: string, status: GoalStatus, opts?: {
|
|
23
|
+
daemonLeaseId?: string;
|
|
24
|
+
now?: Date;
|
|
25
|
+
}): MaybePromise<Goal>;
|
|
26
|
+
updateGoalPlanNode(goalId: string, key: string, patch: Partial<Pick<GoalPlanNode, "status" | "tokensUsed" | "timeUsedSeconds" | "ready">>, opts?: {
|
|
27
|
+
daemonLeaseId?: string;
|
|
28
|
+
now?: Date;
|
|
29
|
+
}): MaybePromise<GoalPlanNode>;
|
|
30
|
+
recordGoalEvent(input: RecordGoalEventInput, opts?: {
|
|
31
|
+
daemonLeaseId?: string;
|
|
32
|
+
now?: Date;
|
|
33
|
+
}): MaybePromise<GoalRun>;
|
|
34
|
+
}
|
|
35
|
+
export declare function runGoal(store: GoalRunnerStore, input: GoalSpec, opts?: RunGoalOptions): Promise<GoalExecutorResult>;
|
|
36
|
+
export {};
|
package/dist/lib/health.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Loop, LoopRun, LoopStatus } from "../types.js";
|
|
|
2
2
|
import type { DaemonStatus } from "../daemon/control.js";
|
|
3
3
|
import type { DoctorCheck, DoctorReport } from "./doctor.js";
|
|
4
4
|
import type { Store } from "./store.js";
|
|
5
|
-
export type RunFailureClassification = "rate_limit" | "auth" | "provider_unavailable" | "model_not_found" | "context_length" | "schema_response_format" | "node_init" | "preflight" | "route_functional" | "timeout" | "sigsegv" | "restart_interrupted" | "skipped_previous_active" | "circuit_breaker" | "unknown";
|
|
5
|
+
export type RunFailureClassification = "rate_limit" | "auth" | "provider_capacity" | "provider_unavailable" | "model_not_found" | "context_length" | "schema_response_format" | "node_init" | "preflight" | "route_functional" | "timeout" | "sigsegv" | "restart_interrupted" | "skipped_previous_active" | "circuit_breaker" | "unknown";
|
|
6
6
|
export interface RunFailureSignal {
|
|
7
7
|
classification: RunFailureClassification;
|
|
8
8
|
fingerprint: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const LOOP_LABEL_MAX_COUNT = 32;
|
|
2
|
+
export declare function normalizeLoopLabels(value: string | string[] | undefined, label?: string): string[];
|
|
3
|
+
export declare function mergeLoopLabels(current: string[] | undefined, added: string | string[]): string[];
|
|
4
|
+
export declare function removeLoopLabels(current: string[] | undefined, removed: string | string[]): string[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LoopStatus } from "../types.js";
|
|
2
|
+
export declare const LOOP_STATUSES: readonly ["active", "paused", "stopped", "expired"];
|
|
3
|
+
export declare function isLoopStatus(value: unknown): value is LoopStatus;
|
|
4
|
+
export declare function assertLoopStatus(value: unknown): asserts value is LoopStatus;
|
package/dist/lib/migration.d.ts
CHANGED
|
@@ -81,26 +81,13 @@ export interface ApplyLoopsMigrationResult {
|
|
|
81
81
|
export interface SelfHostedPlanOptions {
|
|
82
82
|
operation: "self-hosted-push" | "self-hosted-pull" | "self-hosted-migrate";
|
|
83
83
|
apiUrl?: string;
|
|
84
|
-
|
|
84
|
+
apiKey?: string;
|
|
85
|
+
timeoutMs?: number;
|
|
85
86
|
fetchImpl?: typeof fetch;
|
|
86
87
|
env?: NodeJS.ProcessEnv;
|
|
87
88
|
includeRuns?: boolean;
|
|
88
89
|
replace?: boolean;
|
|
89
90
|
}
|
|
90
|
-
export interface RunnerRegistrationOptions {
|
|
91
|
-
apiUrl?: string;
|
|
92
|
-
apiToken?: string;
|
|
93
|
-
runnerId: string;
|
|
94
|
-
machineId?: string;
|
|
95
|
-
labels?: Record<string, string>;
|
|
96
|
-
capabilities?: Record<string, unknown>;
|
|
97
|
-
fetchImpl?: typeof fetch;
|
|
98
|
-
env?: NodeJS.ProcessEnv;
|
|
99
|
-
}
|
|
100
|
-
export interface RunnerRegistrationResult {
|
|
101
|
-
ok: boolean;
|
|
102
|
-
runner: Record<string, unknown>;
|
|
103
|
-
}
|
|
104
91
|
export declare function migrationHash(value: unknown): string;
|
|
105
92
|
export declare function exportLoopsMigrationBundle(store: Store, opts?: ExportLoopsMigrationOptions): LoopsMigrationBundle;
|
|
106
93
|
export declare function validateLoopsMigrationBundle(value: unknown): LoopsMigrationBundle;
|
|
@@ -109,7 +96,8 @@ export declare function applyImportMigrationBundle(store: Store, bundle: LoopsMi
|
|
|
109
96
|
export declare function buildSelfHostedMigrationPlan(store: Store, opts: SelfHostedPlanOptions): Promise<LoopsMigrationPlan>;
|
|
110
97
|
export interface SelfHostedPushOptions {
|
|
111
98
|
apiUrl?: string;
|
|
112
|
-
|
|
99
|
+
apiKey?: string;
|
|
100
|
+
timeoutMs?: number;
|
|
113
101
|
includeRuns?: boolean;
|
|
114
102
|
replace?: boolean;
|
|
115
103
|
fetchImpl?: typeof fetch;
|
|
@@ -210,6 +198,5 @@ export interface SelfHostedPushManifest {
|
|
|
210
198
|
* endpoint upserts by id, so re-running never duplicates rows.
|
|
211
199
|
*/
|
|
212
200
|
export declare function applySelfHostedPush(store: Store, opts: SelfHostedPushOptions): Promise<SelfHostedPushResult>;
|
|
213
|
-
export declare function registerSelfHostedRunner(opts: RunnerRegistrationOptions): Promise<RunnerRegistrationResult>;
|
|
214
201
|
export declare function publicMigrationBundle(bundle: LoopsMigrationBundle): Record<string, unknown>;
|
|
215
202
|
export declare function selfHostedControlPlaneSummary(env?: NodeJS.ProcessEnv): Record<string, unknown>;
|
package/dist/lib/mode.d.ts
CHANGED
|
@@ -11,11 +11,8 @@ export interface LoopModeResolution {
|
|
|
11
11
|
}
|
|
12
12
|
export interface LoopControlPlaneConfig {
|
|
13
13
|
apiUrl?: string;
|
|
14
|
-
cloudApiUrl?: string;
|
|
15
14
|
databaseUrlPresent: boolean;
|
|
16
|
-
|
|
17
|
-
cloudAuthTokenPresent: boolean;
|
|
18
|
-
authTokenPresent: boolean;
|
|
15
|
+
apiKeyPresent: boolean;
|
|
19
16
|
}
|
|
20
17
|
export interface LoopDeploymentStatus {
|
|
21
18
|
packageVersion: string;
|
|
@@ -32,7 +29,7 @@ export interface LoopDeploymentStatus {
|
|
|
32
29
|
configured: boolean;
|
|
33
30
|
apiUrl?: string;
|
|
34
31
|
databaseUrlPresent: boolean;
|
|
35
|
-
|
|
32
|
+
apiKeyPresent: boolean;
|
|
36
33
|
};
|
|
37
34
|
runner: {
|
|
38
35
|
required: boolean;
|
package/dist/lib/mode.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// package.json
|
|
3
3
|
var package_default = {
|
|
4
4
|
name: "@hasna/loops",
|
|
5
|
-
version: "0.4.
|
|
5
|
+
version: "0.4.29",
|
|
6
6
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
7
7
|
type: "module",
|
|
8
8
|
main: "dist/index.js",
|
|
@@ -81,11 +81,14 @@ var package_default = {
|
|
|
81
81
|
"build:bin": "bun build src/cli/index.ts --compile --outfile dist/loops",
|
|
82
82
|
typecheck: "tsc --noEmit",
|
|
83
83
|
test: "bun test",
|
|
84
|
+
"check:branding": "bun test scripts/check-branding.test.mjs && bun run scripts/check-branding.mjs",
|
|
84
85
|
"test:boundary": "bun run scripts/no-private-cloud-boundary.mjs",
|
|
86
|
+
"check:contracts": "bun run scripts/check-storage-kit.mjs && bun run scripts/check-contract-conformance.mjs",
|
|
87
|
+
"check:supply-chain": "bun audit && bun audit --production && bun run check:contracts && bun run check:branding && bun pm pack --dry-run --ignore-scripts && bun run test:boundary && bun run scripts/check-packed-boundary.mjs",
|
|
85
88
|
prepare: "test -d dist || bun run build",
|
|
86
89
|
"dev:cli": "bun run src/cli/index.ts",
|
|
87
90
|
"dev:daemon": "bun run src/daemon/index.ts",
|
|
88
|
-
prepublishOnly: "bun run build && bun run typecheck && bun test && bun run test:boundary"
|
|
91
|
+
prepublishOnly: "bun run build && bun run typecheck && bun test && bun run test:boundary && bun run check:supply-chain"
|
|
89
92
|
},
|
|
90
93
|
keywords: [
|
|
91
94
|
"loops",
|
|
@@ -112,7 +115,8 @@ var package_default = {
|
|
|
112
115
|
bun: ">=1.0.0"
|
|
113
116
|
},
|
|
114
117
|
dependencies: {
|
|
115
|
-
"@
|
|
118
|
+
"@aws-sdk/client-secrets-manager": "^3.1083.0",
|
|
119
|
+
"@hasna/contracts": "0.5.2",
|
|
116
120
|
"@hasna/events": "^0.1.9",
|
|
117
121
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
118
122
|
"@openrouter/ai-sdk-provider": "2.9.1",
|
|
@@ -144,13 +148,10 @@ function packageVersion() {
|
|
|
144
148
|
|
|
145
149
|
// src/lib/mode.ts
|
|
146
150
|
var LOOP_DEPLOYMENT_MODES = ["local", "self_hosted", "cloud"];
|
|
147
|
-
var MODE_ENV_KEYS = ["
|
|
148
|
-
var API_URL_ENV_KEYS = ["
|
|
149
|
-
var
|
|
150
|
-
var
|
|
151
|
-
var API_TOKEN_ENV_KEYS = ["LOOPS_API_TOKEN", "HASNA_LOOPS_API_TOKEN"];
|
|
152
|
-
var CLOUD_TOKEN_ENV_KEYS = ["LOOPS_CLOUD_TOKEN", "HASNA_LOOPS_CLOUD_TOKEN"];
|
|
153
|
-
var TOKEN_ENV_KEYS = [...API_TOKEN_ENV_KEYS, ...CLOUD_TOKEN_ENV_KEYS];
|
|
151
|
+
var MODE_ENV_KEYS = ["HASNA_LOOPS_STORAGE_MODE"];
|
|
152
|
+
var API_URL_ENV_KEYS = ["HASNA_LOOPS_API_URL"];
|
|
153
|
+
var DATABASE_URL_ENV_KEYS = ["HASNA_LOOPS_DATABASE_URL"];
|
|
154
|
+
var API_KEY_ENV_KEYS = ["HASNA_LOOPS_API_KEY"];
|
|
154
155
|
function envValue(env, keys) {
|
|
155
156
|
for (const key of keys) {
|
|
156
157
|
const value = env[key]?.trim();
|
|
@@ -160,14 +161,14 @@ function envValue(env, keys) {
|
|
|
160
161
|
return;
|
|
161
162
|
}
|
|
162
163
|
function normalizeLoopDeploymentMode(value) {
|
|
163
|
-
const normalized = value.trim()
|
|
164
|
+
const normalized = value.trim();
|
|
164
165
|
if (normalized === "local")
|
|
165
166
|
return "local";
|
|
166
|
-
if (normalized === "self_hosted"
|
|
167
|
+
if (normalized === "self_hosted")
|
|
167
168
|
return "self_hosted";
|
|
168
|
-
if (normalized === "cloud"
|
|
169
|
+
if (normalized === "cloud")
|
|
169
170
|
return "cloud";
|
|
170
|
-
throw new Error(`unsupported
|
|
171
|
+
throw new Error(`unsupported Loops deployment mode "${value}"; expected local, self_hosted, or cloud`);
|
|
171
172
|
}
|
|
172
173
|
function resolveLoopDeploymentMode(env = process.env) {
|
|
173
174
|
const explicitMode = envValue(env, MODE_ENV_KEYS);
|
|
@@ -177,9 +178,6 @@ function resolveLoopDeploymentMode(env = process.env) {
|
|
|
177
178
|
source: explicitMode.key
|
|
178
179
|
};
|
|
179
180
|
}
|
|
180
|
-
const cloudApiUrl = envValue(env, CLOUD_API_URL_ENV_KEYS);
|
|
181
|
-
if (cloudApiUrl)
|
|
182
|
-
return { deploymentMode: "cloud", source: cloudApiUrl.key };
|
|
183
181
|
const apiUrl = envValue(env, API_URL_ENV_KEYS);
|
|
184
182
|
if (apiUrl)
|
|
185
183
|
return { deploymentMode: "self_hosted", source: apiUrl.key };
|
|
@@ -191,11 +189,8 @@ function resolveLoopDeploymentMode(env = process.env) {
|
|
|
191
189
|
function loopControlPlaneConfig(env = process.env) {
|
|
192
190
|
return {
|
|
193
191
|
apiUrl: envValue(env, API_URL_ENV_KEYS)?.value,
|
|
194
|
-
cloudApiUrl: envValue(env, CLOUD_API_URL_ENV_KEYS)?.value,
|
|
195
192
|
databaseUrlPresent: Boolean(envValue(env, DATABASE_URL_ENV_KEYS)),
|
|
196
|
-
|
|
197
|
-
cloudAuthTokenPresent: Boolean(envValue(env, CLOUD_TOKEN_ENV_KEYS)),
|
|
198
|
-
authTokenPresent: Boolean(envValue(env, TOKEN_ENV_KEYS))
|
|
193
|
+
apiKeyPresent: Boolean(envValue(env, API_KEY_ENV_KEYS))
|
|
199
194
|
};
|
|
200
195
|
}
|
|
201
196
|
function sourceOfTruthForMode(mode) {
|
|
@@ -217,7 +212,7 @@ function remoteSchedulerBackendForMode(mode, config) {
|
|
|
217
212
|
if (mode === "local")
|
|
218
213
|
return "none";
|
|
219
214
|
if (mode === "cloud")
|
|
220
|
-
return config.
|
|
215
|
+
return config.apiUrl ? "hosted_control_plane_contract" : "unconfigured";
|
|
221
216
|
if (config.databaseUrlPresent)
|
|
222
217
|
return "postgres_contract";
|
|
223
218
|
if (config.apiUrl)
|
|
@@ -265,26 +260,22 @@ function buildDeploymentStatus(opts = {}) {
|
|
|
265
260
|
const active = resolveLoopDeploymentMode(env);
|
|
266
261
|
const deploymentMode = opts.perspective ?? active.deploymentMode;
|
|
267
262
|
const config = loopControlPlaneConfig(env);
|
|
268
|
-
const
|
|
269
|
-
const apiUrl = displayControlPlaneUrl(rawApiUrl);
|
|
263
|
+
const apiUrl = displayControlPlaneUrl(config.apiUrl);
|
|
270
264
|
const controlPlaneKind = deploymentMode === "local" ? "none" : deploymentMode;
|
|
271
|
-
const deploymentAuthTokenPresent = deploymentMode === "cloud" ? config.
|
|
272
|
-
const controlPlaneConfigured = deploymentMode === "local" ? false : deploymentMode === "self_hosted" ? Boolean(config.apiUrl || config.databaseUrlPresent) : Boolean(config.
|
|
265
|
+
const deploymentAuthTokenPresent = deploymentMode === "cloud" ? config.apiKeyPresent : deploymentMode === "self_hosted" ? config.apiKeyPresent : false;
|
|
266
|
+
const controlPlaneConfigured = deploymentMode === "local" ? false : deploymentMode === "self_hosted" ? Boolean(config.apiUrl || config.databaseUrlPresent) : Boolean(config.apiUrl && deploymentAuthTokenPresent);
|
|
273
267
|
const warnings = [];
|
|
274
268
|
if (deploymentMode === "self_hosted" && !controlPlaneConfigured) {
|
|
275
|
-
warnings.push("self_hosted mode needs
|
|
269
|
+
warnings.push("self_hosted mode needs HASNA_LOOPS_API_URL or HASNA_LOOPS_DATABASE_URL before it can become authoritative");
|
|
276
270
|
}
|
|
277
271
|
if (deploymentMode === "self_hosted" && config.databaseUrlPresent && !config.apiUrl) {
|
|
278
|
-
warnings.push("
|
|
272
|
+
warnings.push("HASNA_LOOPS_DATABASE_URL selects the self_hosted storage contract; loops-runner still needs HASNA_LOOPS_API_URL to claim remote work");
|
|
279
273
|
}
|
|
280
|
-
if (deploymentMode === "cloud" && config.apiUrl
|
|
281
|
-
warnings.push("
|
|
274
|
+
if (deploymentMode === "cloud" && !config.apiUrl) {
|
|
275
|
+
warnings.push("cloud mode needs HASNA_LOOPS_API_URL before it can become ready");
|
|
282
276
|
}
|
|
283
|
-
if (deploymentMode === "cloud" &&
|
|
284
|
-
warnings.push("cloud mode
|
|
285
|
-
}
|
|
286
|
-
if (deploymentMode === "cloud" && config.cloudApiUrl && !deploymentAuthTokenPresent) {
|
|
287
|
-
warnings.push("cloud mode needs LOOPS_CLOUD_TOKEN or HASNA_LOOPS_CLOUD_TOKEN before it can become ready");
|
|
277
|
+
if (deploymentMode === "cloud" && config.apiUrl && !deploymentAuthTokenPresent) {
|
|
278
|
+
warnings.push("cloud mode needs HASNA_LOOPS_API_KEY before it can become ready");
|
|
288
279
|
}
|
|
289
280
|
if (opts.perspective && opts.perspective !== active.deploymentMode) {
|
|
290
281
|
warnings.push(`active deployment mode is ${active.deploymentMode}; this is the ${opts.perspective} perspective`);
|
|
@@ -304,7 +295,7 @@ function buildDeploymentStatus(opts = {}) {
|
|
|
304
295
|
configured: controlPlaneConfigured,
|
|
305
296
|
apiUrl,
|
|
306
297
|
databaseUrlPresent: config.databaseUrlPresent,
|
|
307
|
-
|
|
298
|
+
apiKeyPresent: deploymentAuthTokenPresent
|
|
308
299
|
},
|
|
309
300
|
runner: {
|
|
310
301
|
required: deploymentMode !== "local",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route engine: turns Hasna events, todos ready queues, and health/hygiene
|
|
3
|
-
* findings into deduped, throttled
|
|
3
|
+
* findings into deduped, throttled Loops workflow loops and todos tasks.
|
|
4
4
|
* Extracted from the CLI so commands stay thin and the SDK/daemon can reuse it.
|
|
5
5
|
*/
|
|
6
6
|
export * from "./types.js";
|
|
@@ -11,7 +11,7 @@ export { GateError, normalizeLoopTargetForStorage, normalizeWorkflowForStorage,
|
|
|
11
11
|
export { accountPoolFromOpts, normalizeAgentProvider, permissionModeFromOpts, providerAuthProfileFromOpts, providerRoutingPublic, resolveProviderRouting, roleAccountFromOpts, sandboxFromOpts, SUPPORTED_AGENT_PROVIDERS, type ProviderRoutingDecision, } from "./provider.js";
|
|
12
12
|
export { checkProviderAdmission, parseCodewithAdmissionDiagnostics, providerActiveCapFromOpts, providerAdmissionPlanFromOpts, type CodewithAdmissionDiagnostics, type ProviderAdmissionDecision, type ProviderAdmissionPlan, } from "./provider-admission.js";
|
|
13
13
|
export { prReviewRoutingDecision, type PrReviewRoutingDecision } from "./pr-review.js";
|
|
14
|
-
export { hasThrottleLimits, normalizeRoutePath, routeThrottleDecision, routeThrottleLimitsFromOpts, type RouteThrottleDecision, type RouteThrottleLimits, } from "./throttle.js";
|
|
14
|
+
export { hasThrottleLimits, normalizeRoutePath, routeThrottleDecision, routeThrottleLimitsFromInputs, routeThrottleLimitsFromOpts, type RouteThrottleDecision, type RouteThrottleLimits, } from "./throttle.js";
|
|
15
15
|
export { generatedRouteSandboxPreflight, readEventEnvelopeInput, routeEventByKind, routeGenericEvent, routeTodosTaskEvent, todosTaskRouteTemplateId, } from "./route-event.js";
|
|
16
16
|
export { drainTodosTaskRoutes, type DrainResult } from "./drain.js";
|
|
17
17
|
export { buildHygieneRouteTasks, parseHygieneChecks, taskAutoRoute, upsertRouteTasks, type HygieneCheckKind, type HygieneRouteTask, type RouteTaskSpec, type UpsertRouteTasksOptions, } from "./route-tasks.js";
|
|
@@ -26,6 +26,13 @@ export declare function routeThrottleLimitsFromOpts(opts: {
|
|
|
26
26
|
maxActivePerProjectGroup?: string;
|
|
27
27
|
maxPerProfile?: string;
|
|
28
28
|
}): RouteThrottleLimits;
|
|
29
|
+
export declare function routeThrottleLimitsFromInputs(opts: {
|
|
30
|
+
maxActive?: string;
|
|
31
|
+
maxActiveScope?: string;
|
|
32
|
+
maxActivePerProject?: string;
|
|
33
|
+
maxActivePerProjectGroup?: string;
|
|
34
|
+
maxPerProfile?: string;
|
|
35
|
+
}, data: Record<string, unknown>, metadata: Record<string, unknown>): RouteThrottleLimits;
|
|
29
36
|
export declare function hasThrottleLimits(limits: RouteThrottleLimits): boolean;
|
|
30
37
|
export declare function normalizeRoutePath(value: string | undefined): string | undefined;
|
|
31
38
|
export declare function routeProjectGroup(optsGroup: string | undefined, data: Record<string, unknown>, metadata: Record<string, unknown>): string | undefined;
|
|
@@ -29,6 +29,9 @@ export interface TodosTaskRouteOptions {
|
|
|
29
29
|
verifierIdleTimeout?: string;
|
|
30
30
|
permissionMode?: string;
|
|
31
31
|
sandbox?: string;
|
|
32
|
+
allowTool?: string[];
|
|
33
|
+
allowCommand?: string[];
|
|
34
|
+
safetyReason?: string;
|
|
32
35
|
manualBreakGlass?: boolean;
|
|
33
36
|
projectPath?: string;
|
|
34
37
|
projectGroup?: string;
|