agent-relay-runner 0.127.5 → 0.127.7
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/package.json +2 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/src/adapter.ts +81 -4
- package/src/adapters/claude-permission.ts +209 -0
- package/src/{claude-prompt-gates.ts → adapters/claude-prompt-gates.ts} +11 -5
- package/src/adapters/claude-session-capture.ts +235 -0
- package/src/adapters/claude.ts +18 -26
- package/src/{codex-version.ts → adapters/codex-version.ts} +1 -1
- package/src/adapters/{claude-quota-harvest.ts → provider-quota-harvest.ts} +3 -3
- package/src/control-server.ts +130 -278
- package/src/launch-assembly.ts +2 -2
- package/src/profile-home.ts +1 -1
- package/src/runner-core.ts +155 -322
- package/src/runner-helpers.ts +36 -1
package/src/adapters/claude.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
2
|
import { homedir } from "node:os";
|
|
4
3
|
import { dirname, join, resolve } from "node:path";
|
|
5
4
|
import { type Message } from "agent-relay-sdk";
|
|
@@ -9,18 +8,22 @@ import { tmuxCommand, tmuxHasSession } from "agent-relay-sdk/tmux-utils";
|
|
|
9
8
|
import { profileAllowsRelayFeature, type ManagedProcess, type ProviderAdapter, type ProviderConfig, type ProviderStatusUpdate, type RunnerSpawnConfig, type SemanticStatus, type SpawnArgs } from "../adapter";
|
|
10
9
|
import { computeLivenessSignal, type LivenessInputs } from "../liveness";
|
|
11
10
|
import type { LivenessSignal } from "agent-relay-sdk";
|
|
12
|
-
import { collectClaudeSessionEvents } from "./claude-transcript";
|
|
13
|
-
import type { SessionEvent } from "../session-insights";
|
|
14
11
|
import { prepareClaudeProfileHome, profileUsesClaudeHostProviderGlobals } from "../profile-home";
|
|
15
12
|
import { assembleLaunch, materializeLaunchAssembly, sessionStatusLineSettingsArgs, stripSettingsArgs } from "../launch-assembly";
|
|
16
13
|
import { claudeProviderMessageText } from "./claude-delivery";
|
|
17
14
|
import { claudeProbeActivity, readManagedClaudeStatus, resolveClaudePid, type ClaudeProbeActivity } from "./claude-session-probe";
|
|
18
|
-
import { evaluateClaudePromptGatePane, initialClaudePromptGatePaneState, type ClaudePromptGatePaneState } from "
|
|
15
|
+
import { evaluateClaudePromptGatePane, initialClaudePromptGatePaneState, type ClaudePromptGatePaneState } from "./claude-prompt-gates";
|
|
19
16
|
import { claudePaneLooksReady, claudePaneIsBusy, claudeRateLimitStatus, claudeConnectionRetryStatus, claudeModelUnavailableStatus } from "./claude-status-detectors";
|
|
20
17
|
import { launcherScriptPathForSession } from "../session-scratch";
|
|
18
|
+
import { ClaudeSessionCapture, collectClaudeTranscriptArchiveSegment, collectClaudeTranscriptSessionEvents } from "./claude-session-capture";
|
|
19
|
+
import { claudePermissionPromptHandler } from "./claude-permission";
|
|
21
20
|
|
|
22
21
|
export class ClaudeAdapter implements ProviderAdapter {
|
|
23
22
|
readonly provider = "claude";
|
|
23
|
+
readonly statusSourceId = "claude";
|
|
24
|
+
private readonly paneSourceId = `${this.statusSourceId}-pane`;
|
|
25
|
+
readonly stopObligationPath = "claude-stop";
|
|
26
|
+
readonly permissionPromptHandler = claudePermissionPromptHandler;
|
|
24
27
|
readonly compactSupportsInstructions = true;
|
|
25
28
|
// #352: initial prompt is seeded as Claude's positional launch arg (buildSpawnArgs) — reliable,
|
|
26
29
|
// no send-keys/onboarding race; tells the runner to skip the redundant post-launch delivery.
|
|
@@ -31,6 +34,16 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
31
34
|
private modelUnavailableReported = false;
|
|
32
35
|
private connectionRetryActive = false;
|
|
33
36
|
private promptGatePaneState: ClaudePromptGatePaneState = initialClaudePromptGatePaneState();
|
|
37
|
+
private readonly sessionCapture = new ClaudeSessionCapture();
|
|
38
|
+
readonly onSessionEvent = this.sessionCapture.onSessionEvent.bind(this.sessionCapture);
|
|
39
|
+
readonly onSessionEvents = this.sessionCapture.onSessionEvents.bind(this.sessionCapture);
|
|
40
|
+
readonly captureSessionTurn = this.sessionCapture.captureSessionTurn.bind(this.sessionCapture);
|
|
41
|
+
readonly handleUserPrompt = this.sessionCapture.handleUserPrompt.bind(this.sessionCapture);
|
|
42
|
+
readonly drainSessionTrace = this.sessionCapture.drainReasoningTail.bind(this.sessionCapture);
|
|
43
|
+
readonly settleSessionTraceForToolUse = this.sessionCapture.settlePollForToolUse.bind(this.sessionCapture);
|
|
44
|
+
readonly stopSessionTrace = this.sessionCapture.stopSessionTrace.bind(this.sessionCapture);
|
|
45
|
+
readonly collectSessionEvents = collectClaudeTranscriptSessionEvents;
|
|
46
|
+
readonly collectSessionArchiveSegment = collectClaudeTranscriptArchiveSegment;
|
|
34
47
|
|
|
35
48
|
onStatusChange(cb: (status: ProviderStatusUpdate) => void): void {
|
|
36
49
|
this.statusCb = cb;
|
|
@@ -91,28 +104,6 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
91
104
|
return { method: "tmux-inject", command: "/clear" };
|
|
92
105
|
}
|
|
93
106
|
|
|
94
|
-
// #183/#184: parse the full Claude transcript into the shared SessionEvent stream. The
|
|
95
|
-
// runner slices per-segment, so we return the whole transcript's events each call.
|
|
96
|
-
async collectSessionEvents(_process: ManagedProcess, ctx: { transcriptPath?: string }): Promise<SessionEvent[] | null> {
|
|
97
|
-
if (!ctx.transcriptPath) return null;
|
|
98
|
-
let jsonl: string;
|
|
99
|
-
try {
|
|
100
|
-
jsonl = await readFile(ctx.transcriptPath, "utf8");
|
|
101
|
-
} catch {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
return collectClaudeSessionEvents(jsonl);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async collectSessionArchiveSegment(_process: ManagedProcess, ctx: { transcriptPath?: string }): Promise<string | null> {
|
|
108
|
-
if (!ctx.transcriptPath) return null;
|
|
109
|
-
try {
|
|
110
|
-
return await readFile(ctx.transcriptPath, "utf8");
|
|
111
|
-
} catch {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
107
|
async interrupt(process: ManagedProcess): Promise<Record<string, unknown>> {
|
|
117
108
|
const session = process.meta?.tmuxSession as string | undefined;
|
|
118
109
|
const socket = process.meta?.tmuxSocket as string | undefined;
|
|
@@ -434,6 +425,7 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
434
425
|
const promptGate = evaluateClaudePromptGatePane(pane, this.promptGatePaneState, {
|
|
435
426
|
sessionName,
|
|
436
427
|
busy: claudePaneIsBusy(pane),
|
|
428
|
+
sourceId: this.paneSourceId,
|
|
437
429
|
});
|
|
438
430
|
this.promptGatePaneState = promptGate.state;
|
|
439
431
|
if (promptGate.action) {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// executes CODEX_HOME/hooks.json for these event names (Stop/PreToolUse/…), not just that the
|
|
10
10
|
// version string clears the gate. The version probe is necessary but not sufficient.
|
|
11
11
|
|
|
12
|
-
import { codexVersionOverrideFromEnv } from "
|
|
12
|
+
import { codexVersionOverrideFromEnv } from "../config";
|
|
13
13
|
|
|
14
14
|
// Codex releases from this version on ship stable hooks with Claude-compatible events.
|
|
15
15
|
const CODEX_HOOKS_MIN_VERSION = [0, 124, 0] as const;
|
|
@@ -8,20 +8,20 @@ const PASSIVE_QUOTA_SAMPLE_STALE_MS = 10 * 60_000;
|
|
|
8
8
|
const PASSIVE_QUOTA_LEASE_MAX_TTL_MS = 10 * 60_000;
|
|
9
9
|
const PASSIVE_QUOTA_LEASE_PADDING_MS = 60_000;
|
|
10
10
|
|
|
11
|
-
interface
|
|
11
|
+
interface ProviderQuotaHarvestOptions {
|
|
12
12
|
agentId: string;
|
|
13
13
|
provider: string;
|
|
14
14
|
http: () => Pick<RelayHttpClient, "acquireProviderQuotaPublisherLease" | "getProviderQuotaConfig" | "upsertProviderQuota">;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export class
|
|
17
|
+
export class ProviderQuotaHarvest {
|
|
18
18
|
private config = { ...DEFAULT_PROVIDER_QUOTA_CONFIG };
|
|
19
19
|
private configRefreshAt = 0;
|
|
20
20
|
private lastReportAt?: number;
|
|
21
21
|
private lease?: { accountKey: string; leaseToken: string; expiresAt: number };
|
|
22
22
|
private nextLeaseAttemptAt?: number;
|
|
23
23
|
|
|
24
|
-
constructor(private readonly options:
|
|
24
|
+
constructor(private readonly options: ProviderQuotaHarvestOptions) {}
|
|
25
25
|
|
|
26
26
|
async publish(): Promise<void> {
|
|
27
27
|
if (this.options.provider !== "claude") return;
|