agent-relay-runner 0.19.3 → 0.21.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/package.json +2 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/src/adapter.ts +3 -1
- package/src/adapters/claude.ts +2 -0
- package/src/adapters/codex.ts +77 -5
- package/src/outbox.ts +4 -0
- package/src/relay-instructions.ts +1 -1
- package/src/relay-mcp.ts +50 -0
- package/src/runner.ts +12 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Unified provider lifecycle runner for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"directory": "runner"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"agent-relay-sdk": "0.2.
|
|
23
|
+
"agent-relay-sdk": "0.2.12"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/bun": "latest",
|
package/src/adapter.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface ProviderSessionEvent {
|
|
|
40
40
|
origin?: "chat" | "terminal" | "provider";
|
|
41
41
|
turnId?: string;
|
|
42
42
|
label?: string;
|
|
43
|
+
status?: "running" | "completed" | "failed";
|
|
44
|
+
streaming?: boolean;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export interface ProviderConfig {
|
|
@@ -157,7 +159,7 @@ export function profileAllowsRelayFeature(config: RunnerSpawnConfig, feature: ke
|
|
|
157
159
|
return config.agentProfile?.relay?.[feature] !== false;
|
|
158
160
|
}
|
|
159
161
|
|
|
160
|
-
export const RELAY_CONTEXT = `[agent-relay] You are connected to Agent Relay, a real-time message bus between agents and users. When you receive a relay message: read it, do what it asks, and reply through the relay when a text response is needed. Use agent-relay /react <messageId> <emoji> for lightweight acknowledgement or approval. If Relay MCP tools are available, prefer relay_reply, relay_get_message, relay_get_thread, relay_send_message, relay_upload_artifact, relay_attach_artifact, relay_agent_status, relay_spawn_agent, and relay_shutdown_agent. CLI fallback: agent-relay /reply <messageId> --stdin < response.md; if a delivered message says it was truncated, fetch the full body with: agent-relay get-message <messageId>. For command details, run: agent-relay /guide`;
|
|
162
|
+
export const RELAY_CONTEXT = `[agent-relay] You are connected to Agent Relay, a real-time message bus between agents and users. When you receive a relay message: read it, do what it asks, and reply through the relay when a text response is needed. Use agent-relay /react <messageId> <emoji> for lightweight acknowledgement or approval. If Relay MCP tools are available, prefer relay_reply, relay_get_message, relay_get_thread, relay_send_message, relay_upload_artifact, relay_attach_artifact, relay_agent_status, relay_find_agents, relay_spawn_agent, and relay_shutdown_agent. You never need to know or pass your own agent id — relay fills it from your token; use relay_whoami only if you need to reason about yourself. relay_spawn_agent / relay_shutdown_agent only appear if your profile grants spawning (a live-children quota); when present you can stand up long-living child agents and shut down your own — find them later with relay_find_agents spawnedBy:me. CLI fallback: agent-relay /reply <messageId> --stdin < response.md; if a delivered message says it was truncated, fetch the full body with: agent-relay get-message <messageId>. For command details, run: agent-relay /guide`;
|
|
161
163
|
|
|
162
164
|
const PROVIDER_MESSAGE_BODY_PREVIEW_CHARS = 4000;
|
|
163
165
|
|
package/src/adapters/claude.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { tmuxCommand, tmuxHasSession } from "agent-relay-sdk/tmux-utils";
|
|
|
7
7
|
import { sanitizeFsName } from "agent-relay-sdk/fs-name";
|
|
8
8
|
import { profileAllowsRelayFeature, type ManagedProcess, type ProviderAdapter, type ProviderConfig, type ProviderStatusUpdate, type RunnerSpawnConfig, type SemanticStatus, type SpawnArgs } from "../adapter";
|
|
9
9
|
import { prepareClaudeProfileHome, profileUsesHostProviderGlobals } from "../profile-home";
|
|
10
|
+
import { relayMcpClaudeConfigArg } from "../relay-mcp";
|
|
10
11
|
import { claudeProviderMessageText } from "./claude-delivery";
|
|
11
12
|
|
|
12
13
|
export class ClaudeAdapter implements ProviderAdapter {
|
|
@@ -203,6 +204,7 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
203
204
|
const args = [
|
|
204
205
|
...rigPrefix,
|
|
205
206
|
...pluginDirs.flatMap((dir) => ["--plugin-dir", dir]),
|
|
207
|
+
...(profileAllowsRelayFeature(config, "mcp") ? relayMcpClaudeConfigArg(config.relayUrl) : []),
|
|
206
208
|
...(profileAllowsRelayFeature(config, "statusLine") ? sessionStatusLineSettingsArgs(defaultArgs, config.providerArgs) : []),
|
|
207
209
|
...(config.systemPromptAppend ? ["--append-system-prompt", config.systemPromptAppend] : []),
|
|
208
210
|
...providerArgs,
|
package/src/adapters/codex.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { isRecord, stringValue } from "agent-relay-sdk";
|
|
|
6
6
|
import { isPidAlive, killPid, waitForPidsExit } from "agent-relay-sdk/process-utils";
|
|
7
7
|
import { profileAllowsRelayFeature, providerMessageText, RELAY_CONTEXT, type ManagedProcess, type ProviderAdapter, type ProviderConfig, type ProviderPermissionDecisionInput, type ProviderSessionEvent, type ProviderStatusUpdate, type RunnerSpawnConfig, type SpawnArgs, type TerminalAttachSpec } from "../adapter";
|
|
8
8
|
import { workspaceDepsNoteFromEnv } from "../relay-instructions";
|
|
9
|
+
import { relayMcpCodexConfigArgs, tomlString } from "../relay-mcp";
|
|
9
10
|
import { logger } from "../logger";
|
|
10
11
|
|
|
11
12
|
/** Relay context prepended to a Codex agent's first turn: the standard relay
|
|
@@ -16,6 +17,8 @@ function codexRelayContextBlock(): string {
|
|
|
16
17
|
import { prepareCodexProfileHome, profileUsesHostProviderGlobals } from "../profile-home";
|
|
17
18
|
import { CodexAppClient, type ClientEvent } from "./codex-client";
|
|
18
19
|
|
|
20
|
+
export const DEFAULT_CODEX_TOOL_OUTPUT_TOKEN_LIMIT = 12_000;
|
|
21
|
+
|
|
19
22
|
type PendingCodexApproval = {
|
|
20
23
|
id: string;
|
|
21
24
|
requestId: string | number;
|
|
@@ -36,6 +39,7 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
36
39
|
// Assistant message text accumulated across the current turn's agentMessage items,
|
|
37
40
|
// flushed as one session response on turn/completed (mirrors Claude's chatCaptureMode).
|
|
38
41
|
private turnMessages: string[] = [];
|
|
42
|
+
private readonly itemTextBuffers = new Map<string, string>();
|
|
39
43
|
private captureMode: "final" | "full" = "final";
|
|
40
44
|
|
|
41
45
|
onStatusChange(cb: (status: ProviderStatusUpdate) => void): void {
|
|
@@ -257,6 +261,8 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
257
261
|
...codexModelConfigArgs(config.model, config.effort),
|
|
258
262
|
...codexApprovalConfigArgs(config.approvalMode),
|
|
259
263
|
...(profileAllowsRelayFeature(config, "skills") ? bundledSkillConfigArgs() : []),
|
|
264
|
+
...(profileAllowsRelayFeature(config, "mcp") ? relayMcpCodexConfigArgs(config.relayUrl) : []),
|
|
265
|
+
...codexToolOutputTokenLimitConfigArgs(config),
|
|
260
266
|
...codexManagedConfigArgs(),
|
|
261
267
|
"--listen",
|
|
262
268
|
appServerUrl,
|
|
@@ -331,6 +337,7 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
331
337
|
const turn = isRecord(params?.turn) ? params.turn : undefined;
|
|
332
338
|
this.activeTurnId = stringValue(turn?.id);
|
|
333
339
|
this.turnMessages = [];
|
|
340
|
+
this.itemTextBuffers.clear();
|
|
334
341
|
this.statusCb({ status: "busy", reason: "provider-turn", id: this.activeTurnId });
|
|
335
342
|
}
|
|
336
343
|
}
|
|
@@ -347,6 +354,7 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
347
354
|
if ((method.includes("item/completed") || method.includes("item.completed")) && !isSubagent) {
|
|
348
355
|
this.handleCodexItem(isRecord(params?.item) ? params.item : undefined);
|
|
349
356
|
}
|
|
357
|
+
if (!isSubagent) this.handleCodexItemDelta(method, params);
|
|
350
358
|
if (method.includes("thread/status")) {
|
|
351
359
|
const status = statusType(params?.status);
|
|
352
360
|
if (threadId && this.subagentThreads.has(threadId)) {
|
|
@@ -366,9 +374,11 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
366
374
|
if (!item) return;
|
|
367
375
|
const type = stringValue(item.type);
|
|
368
376
|
const turnId = this.activeTurnId;
|
|
377
|
+
const itemId = codexItemId(item);
|
|
369
378
|
if (type === "agentMessage") {
|
|
370
|
-
const text = stringValue(item.text)?.trim();
|
|
379
|
+
const text = (stringValue(item.text) ?? (itemId ? this.itemTextBuffers.get(itemId) : undefined))?.trim();
|
|
371
380
|
if (text) this.turnMessages.push(text);
|
|
381
|
+
if (itemId) this.itemTextBuffers.delete(itemId);
|
|
372
382
|
return;
|
|
373
383
|
}
|
|
374
384
|
if (type === "userMessage") {
|
|
@@ -377,12 +387,39 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
377
387
|
return;
|
|
378
388
|
}
|
|
379
389
|
if (type === "reasoning") {
|
|
380
|
-
const
|
|
390
|
+
const buffered = itemId ? this.itemTextBuffers.get(itemId) : undefined;
|
|
391
|
+
const text = (codexReasoningText(item) || buffered || "").trim();
|
|
381
392
|
if (text) this.sessionEventCb({ type: "reasoning", origin: "provider", body: text, ...(turnId ? { turnId } : {}) });
|
|
393
|
+
if (itemId) this.itemTextBuffers.delete(itemId);
|
|
382
394
|
return;
|
|
383
395
|
}
|
|
384
396
|
const tool = codexToolSummary(type, item);
|
|
385
|
-
if (tool) this.sessionEventCb({ type: "tool", origin: "provider", body: tool.body, label: tool.label, ...(turnId ? { turnId } : {}) });
|
|
397
|
+
if (tool) this.sessionEventCb({ type: "tool", origin: "provider", body: tool.body, label: tool.label, status: "completed", ...(turnId ? { turnId } : {}) });
|
|
398
|
+
if (itemId) this.itemTextBuffers.delete(itemId);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private handleCodexItemDelta(method: string, params: Record<string, unknown> | undefined): void {
|
|
402
|
+
if (!method.includes("item/") && !method.includes("item.")) return;
|
|
403
|
+
const item = isRecord(params?.item) ? params.item : undefined;
|
|
404
|
+
const itemId = codexItemId(params) ?? codexItemId(item);
|
|
405
|
+
const type = codexItemTypeFromMethod(method) ?? stringValue(item?.type);
|
|
406
|
+
const turnId = this.activeTurnId;
|
|
407
|
+
|
|
408
|
+
if (method.includes("/started") || method.includes(".started")) {
|
|
409
|
+
const tool = codexToolSummary(type, item ?? params ?? {});
|
|
410
|
+
if (tool) this.sessionEventCb({ type: "tool", origin: "provider", body: tool.body, label: tool.label, status: "running", streaming: true, ...(turnId ? { turnId } : {}) });
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (type === "agentMessage" || type === "reasoning" || type === "plan") {
|
|
415
|
+
const delta = codexDeltaText(params);
|
|
416
|
+
if (delta && itemId) this.itemTextBuffers.set(itemId, `${this.itemTextBuffers.get(itemId) ?? ""}${delta}`);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Raw stdout/stderr deltas can be huge. Surface the tool lifecycle via started/completed
|
|
421
|
+
// summaries only; never persist raw process output into the chat mirror.
|
|
422
|
+
if (method.includes("outputDelta") || method.includes("output_delta") || method.includes("progress")) return;
|
|
386
423
|
}
|
|
387
424
|
|
|
388
425
|
private flushTurnResponse(): void {
|
|
@@ -401,6 +438,25 @@ export class CodexAdapter implements ProviderAdapter {
|
|
|
401
438
|
}
|
|
402
439
|
}
|
|
403
440
|
|
|
441
|
+
function codexItemId(item: Record<string, unknown> | undefined): string | undefined {
|
|
442
|
+
return stringValue(item?.itemId) ?? stringValue(item?.id);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function codexDeltaText(params: Record<string, unknown> | undefined): string {
|
|
446
|
+
const delta = params?.delta;
|
|
447
|
+
if (typeof delta === "string") return delta;
|
|
448
|
+
if (isRecord(delta)) {
|
|
449
|
+
const text = stringValue(delta.text) ?? stringValue(delta.value) ?? stringValue(delta.content);
|
|
450
|
+
if (text) return text;
|
|
451
|
+
}
|
|
452
|
+
return stringValue(params?.text) ?? stringValue(params?.chunk) ?? stringValue(params?.content) ?? "";
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function codexItemTypeFromMethod(method: string): string | undefined {
|
|
456
|
+
const match = method.match(/item[/.]([^/.]+)[/.]/);
|
|
457
|
+
return match?.[1];
|
|
458
|
+
}
|
|
459
|
+
|
|
404
460
|
function codexApprovalFromServerRequest(message: { id: string | number; method: string; params?: unknown }): { pending: PendingCodexApproval; view: Record<string, unknown> } | null {
|
|
405
461
|
if (!isRecord(message.params)) return null;
|
|
406
462
|
const method = message.method;
|
|
@@ -841,6 +897,10 @@ export function bundledSkillConfigArgs(skillDirs = bundledCodexSkillDirs()): str
|
|
|
841
897
|
return ["-c", `skills.config=[${skillsConfig}]`];
|
|
842
898
|
}
|
|
843
899
|
|
|
900
|
+
// tomlString now lives in ../relay-mcp (shared with the relay MCP injection args);
|
|
901
|
+
// re-exported here so existing codex.ts consumers/tests keep their import path.
|
|
902
|
+
export { tomlString };
|
|
903
|
+
|
|
844
904
|
export function codexAppServerConfigArgs(...argLists: string[][]): string[] {
|
|
845
905
|
const result: string[] = [];
|
|
846
906
|
for (const args of argLists) {
|
|
@@ -860,10 +920,22 @@ export function codexAppServerConfigArgs(...argLists: string[][]): string[] {
|
|
|
860
920
|
return result;
|
|
861
921
|
}
|
|
862
922
|
|
|
863
|
-
function
|
|
864
|
-
|
|
923
|
+
export function codexToolOutputTokenLimitConfigArgs(config: Pick<RunnerSpawnConfig, "agentProfile">): string[] {
|
|
924
|
+
const configured = codexToolOutputTokenLimit(config.agentProfile?.providerOptions);
|
|
925
|
+
if (configured === null) return [];
|
|
926
|
+
const limit = configured ?? DEFAULT_CODEX_TOOL_OUTPUT_TOKEN_LIMIT;
|
|
927
|
+
return ["-c", `tool_output_token_limit=${limit}`];
|
|
865
928
|
}
|
|
866
929
|
|
|
930
|
+
function codexToolOutputTokenLimit(providerOptions: unknown): number | null | undefined {
|
|
931
|
+
if (!isRecord(providerOptions)) return undefined;
|
|
932
|
+
const codex = providerOptions.codex;
|
|
933
|
+
if (!isRecord(codex)) return undefined;
|
|
934
|
+
const limit = codex.toolOutputTokenLimit;
|
|
935
|
+
return typeof limit === "number" || limit === null ? limit : undefined;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
|
|
867
939
|
async function connectWithRetry(client: CodexAppClient, attempts = 40): Promise<void> {
|
|
868
940
|
// Give the freshly-spawned app-server a moment to bind its socket before the
|
|
869
941
|
// first attempt, so we don't spend the retry budget on guaranteed refusals.
|
package/src/outbox.ts
CHANGED
|
@@ -248,6 +248,10 @@ export class Outbox {
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// Pure per-row delay calc: the attempt count lives in SQLite (one outbox row
|
|
252
|
+
// at a time may be due), so this stays a stateless function rather than the
|
|
253
|
+
// stateful SDK ReconnectionManager (single in-memory counter) used for the
|
|
254
|
+
// orchestrator/voice reconnect loops — different model, deliberately not shared.
|
|
251
255
|
private backoff(attempts: number): number {
|
|
252
256
|
const exp = Math.min(this.maxBackoffMs, this.baseBackoffMs * 2 ** (attempts - 1));
|
|
253
257
|
return Math.round(exp / 2 + Math.random() * (exp / 2)); // full-ish jitter, never below half
|
|
@@ -58,7 +58,7 @@ export function workspaceLifecycleNote(input: { mode?: string | null; branch?: s
|
|
|
58
58
|
const base = input.baseRef ? `\`${input.baseRef}\`` : "the base branch";
|
|
59
59
|
return [
|
|
60
60
|
`[agent-relay] Isolated workspace: you are in a git worktree on branch ${branch}, based on ${base} — NOT the main checkout. Other agents may work in parallel and land to ${base}, so ${base} will move under you. That is expected; don't fight it.`,
|
|
61
|
-
`Do NOT push this branch yourself — not with \`git push\`, not with \`tl push\` or any other push wrapper, and do not manually rebase or merge. A steward may be auto-rebasing this branch in the background; pushing concurrently races it and can leave the worktree mid-rebase. Just commit your work here. When the task is done, run \`agent-relay workspace ready\` — Relay rebases onto the latest ${base}, lands your work, and pushes for you. (\`agent-relay workspace status\` shows the current state.)`,
|
|
61
|
+
`Do NOT push this branch yourself — not with \`git push\`, not with \`tl push\` or any other push wrapper, and do not manually rebase or merge. A steward may be auto-rebasing this branch in the background; pushing concurrently races it and can leave the worktree mid-rebase. Just commit your work here. When the task is done, run \`agent-relay workspace ready\` — Relay rebases onto the latest ${base}, lands your work, and pushes for you. If the installed \`agent-relay\` binary is stale and says the workspace command is unknown, run the repo-local fallback: \`bun src/index.ts workspace ready\`. (\`agent-relay workspace status\` shows the current state.)`,
|
|
62
62
|
].join("\n");
|
|
63
63
|
}
|
|
64
64
|
|
package/src/relay-mcp.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Single home for the relay HTTP MCP endpoint descriptor + per-provider injection
|
|
2
|
+
// args. Both adapters (and Stage 2's proxy) import from here so the endpoint path,
|
|
3
|
+
// server name, and token-handling rules live in exactly one place.
|
|
4
|
+
//
|
|
5
|
+
// Token handling: the bearer token is NEVER placed in argv (it would leak via `ps`
|
|
6
|
+
// and the agent's own process inspection). Claude expands `${AGENT_RELAY_TOKEN}`
|
|
7
|
+
// from the env at MCP-config parse time; Codex reads it from the named env var.
|
|
8
|
+
|
|
9
|
+
export const RELAY_MCP_SERVER_NAME = "agent-relay";
|
|
10
|
+
export const RELAY_MCP_PATH = "/api/mcp";
|
|
11
|
+
|
|
12
|
+
export function relayMcpEndpoint(relayUrl: string): string {
|
|
13
|
+
return `${relayUrl.replace(/\/+$/, "")}${RELAY_MCP_PATH}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Claude: additive `--mcp-config` JSON (NOT --strict-mcp-config, which would clobber
|
|
17
|
+
// the user's own servers). HTTP transport, token via env-var expansion so it never
|
|
18
|
+
// hits argv. Returns the full ["--mcp-config", "<json>"] arg pair.
|
|
19
|
+
export function relayMcpClaudeConfigArg(relayUrl: string): string[] {
|
|
20
|
+
return [
|
|
21
|
+
"--mcp-config",
|
|
22
|
+
JSON.stringify({
|
|
23
|
+
mcpServers: {
|
|
24
|
+
[RELAY_MCP_SERVER_NAME]: {
|
|
25
|
+
type: "http",
|
|
26
|
+
url: relayMcpEndpoint(relayUrl),
|
|
27
|
+
headers: { Authorization: "Bearer ${AGENT_RELAY_TOKEN}" },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Codex: `-c mcp_servers.<name>.*` overrides. `bearer_token_env_var` tells Codex to
|
|
35
|
+
// read the token from the env var itself → transport resolves to streamable_http.
|
|
36
|
+
export function relayMcpCodexConfigArgs(relayUrl: string): string[] {
|
|
37
|
+
const key = `mcp_servers.${RELAY_MCP_SERVER_NAME}`;
|
|
38
|
+
return [
|
|
39
|
+
"-c",
|
|
40
|
+
`${key}.url=${tomlString(relayMcpEndpoint(relayUrl))}`,
|
|
41
|
+
"-c",
|
|
42
|
+
`${key}.bearer_token_env_var=${tomlString("AGENT_RELAY_TOKEN")}`,
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Shared TOML string escaper for Codex `-c key=value` overrides. One home; codex.ts
|
|
47
|
+
// imports this rather than re-declaring it.
|
|
48
|
+
export function tomlString(value: string): string {
|
|
49
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
50
|
+
}
|
package/src/runner.ts
CHANGED
|
@@ -1158,8 +1158,9 @@ export class AgentRunner {
|
|
|
1158
1158
|
return;
|
|
1159
1159
|
}
|
|
1160
1160
|
if (event.type === "response") {
|
|
1161
|
-
//
|
|
1162
|
-
//
|
|
1161
|
+
// Dashboard prompt injection is already answered by this captured App Server
|
|
1162
|
+
// response. Other Relay inbox obligations still belong to the agent's explicit
|
|
1163
|
+
// reply path, so those keep suppressing auto-capture to avoid duplicates.
|
|
1163
1164
|
let replyToMessageId: number | undefined;
|
|
1164
1165
|
const pendingPrompt = this.pendingPromptMessageId;
|
|
1165
1166
|
if (pendingPrompt) {
|
|
@@ -1176,6 +1177,7 @@ export class AgentRunner {
|
|
|
1176
1177
|
...(replyToMessageId ? { replyTo: replyToMessageId } : {}),
|
|
1177
1178
|
session: { type: "response", origin: event.origin ?? "provider", ...(turnId ? { turnId } : {}) },
|
|
1178
1179
|
});
|
|
1180
|
+
if (replyToMessageId) this.obligationCache.markDirty();
|
|
1179
1181
|
return;
|
|
1180
1182
|
}
|
|
1181
1183
|
if (this.options.providerConfig.reasoningCapture === false) return;
|
|
@@ -1183,7 +1185,14 @@ export class AgentRunner {
|
|
|
1183
1185
|
from: this.agentId,
|
|
1184
1186
|
to: "user",
|
|
1185
1187
|
body,
|
|
1186
|
-
session: {
|
|
1188
|
+
session: {
|
|
1189
|
+
type: event.type,
|
|
1190
|
+
origin: event.origin ?? "provider",
|
|
1191
|
+
...(turnId ? { turnId } : {}),
|
|
1192
|
+
...(event.label ? { label: event.label } : {}),
|
|
1193
|
+
...(event.status ? { status: event.status } : {}),
|
|
1194
|
+
...(event.streaming !== undefined ? { streaming: event.streaming } : {}),
|
|
1195
|
+
},
|
|
1187
1196
|
});
|
|
1188
1197
|
}
|
|
1189
1198
|
|