@humwick/runner 0.0.0-stage → 0.130.0
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/LICENSE +167 -0
- package/agent-bin/agent-relay-browser +10 -0
- package/agent-bin/humwick +10 -0
- package/hooks/fff-search-redirect/fff-search-redirect.cjs +161 -0
- package/hooks/fff-search-redirect/hooks.json +13 -0
- package/hooks/rg-r-guard/hooks.json +18 -0
- package/hooks/rg-r-guard/rg-r-guard.cjs +144 -0
- package/hooks/web-tools-redirect/hooks.json +13 -0
- package/hooks/web-tools-redirect/web-tools-redirect.cjs +84 -0
- package/node_modules/@humwick/offload/LICENSE +167 -0
- package/node_modules/@humwick/offload/README.md +57 -0
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +18 -0
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/async-runner.js +158 -0
- package/node_modules/@humwick/offload/dist/async-runner.js.map +1 -0
- package/node_modules/@humwick/offload/dist/envelope.d.ts +25 -0
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/envelope.js +80 -0
- package/node_modules/@humwick/offload/dist/envelope.js.map +1 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +10 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +59 -0
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +1 -0
- package/node_modules/@humwick/offload/dist/index.d.ts +5 -0
- package/node_modules/@humwick/offload/dist/index.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/index.js +5 -0
- package/node_modules/@humwick/offload/dist/index.js.map +1 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +62 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +295 -0
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +1 -0
- package/node_modules/@humwick/offload/dist/types.d.ts +111 -0
- package/node_modules/@humwick/offload/dist/types.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/types.js +176 -0
- package/node_modules/@humwick/offload/dist/types.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +23 -0
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-pool.js +435 -0
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +2 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.js +27 -0
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +1 -0
- package/node_modules/@humwick/offload/dist/worker.d.ts +5 -0
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +1 -0
- package/node_modules/@humwick/offload/dist/worker.js +1 -0
- package/node_modules/@humwick/offload/dist/worker.js.map +1 -0
- package/node_modules/@humwick/offload/package.json +46 -0
- package/node_modules/@humwick/offload/src/async-runner.ts +170 -0
- package/node_modules/@humwick/offload/src/envelope.ts +92 -0
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +49 -0
- package/node_modules/@humwick/offload/src/index.ts +4 -0
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +334 -0
- package/node_modules/@humwick/offload/src/types.ts +235 -0
- package/node_modules/@humwick/offload/src/worker-pool.ts +463 -0
- package/node_modules/@humwick/offload/src/worker-runtime.ts +32 -0
- package/node_modules/@humwick/offload/src/worker.ts +5 -0
- package/package.json +47 -4
- package/plugins/browser/skills/playwright-cli/SKILL.md +53 -0
- package/plugins/claude/.claude-plugin/plugin.json +8 -0
- package/plugins/claude/claude-statusline-probe.cjs +144 -0
- package/plugins/claude/hooks/gate-run-guard.provisioned.mjs +914 -0
- package/plugins/claude/hooks/gate-run-guard.sh +9 -0
- package/plugins/claude/hooks/gate-run-guard.ts +237 -0
- package/plugins/claude/hooks/hooks.json +158 -0
- package/plugins/claude/hooks/kill-guard.provisioned.mjs +7027 -0
- package/plugins/claude/hooks/kill-guard.sh +341 -0
- package/plugins/claude/hooks/kill-guard.ts +337 -0
- package/plugins/claude/hooks/permission-request.sh +22 -0
- package/plugins/claude/hooks/post-compact.sh +10 -0
- package/plugins/claude/hooks/post-tool-use.sh +8 -0
- package/plugins/claude/hooks/pre-compact.sh +12 -0
- package/plugins/claude/hooks/read-only-bash-guard.sh +587 -0
- package/plugins/claude/hooks/relay-status.sh +373 -0
- package/plugins/claude/hooks/session-end.sh +26 -0
- package/plugins/claude/hooks/session-start.sh +31 -0
- package/plugins/claude/hooks/shell-trust.ts +28 -0
- package/plugins/claude/hooks/stop-failure.sh +47 -0
- package/plugins/claude/hooks/stop.sh +48 -0
- package/plugins/claude/hooks/subagent-guard.cjs +26 -0
- package/plugins/claude/hooks/subagent-skill-policy.cjs +160 -0
- package/plugins/claude/hooks/subagent-start.sh +13 -0
- package/plugins/claude/hooks/subagent-stop.sh +19 -0
- package/plugins/claude/hooks/user-prompt-submit.sh +18 -0
- package/plugins/claude/hooks/wait-loop-guard.ts +120 -0
- package/plugins/claude/monitors/monitors.json +7 -0
- package/plugins/claude/monitors/relay-monitor.provisioned.mjs +786 -0
- package/plugins/claude/monitors/relay-monitor.ts +58 -0
- package/plugins/claude/skills/disconnect/SKILL.md +16 -0
- package/plugins/claude/skills/label/SKILL.md +23 -0
- package/plugins/claude/skills/message/SKILL.md +40 -0
- package/plugins/claude/skills/pair/SKILL.md +30 -0
- package/plugins/claude/skills/react/SKILL.md +18 -0
- package/plugins/claude/skills/read-message/SKILL.md +24 -0
- package/plugins/claude/skills/reply/SKILL.md +44 -0
- package/plugins/claude/skills/send-claimable/SKILL.md +39 -0
- package/plugins/claude/skills/slice-first/SKILL.md +32 -0
- package/plugins/claude/skills/slice-first/references/plan-recipe.md +46 -0
- package/plugins/claude/skills/status/SKILL.md +16 -0
- package/plugins/claude/skills/tags/SKILL.md +25 -0
- package/plugins/claude/skills/task/SKILL.md +28 -0
- package/plugins/claude-output-style/.claude-plugin/plugin.json +5 -0
- package/plugins/claude-output-style/output-styles/agent-relay.md +5 -0
- package/plugins/codex/skills/disconnect/SKILL.md +15 -0
- package/plugins/codex/skills/guide/SKILL.md +15 -0
- package/plugins/codex/skills/label/SKILL.md +22 -0
- package/plugins/codex/skills/message/SKILL.md +40 -0
- package/plugins/codex/skills/pair/SKILL.md +29 -0
- package/plugins/codex/skills/react/SKILL.md +17 -0
- package/plugins/codex/skills/read-message/SKILL.md +23 -0
- package/plugins/codex/skills/reply/SKILL.md +44 -0
- package/plugins/codex/skills/send-claimable/SKILL.md +39 -0
- package/plugins/codex/skills/slice-first/SKILL.md +32 -0
- package/plugins/codex/skills/slice-first/references/plan-recipe.md +46 -0
- package/plugins/codex/skills/status/SKILL.md +15 -0
- package/plugins/codex/skills/tags/SKILL.md +24 -0
- package/plugins/codex/skills/task/SKILL.md +28 -0
- package/src/adapter.ts +1061 -0
- package/src/adapters/__fixtures__/codex-app-server.ts +47 -0
- package/src/adapters/claude-api.ts +243 -0
- package/src/adapters/claude-context-command.ts +16 -0
- package/src/adapters/claude-credentials.ts +22 -0
- package/src/adapters/claude-delivery-lanes.ts +28 -0
- package/src/adapters/claude-delivery.ts +166 -0
- package/src/adapters/claude-effort.ts +94 -0
- package/src/adapters/claude-exit.ts +290 -0
- package/src/adapters/claude-headless-mirror.ts +88 -0
- package/src/adapters/claude-headless-refusal.ts +51 -0
- package/src/adapters/claude-headless.ts +617 -0
- package/src/adapters/claude-monitor-delivery-plan.ts +317 -0
- package/src/adapters/claude-permission.ts +243 -0
- package/src/adapters/claude-prompt-gates.ts +269 -0
- package/src/adapters/claude-prompt-hook.ts +145 -0
- package/src/adapters/claude-session-capture.ts +293 -0
- package/src/adapters/claude-session-probe.ts +414 -0
- package/src/adapters/claude-shutdown.ts +190 -0
- package/src/adapters/claude-status-detectors.ts +426 -0
- package/src/adapters/claude-subagents.ts +93 -0
- package/src/adapters/claude-submit-confirmation.ts +287 -0
- package/src/adapters/claude-tmux.ts +548 -0
- package/src/adapters/claude-token-usage-capture.ts +253 -0
- package/src/adapters/claude-tool-policy.ts +165 -0
- package/src/adapters/claude-transcript-tail.ts +193 -0
- package/src/adapters/claude-transcript.ts +812 -0
- package/src/adapters/claude.ts +1097 -0
- package/src/adapters/codex-agent-message-capture.ts +189 -0
- package/src/adapters/codex-app-server.ts +70 -0
- package/src/adapters/codex-client.ts +362 -0
- package/src/adapters/codex-effective-config.ts +87 -0
- package/src/adapters/codex-launch-config.ts +153 -0
- package/src/adapters/codex-rollout-context.ts +272 -0
- package/src/adapters/codex-skill-discovery.ts +18 -0
- package/src/adapters/codex-token-usage-capture.ts +163 -0
- package/src/adapters/codex-tool-output.ts +53 -0
- package/src/adapters/codex-turn-outcome.ts +27 -0
- package/src/adapters/codex-version.ts +64 -0
- package/src/adapters/codex.ts +1442 -0
- package/src/adapters/incremental-file.ts +110 -0
- package/src/adapters/opencode-api.ts +71 -0
- package/src/adapters/opencode-context.ts +277 -0
- package/src/adapters/opencode-model-registry.ts +126 -0
- package/src/adapters/opencode-provider.ts +169 -0
- package/src/adapters/opencode-server-retry.ts +50 -0
- package/src/adapters/opencode-soft-steer.ts +302 -0
- package/src/adapters/opencode-spawn.ts +145 -0
- package/src/adapters/opencode-subscribe.ts +62 -0
- package/src/adapters/opencode-token-usage-capture.ts +187 -0
- package/src/adapters/opencode-tool-events.ts +302 -0
- package/src/adapters/opencode-tools-config.ts +17 -0
- package/src/adapters/opencode.ts +2264 -0
- package/src/adapters/project-shell.ts +80 -0
- package/src/adapters/provider-launch-config.ts +21 -0
- package/src/adapters/provider-quota-harvest.ts +127 -0
- package/src/adapters/service-access.ts +153 -0
- package/src/adapters/tmux-offload.ts +207 -0
- package/src/adapters/tool-summary.ts +214 -0
- package/src/agent-redact.ts +75 -0
- package/src/agent-runner/commands/dispatch.ts +185 -0
- package/src/agent-runner/commands/lifecycle.ts +84 -0
- package/src/agent-runner/commands/prompt.ts +284 -0
- package/src/agent-runner/credentials.ts +208 -0
- package/src/agent-runner/http-liveness.ts +85 -0
- package/src/agent-runner/lifecycle-milestones.ts +207 -0
- package/src/agent-runner/lifecycle-phase.ts +80 -0
- package/src/agent-runner/registration.ts +163 -0
- package/src/agent-runner/runner-context.ts +158 -0
- package/src/agent-runner/session-mirror.ts +278 -0
- package/src/agent-runner/task-claims.ts +116 -0
- package/src/agent-runner/token-usage-coverage.ts +54 -0
- package/src/agent-runner/turn-report.ts +269 -0
- package/src/api-budget-accounting.ts +91 -0
- package/src/area-memory.ts +255 -0
- package/src/artifact-path-materialize.ts +52 -0
- package/src/async-guard.ts +35 -0
- package/src/attachment-cache-sweep.ts +31 -0
- package/src/attachment-cache.ts +195 -0
- package/src/background-script-reconcile.ts +106 -0
- package/src/bounded-log.ts +59 -0
- package/src/bounded-spawn-sync.ts +18 -0
- package/src/busy-reconciler.ts +282 -0
- package/src/claim-tracker.ts +140 -0
- package/src/claude-settings.ts +167 -0
- package/src/claude-workspace-trust.ts +289 -0
- package/src/config.ts +547 -0
- package/src/context-boundary.ts +101 -0
- package/src/context-command.ts +151 -0
- package/src/context-probe-state.ts +6 -0
- package/src/continuation-archive.ts +179 -0
- package/src/control-server.ts +730 -0
- package/src/delivery-policy.ts +9 -0
- package/src/delivery-result.ts +24 -0
- package/src/effort-command.ts +125 -0
- package/src/empty-turn.ts +816 -0
- package/src/free-port.ts +25 -0
- package/src/fs-offload.ts +26 -0
- package/src/gate-run-config.ts +35 -0
- package/src/gate-run-lease-probe.ts +38 -0
- package/src/host-asset-plan.ts +450 -0
- package/src/host-claude-config-worker.ts +34 -0
- package/src/host-sources.ts +40 -0
- package/src/inbox-carrier.ts +7 -0
- package/src/index.ts +386 -0
- package/src/launch-assembly.ts +955 -0
- package/src/liveness.ts +105 -0
- package/src/logger.ts +108 -0
- package/src/mcp-outbox.ts +204 -0
- package/src/mcp-preflight.ts +86 -0
- package/src/mcp-source-selection.ts +54 -0
- package/src/mcp-tool-deferral.ts +35 -0
- package/src/mcp-vault-env.ts +25 -0
- package/src/message-body-config/config.ts +16 -0
- package/src/message-body-config.ts +1 -0
- package/src/mid-turn-delivery.ts +43 -0
- package/src/native-memory-lever.ts +329 -0
- package/src/native-self-resume.ts +244 -0
- package/src/outbox.ts +453 -0
- package/src/park-post.ts +17 -0
- package/src/pending-permission-store.ts +148 -0
- package/src/process-meta.ts +9 -0
- package/src/profile-home.ts +419 -0
- package/src/profile-projection.ts +411 -0
- package/src/project-hooks.ts +65 -0
- package/src/project-instructions.ts +119 -0
- package/src/provider-double.ts +59 -0
- package/src/provider-hold.ts +228 -0
- package/src/provider-home-marker.ts +3 -0
- package/src/provider-home-tombstone.ts +4 -0
- package/src/provider-input-queue.ts +134 -0
- package/src/provider-integration.ts +93 -0
- package/src/provider-processes.ts +17 -0
- package/src/provider-submit-failure.ts +15 -0
- package/src/providers.ts +35 -0
- package/src/provisioning.ts +703 -0
- package/src/question-decision.ts +18 -0
- package/src/quota.ts +22 -0
- package/src/rate-limit.ts +351 -0
- package/src/refused-prompt-store.ts +36 -0
- package/src/relay-injection-events.ts +161 -0
- package/src/relay-instructions.ts +107 -0
- package/src/relay-mcp-proxy.long-turn-e2e.ts +183 -0
- package/src/relay-mcp-proxy.ts +704 -0
- package/src/relay-mcp.ts +583 -0
- package/src/reply-obligation-cache.ts +211 -0
- package/src/response-capture-report.ts +111 -0
- package/src/response-routing.ts +6 -0
- package/src/runner-core.ts +2955 -0
- package/src/runner-exit.ts +12 -0
- package/src/runner-helpers.ts +582 -0
- package/src/runner-insights.ts +105 -0
- package/src/runner-mcp-proxy.ts +46 -0
- package/src/runner-spawn-config.ts +68 -0
- package/src/runner-stale-scratch-sweep.ts +21 -0
- package/src/runner.ts +9 -0
- package/src/runtime-token-file.ts +45 -0
- package/src/runtime-token-remint.ts +56 -0
- package/src/service-access.ts +88 -0
- package/src/session-destroy.ts +27 -0
- package/src/session-insights.ts +118 -0
- package/src/session-scratch.ts +296 -0
- package/src/spawn-input.ts +147 -0
- package/src/startup-config-fetch.ts +48 -0
- package/src/subreaper.ts +113 -0
- package/src/template-resolver.ts +29 -0
- package/src/test-run-dedup.ts +435 -0
- package/src/transcript-context.ts +99 -0
- package/src/transport-errors.ts +48 -0
- package/src/turn-opener.ts +55 -0
- package/src/turn-reconcile-event.ts +88 -0
- package/src/vault-proxy-hop.ts +99 -0
- package/src/version.ts +44 -0
- package/src/web-tool-call-proxy.ts +128 -0
- package/src/web-tools.ts +59 -0
- package/README.md +0 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ActiveTurnMessageDelivery, ProviderAdapter, ProviderConfig } from "./adapter";
|
|
2
|
+
|
|
3
|
+
export function resolveActiveTurnMessageDelivery(config: ProviderConfig | undefined, adapterDefault: ActiveTurnMessageDelivery): ActiveTurnMessageDelivery {
|
|
4
|
+
return config?.activeTurnMessageDelivery ?? adapterDefault;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function supportsActiveTurnSteer(adapter: ProviderAdapter, config?: ProviderConfig): boolean {
|
|
8
|
+
return adapter.supportsMidTurnSteer === true && resolveActiveTurnMessageDelivery(config, "steer") === "steer";
|
|
9
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Message } from "@humwick/sdk";
|
|
2
|
+
import type { DeliverResult } from "./adapter";
|
|
3
|
+
|
|
4
|
+
/** Normalize adapter acceptance, consumption and failure outcomes. */
|
|
5
|
+
export function normalizeDeliverResult(
|
|
6
|
+
result: void | number[] | DeliverResult,
|
|
7
|
+
deliverable: Message[],
|
|
8
|
+
): { acknowledgedIds: number[]; terminalFailureIds: number[]; awaitingConsumptionIds: number[]; providerHeldIds: number[] } {
|
|
9
|
+
if (result === undefined) return { acknowledgedIds: deliverable.map((message) => message.id), terminalFailureIds: [], awaitingConsumptionIds: [], providerHeldIds: [] };
|
|
10
|
+
if (Array.isArray(result)) return { acknowledgedIds: result, terminalFailureIds: [], awaitingConsumptionIds: [], providerHeldIds: [] };
|
|
11
|
+
// #2397 round 4 review (F5) — adapter.ts documents terminalFailureIds as a subset of the ids NOT
|
|
12
|
+
// in acknowledgedIds, but that's only true because every adapter today always reports both
|
|
13
|
+
// together. Enforce it here too: a future adapter that reports only terminalFailureIds (omitting
|
|
14
|
+
// acknowledgedIds) must not have this default silently ack its own failures.
|
|
15
|
+
const terminalFailureIds = result.terminalFailureIds ?? [];
|
|
16
|
+
const providerHeldIds = result.providerHeldIds ?? [];
|
|
17
|
+
return {
|
|
18
|
+
acknowledgedIds: result.acknowledgedIds ?? deliverable.filter((message) => !terminalFailureIds.includes(message.id) && !providerHeldIds.includes(message.id)).map((message) => message.id),
|
|
19
|
+
providerHeldIds,
|
|
20
|
+
terminalFailureIds,
|
|
21
|
+
// #2539 Phase B — accepted-but-unconsumed soft-steer ids: recorded, not settled (see settleMessageDelivery).
|
|
22
|
+
awaitingConsumptionIds: result.awaitingConsumptionIds ?? [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { effortStatePending, errMessage, isRecord, type AgentEffortState } from "@humwick/sdk";
|
|
2
|
+
import type { ManagedProcess, ProviderAdapter } from "./adapter";
|
|
3
|
+
import { fireAndForget } from "./async-guard";
|
|
4
|
+
import type { ProviderInputTicket } from "./provider-input-queue";
|
|
5
|
+
|
|
6
|
+
/** #2950 — this process can switch effort: the adapter can switch and its launch allows it. */
|
|
7
|
+
export function runnerEffortSwitch(adapter: ProviderAdapter, process: ManagedProcess | undefined): boolean {
|
|
8
|
+
return Boolean(adapter.setEffort) && process?.meta?.effortSwitch === true;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type EffortHttp = { request(method: string, path: string, body?: unknown): Promise<Response> };
|
|
12
|
+
|
|
13
|
+
/** The agent's desired/observed effort record, or undefined when nobody ever changed it. */
|
|
14
|
+
export async function readEffortState(http: EffortHttp, agentId: string): Promise<AgentEffortState | undefined> {
|
|
15
|
+
const response = await http.request("GET", `/api/agents/${encodeURIComponent(agentId)}/effort`);
|
|
16
|
+
if (!response.ok) throw new Error(`effort state read HTTP ${response.status}`);
|
|
17
|
+
const body: unknown = await response.json();
|
|
18
|
+
return isRecord(body) && isRecord(body.state) ? body.state as unknown as AgentEffortState : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface EffortObservedReport { instanceId: string; version: number; effort: string; previousEffort: string | null }
|
|
22
|
+
|
|
23
|
+
export async function reportEffortObserved(http: EffortHttp, agentId: string, report: EffortObservedReport): Promise<void> {
|
|
24
|
+
const response = await http.request("POST", `/api/agents/${encodeURIComponent(agentId)}/effort/observed`, report);
|
|
25
|
+
if (!response.ok) throw new Error(`effort report HTTP ${response.status}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface EffortReconcilerDeps {
|
|
29
|
+
http(): EffortHttp;
|
|
30
|
+
agentId(): string;
|
|
31
|
+
/** The runner's launch options: its incarnation, adapter, and the effort it runs at (restart + heartbeat read it). */
|
|
32
|
+
runner(): { instanceId: string; adapter: ProviderAdapter; effort?: string };
|
|
33
|
+
process(): ManagedProcess | undefined;
|
|
34
|
+
/** Publish the confirmed level now (heartbeat). */
|
|
35
|
+
publish(): void;
|
|
36
|
+
hasProviderTurn(): boolean;
|
|
37
|
+
reserveInput(): ProviderInputTicket;
|
|
38
|
+
log(message: string): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A bus event announcing a new desired version (the audit row's `command.pending`). */
|
|
42
|
+
export function isEffortNotice(eventType: string, data: unknown): boolean {
|
|
43
|
+
return eventType === "command.pending" && isRecord(data) && isRecord(data.command) && data.command.type === "agent.setEffort";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* #2984 — converges the provider on Relay's desired effort version between turns. Triggered by the
|
|
48
|
+
* runner's existing edges (adoption, registration, the HTTP liveness pass, a bus notice, turn end);
|
|
49
|
+
* a failed read or report leaves the version pending for the next trigger. No timers of its own.
|
|
50
|
+
*/
|
|
51
|
+
export class EffortReconciler {
|
|
52
|
+
private running = false;
|
|
53
|
+
private again = false;
|
|
54
|
+
private done: Promise<void> = Promise.resolve();
|
|
55
|
+
/** The last read left work to do, so a turn end is worth a pass. */
|
|
56
|
+
private pending = false;
|
|
57
|
+
/** The version this runner switched, kept so a retried report still names the level it replaced. */
|
|
58
|
+
private switched?: { version: number; previousEffort: string | null };
|
|
59
|
+
|
|
60
|
+
constructor(private readonly deps: EffortReconcilerDeps) {}
|
|
61
|
+
|
|
62
|
+
trigger(): void {
|
|
63
|
+
if (this.running) { this.again = true; return; }
|
|
64
|
+
this.running = true;
|
|
65
|
+
this.done = fireAndForget("effort reconcile", () => this.loop());
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Resolves once the current reconcile, if any, has finished. */
|
|
69
|
+
idle(): Promise<void> {
|
|
70
|
+
return this.done;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
turnCleared(): void {
|
|
74
|
+
if (this.pending) this.trigger();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private async loop(): Promise<void> {
|
|
78
|
+
try {
|
|
79
|
+
do {
|
|
80
|
+
this.again = false;
|
|
81
|
+
await this.pass().catch((error) => this.deps.log(`effort reconcile: ${errMessage(error)}`));
|
|
82
|
+
} while (this.again);
|
|
83
|
+
} finally {
|
|
84
|
+
this.running = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** A replacement runner verifies its predecessor's observation instead of trusting it. */
|
|
89
|
+
private needsWork(state: AgentEffortState | undefined): state is AgentEffortState {
|
|
90
|
+
return Boolean(state) && (effortStatePending(state!) || state!.observedInstanceId !== this.deps.runner().instanceId);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private read(): Promise<AgentEffortState | undefined> {
|
|
94
|
+
return readEffortState(this.deps.http(), this.deps.agentId());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private async pass(): Promise<void> {
|
|
98
|
+
const runner = this.deps.runner();
|
|
99
|
+
if (!runnerEffortSwitch(runner.adapter, this.deps.process())) return;
|
|
100
|
+
this.pending = this.needsWork(await this.read());
|
|
101
|
+
if (!this.pending || this.deps.hasProviderTurn()) return;
|
|
102
|
+
const ticket = this.deps.reserveInput();
|
|
103
|
+
try {
|
|
104
|
+
await ticket.turn;
|
|
105
|
+
// A turn that started ahead of this slot keeps the change pending until it ends too.
|
|
106
|
+
if (this.deps.hasProviderTurn()) return;
|
|
107
|
+
const state = await this.read();
|
|
108
|
+
this.pending = this.needsWork(state);
|
|
109
|
+
if (!this.needsWork(state)) return;
|
|
110
|
+
const current = runner.effort ?? null;
|
|
111
|
+
let previousEffort = current;
|
|
112
|
+
if (current === state.desiredEffort && this.switched?.version === state.desiredVersion) previousEffort = this.switched.previousEffort;
|
|
113
|
+
if (current !== state.desiredEffort) {
|
|
114
|
+
const switched = await runner.adapter.setEffort!(this.deps.process()!, state.desiredEffort);
|
|
115
|
+
this.switched = { version: state.desiredVersion, previousEffort: current };
|
|
116
|
+
runner.effort = switched.effort;
|
|
117
|
+
this.deps.publish();
|
|
118
|
+
}
|
|
119
|
+
await reportEffortObserved(this.deps.http(), this.deps.agentId(), { instanceId: runner.instanceId, version: state.desiredVersion, effort: runner.effort ?? state.desiredEffort, previousEffort });
|
|
120
|
+
this.pending = false;
|
|
121
|
+
} finally {
|
|
122
|
+
ticket.release();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|