agent-relay-server 0.59.0 → 0.60.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/openapi.json +156 -1
- package/package.json +2 -2
- package/public/assets/MessageBubble-vzupFQD8.js +2 -0
- package/public/assets/MessageBubble-vzupFQD8.js.map +1 -0
- package/public/assets/{PlanGraphPanel-CXix1C-H.js → PlanGraphPanel-DQvycBMW.js} +2 -2
- package/public/assets/{PlanGraphPanel-CXix1C-H.js.map → PlanGraphPanel-DQvycBMW.js.map} +1 -1
- package/public/assets/{activity-CRafQ1Je.js → activity-gmfkOBhQ.js} +2 -2
- package/public/assets/{activity-CRafQ1Je.js.map → activity-gmfkOBhQ.js.map} +1 -1
- package/public/assets/{agent-profiles-DW4o5-ND.js → agent-profiles-7X8gFuwj.js} +2 -2
- package/public/assets/{agent-profiles-DW4o5-ND.js.map → agent-profiles-7X8gFuwj.js.map} +1 -1
- package/public/assets/{agents-Cbegv0Hm.js → agents-D201CuTR.js} +2 -2
- package/public/assets/{agents-Cbegv0Hm.js.map → agents-D201CuTR.js.map} +1 -1
- package/public/assets/{analytics-DX6T7JX3.js → analytics-CkOTrr5t.js} +2 -2
- package/public/assets/{analytics-DX6T7JX3.js.map → analytics-CkOTrr5t.js.map} +1 -1
- package/public/assets/{automation-CzSBv4gg.js → automation-_5u-nVs8.js} +2 -2
- package/public/assets/{automation-CzSBv4gg.js.map → automation-_5u-nVs8.js.map} +1 -1
- package/public/assets/{branch-state-badge-CRA50r81.js → branch-state-badge-BPXXLpQG.js} +2 -2
- package/public/assets/{branch-state-badge-CRA50r81.js.map → branch-state-badge-BPXXLpQG.js.map} +1 -1
- package/public/assets/{channels-B1MQCPCT.js → channels-BsJu5TXR.js} +2 -2
- package/public/assets/{channels-B1MQCPCT.js.map → channels-BsJu5TXR.js.map} +1 -1
- package/public/assets/{chat-BqIaFYGJ.js → chat-CHgPGHQD.js} +2 -2
- package/public/assets/{chat-BqIaFYGJ.js.map → chat-CHgPGHQD.js.map} +1 -1
- package/public/assets/{connectors-C9aFJIrh.js → connectors-arjfBQvc.js} +2 -2
- package/public/assets/{connectors-C9aFJIrh.js.map → connectors-arjfBQvc.js.map} +1 -1
- package/public/assets/display-CketawEF.js.map +1 -1
- package/public/assets/{formatted-body-impl-DOXH7oDF.js → formatted-body-impl-DBt5tnS0.js} +2 -2
- package/public/assets/{formatted-body-impl-DOXH7oDF.js.map → formatted-body-impl-DBt5tnS0.js.map} +1 -1
- package/public/assets/{index-br_Y6Cvt.js → index-D7js3XlH.js} +6 -6
- package/public/assets/{index-br_Y6Cvt.js.map → index-D7js3XlH.js.map} +1 -1
- package/public/assets/{insights-B1edq5lk.js → insights-BvjkOXk9.js} +2 -2
- package/public/assets/{insights-B1edq5lk.js.map → insights-BvjkOXk9.js.map} +1 -1
- package/public/assets/{integrations-Cm4fjp7_.js → integrations-7L1f3eIE.js} +2 -2
- package/public/assets/{integrations-Cm4fjp7_.js.map → integrations-7L1f3eIE.js.map} +1 -1
- package/public/assets/{maintenance-D6zvw0fU.js → maintenance-BBoL--vs.js} +2 -2
- package/public/assets/{maintenance-D6zvw0fU.js.map → maintenance-BBoL--vs.js.map} +1 -1
- package/public/assets/{managed-agents-CzfEznM1.js → managed-agents-BjYBzVZt.js} +2 -2
- package/public/assets/{managed-agents-CzfEznM1.js.map → managed-agents-BjYBzVZt.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-DZlCaTHQ.js → markdown-preview-impl-BnwjZsmO.js} +2 -2
- package/public/assets/{markdown-preview-impl-DZlCaTHQ.js.map → markdown-preview-impl-BnwjZsmO.js.map} +1 -1
- package/public/assets/{memory-CUUAzJDc.js → memory-SLycz2gC.js} +2 -2
- package/public/assets/{memory-CUUAzJDc.js.map → memory-SLycz2gC.js.map} +1 -1
- package/public/assets/{messages-6QZH46LG.js → messages-DfSCTe6N.js} +2 -2
- package/public/assets/{messages-6QZH46LG.js.map → messages-DfSCTe6N.js.map} +1 -1
- package/public/assets/{orchestrators-CrcqkPuc.js → orchestrators-BwxdTQm_.js} +2 -2
- package/public/assets/{orchestrators-CrcqkPuc.js.map → orchestrators-BwxdTQm_.js.map} +1 -1
- package/public/assets/{overview-418xXZO_.js → overview-Djy3qkY5.js} +2 -2
- package/public/assets/{overview-418xXZO_.js.map → overview-Djy3qkY5.js.map} +1 -1
- package/public/assets/{pairs-CnedATgT.js → pairs-9KNduLnC.js} +2 -2
- package/public/assets/{pairs-CnedATgT.js.map → pairs-9KNduLnC.js.map} +1 -1
- package/public/assets/{security-DS13pbag.js → security-tG3q7fFW.js} +2 -2
- package/public/assets/{security-DS13pbag.js.map → security-tG3q7fFW.js.map} +1 -1
- package/public/assets/{select-BOprGZDS.js → select-CGUdMNOG.js} +2 -2
- package/public/assets/{select-BOprGZDS.js.map → select-CGUdMNOG.js.map} +1 -1
- package/public/assets/{settings-BgInn_f3.js → settings-CGcqyJ5L.js} +2 -2
- package/public/assets/{settings-BgInn_f3.js.map → settings-CGcqyJ5L.js.map} +1 -1
- package/public/assets/{store-DHO_a8BD.js → store-DXdfhiVf.js} +2 -2
- package/public/assets/store-DXdfhiVf.js.map +1 -0
- package/public/assets/{tasks-B-4oCPuv.js → tasks-DWlKg218.js} +2 -2
- package/public/assets/{tasks-B-4oCPuv.js.map → tasks-DWlKg218.js.map} +1 -1
- package/public/assets/{teams-sCQAs_QA.js → teams-DRchUP_p.js} +2 -2
- package/public/assets/{teams-sCQAs_QA.js.map → teams-DRchUP_p.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-BhqS2XpS.js → terminal-viewer-impl-D_Sqe-vo.js} +2 -2
- package/public/assets/{terminal-viewer-impl-BhqS2XpS.js.map → terminal-viewer-impl-D_Sqe-vo.js.map} +1 -1
- package/public/assets/{work-queue-BDhjlwUH.js → work-queue-HNcyjON9.js} +2 -2
- package/public/assets/{work-queue-BDhjlwUH.js.map → work-queue-HNcyjON9.js.map} +1 -1
- package/public/assets/{workspaces-6I-C69nf.js → workspaces-CicYE2Ax.js} +2 -2
- package/public/assets/{workspaces-6I-C69nf.js.map → workspaces-CicYE2Ax.js.map} +1 -1
- package/public/index.html +2 -2
- package/src/agent-lifecycle-events.ts +41 -21
- package/src/branch-landed.ts +46 -35
- package/src/channel-target.ts +10 -0
- package/src/cli/index.ts +11 -0
- package/src/cli/merges.ts +89 -0
- package/src/config.ts +9 -0
- package/src/context-advisory.ts +13 -6
- package/src/db/merge-lease.ts +95 -1
- package/src/db/message-reads.ts +6 -0
- package/src/db/messages.ts +16 -1
- package/src/db/migrations.ts +30 -0
- package/src/db/notification-subscriptions.ts +198 -0
- package/src/db/schema.ts +11 -0
- package/src/index.ts +2 -0
- package/src/maintenance.ts +40 -21
- package/src/mcp-merge-tools.ts +66 -0
- package/src/mcp-notification-subscription-tools.ts +111 -0
- package/src/mcp.ts +5 -3
- package/src/merge-pause-maintenance.ts +13 -0
- package/src/notification-router.ts +168 -0
- package/src/notification-rules.ts +186 -0
- package/src/notify.ts +9 -0
- package/src/reviewer-pipeline.ts +54 -40
- package/src/routes/agent-sessions.ts +4 -2
- package/src/routes/commands.ts +20 -0
- package/src/routes/index.ts +7 -0
- package/src/routes/merges.ts +95 -0
- package/src/security.ts +2 -0
- package/src/self-resume.ts +11 -6
- package/src/services/merge-pause.ts +37 -0
- package/src/services/plan-live-bindings.ts +11 -6
- package/src/services/rate-limit-resume.ts +17 -10
- package/src/services/spawn-agent.ts +10 -8
- package/src/services/transient-agent-reaper.ts +11 -6
- package/src/token-db.ts +3 -3
- package/src/workspace-pr-completion.ts +85 -8
- package/public/assets/MessageBubble-BrEKymoV.js +0 -2
- package/public/assets/MessageBubble-BrEKymoV.js.map +0 -1
- package/public/assets/store-DHO_a8BD.js.map +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Auto-merge coordination routes. Generic pause/resume primitive for callers
|
|
2
|
+
// that need Relay's auto-merge sweep to defer a target briefly (#448).
|
|
3
|
+
import { isRecord } from "agent-relay-sdk";
|
|
4
|
+
import { ValidationError } from "../db";
|
|
5
|
+
import { getAutoMergePause, listAutoMergePauses, pauseAutoMerge, renewAutoMergePause, resumeAutoMerge } from "../services/merge-pause";
|
|
6
|
+
import { cleanString } from "../validation";
|
|
7
|
+
import { authAuditMetadata, authorizeRoute, error, json, parseBody, type Handler } from "./_shared";
|
|
8
|
+
|
|
9
|
+
function cleanTtlMs(value: unknown): number | undefined {
|
|
10
|
+
if (value === undefined || value === null || value === "") return undefined;
|
|
11
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
|
|
12
|
+
throw new ValidationError("ttlMs must be a positive integer");
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function actorFromRequest(req: Request): string {
|
|
18
|
+
const audit = authAuditMetadata(req);
|
|
19
|
+
const auth = isRecord(audit.auth) ? audit.auth : undefined;
|
|
20
|
+
return typeof auth?.sub === "string" ? auth.sub
|
|
21
|
+
: typeof auth?.name === "string" ? auth.name
|
|
22
|
+
: "root";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const getMergePauses: Handler = (req) => {
|
|
26
|
+
const denied = authorizeRoute(req, { scope: "command:read" });
|
|
27
|
+
if (denied) return denied;
|
|
28
|
+
return json({ pauses: listAutoMergePauses() });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const postMergePause: Handler = async (req) => {
|
|
32
|
+
const denied = authorizeRoute(req, { scope: "command:write" });
|
|
33
|
+
if (denied) return denied;
|
|
34
|
+
const parsed = await parseBody<unknown>(req);
|
|
35
|
+
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
36
|
+
try {
|
|
37
|
+
if (!isRecord(parsed.body)) return error("body required");
|
|
38
|
+
const repoRoot = cleanString(parsed.body.repoRoot, "repoRoot", { required: true, max: 1000 })!;
|
|
39
|
+
const baseRef = cleanString(parsed.body.baseRef, "baseRef", { max: 240 }) ?? "main";
|
|
40
|
+
const holder = cleanString(parsed.body.holder, "holder", { max: 240 }) ?? actorFromRequest(req);
|
|
41
|
+
const reason = cleanString(parsed.body.reason, "reason", { max: 500 });
|
|
42
|
+
const ttlMs = cleanTtlMs(parsed.body.ttlMs);
|
|
43
|
+
const result = pauseAutoMerge({ repoRoot, baseRef, holder, reason, ttlMs });
|
|
44
|
+
if (!result.ok) {
|
|
45
|
+
return json({ error: "merge target already paused", pause: result.pause }, 409);
|
|
46
|
+
}
|
|
47
|
+
return json({ pause: result.pause }, 201);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
if (e instanceof ValidationError) return error(e.message, 400);
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const postMergePauseRenew: Handler = async (req) => {
|
|
55
|
+
const denied = authorizeRoute(req, { scope: "command:write" });
|
|
56
|
+
if (denied) return denied;
|
|
57
|
+
const parsed = await parseBody<unknown>(req);
|
|
58
|
+
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
59
|
+
try {
|
|
60
|
+
if (!isRecord(parsed.body)) return error("body required");
|
|
61
|
+
const id = cleanString(parsed.body.id, "id", { required: true, max: 120 })!;
|
|
62
|
+
const ttlMs = cleanTtlMs(parsed.body.ttlMs);
|
|
63
|
+
const pause = renewAutoMergePause(id, ttlMs);
|
|
64
|
+
return pause ? json({ pause }) : error("merge pause not found or expired", 404);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
if (e instanceof ValidationError) return error(e.message, 400);
|
|
67
|
+
throw e;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const postMergePauseResume: Handler = async (req) => {
|
|
72
|
+
const denied = authorizeRoute(req, { scope: "command:write" });
|
|
73
|
+
if (denied) return denied;
|
|
74
|
+
const parsed = await parseBody<unknown>(req);
|
|
75
|
+
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
76
|
+
try {
|
|
77
|
+
if (!isRecord(parsed.body)) return error("body required");
|
|
78
|
+
const id = cleanString(parsed.body.id, "id", { max: 120 });
|
|
79
|
+
const repoRoot = cleanString(parsed.body.repoRoot, "repoRoot", { max: 1000 });
|
|
80
|
+
const baseRef = cleanString(parsed.body.baseRef, "baseRef", { max: 240 });
|
|
81
|
+
if (!id && !repoRoot) return error("id or repoRoot required", 400);
|
|
82
|
+
const released = resumeAutoMerge(id ? { id } : { repoRoot: repoRoot!, baseRef: baseRef ?? "main" });
|
|
83
|
+
return json({ released });
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (e instanceof ValidationError) return error(e.message, 400);
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const getMergePauseById: Handler = (req, params) => {
|
|
91
|
+
const denied = authorizeRoute(req, { scope: "command:read" });
|
|
92
|
+
if (denied) return denied;
|
|
93
|
+
const pause = getAutoMergePause(params.id!);
|
|
94
|
+
return pause ? json(pause) : error("merge pause not found", 404);
|
|
95
|
+
};
|
package/src/security.ts
CHANGED
|
@@ -195,6 +195,7 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
195
195
|
if (pathname.startsWith("/api/inbox")) return method === "GET" ? "message:read" : "message:send";
|
|
196
196
|
if (pathname.startsWith("/api/messages")) return method === "GET" ? "message:read" : "message:send";
|
|
197
197
|
if (pathname.startsWith("/api/commands")) return method === "GET" ? "command:read" : "command:write";
|
|
198
|
+
if (pathname.startsWith("/api/merges")) return method === "GET" ? "command:read" : "command:write";
|
|
198
199
|
if (pathname.startsWith("/api/workspaces")) return method === "GET" ? "agent:read" : "agent:write";
|
|
199
200
|
if (pathname.startsWith("/api/agent-profiles")) return method === "GET" ? "agent:read" : "agent:write";
|
|
200
201
|
if (pathname.startsWith("/api/recipes")) return method === "GET" ? "recipes:read" : "recipes:write";
|
|
@@ -254,6 +255,7 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
254
255
|
if (method === "GET") return "command:read";
|
|
255
256
|
return "command:write";
|
|
256
257
|
}
|
|
258
|
+
if (pathname.startsWith("/api/merges")) return method === "GET" ? "command:read" : "command:write";
|
|
257
259
|
if (pathname.startsWith("/api/workspaces")) return method === "GET" ? "agent:read" : "agent:write";
|
|
258
260
|
if (pathname.startsWith("/api/tokens") || pathname.startsWith("/api/token-profiles")) return method === "GET" ? "token:read" : "token:write";
|
|
259
261
|
if (pathname.startsWith("/api/maintenance")) return "system:admin";
|
package/src/self-resume.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
ValidationError,
|
|
12
12
|
} from "./db";
|
|
13
13
|
import { emitRelayEvent } from "./events";
|
|
14
|
-
import {
|
|
14
|
+
import { routeNotification } from "./notification-router";
|
|
15
15
|
import type { AgentCard, Command, ContinuationEnvelope, ContinuationLedgerEntry, SelfResumeMode } from "./types";
|
|
16
16
|
|
|
17
17
|
export interface CompactAndResumeInput {
|
|
@@ -200,11 +200,16 @@ function notifySelfResumeBudgetExhausted(agent: AgentCard, envelope: Continuatio
|
|
|
200
200
|
};
|
|
201
201
|
emitRelayEvent({ type: "agent.resume_budget_exhausted", source: "server", subject: agent.id, data: payload });
|
|
202
202
|
if (!agent.spawnedBy) return;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
203
|
+
routeNotification({
|
|
204
|
+
type: "agent.resume_budget_exhausted",
|
|
205
|
+
scope: { agentId: agent.id, parent: agent.spawnedBy },
|
|
206
|
+
recipients: [agent.spawnedBy],
|
|
207
|
+
message: {
|
|
208
|
+
subject: "Self-resume budget exhausted",
|
|
209
|
+
body: `Spawned agent \`${agent.id}\` stopped cleanly: ${reason}.`,
|
|
210
|
+
payload,
|
|
211
|
+
replyExpected: false,
|
|
212
|
+
},
|
|
208
213
|
});
|
|
209
214
|
}
|
|
210
215
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
acquireMergePause as acquireMergePauseRow,
|
|
3
|
+
getMergePause,
|
|
4
|
+
listMergePauses,
|
|
5
|
+
releaseMergePause as releaseMergePauseRow,
|
|
6
|
+
renewMergePause as renewMergePauseRow,
|
|
7
|
+
type MergePauseRecord,
|
|
8
|
+
} from "../db";
|
|
9
|
+
|
|
10
|
+
export type { MergePauseRecord };
|
|
11
|
+
|
|
12
|
+
export function listAutoMergePauses(): MergePauseRecord[] {
|
|
13
|
+
return listMergePauses();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getAutoMergePause(id: string): MergePauseRecord | null {
|
|
17
|
+
return getMergePause(id);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function pauseAutoMerge(input: {
|
|
21
|
+
repoRoot: string;
|
|
22
|
+
baseRef?: string;
|
|
23
|
+
holder: string;
|
|
24
|
+
reason?: string;
|
|
25
|
+
ttlMs?: number;
|
|
26
|
+
now?: number;
|
|
27
|
+
}): { ok: true; pause: MergePauseRecord } | { ok: false; pause: MergePauseRecord } {
|
|
28
|
+
return acquireMergePauseRow(input);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function renewAutoMergePause(id: string, ttlMs?: number): MergePauseRecord | null {
|
|
32
|
+
return renewMergePauseRow(id, ttlMs);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resumeAutoMerge(input: { id?: string; repoRoot?: string; baseRef?: string }): boolean {
|
|
36
|
+
return releaseMergePauseRow(input);
|
|
37
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getTeam } from "../db";
|
|
2
2
|
import { subscribeRelayEvents, type RelayEvent } from "../events";
|
|
3
|
-
import {
|
|
3
|
+
import { routeNotification } from "../notification-router";
|
|
4
4
|
import { updateBoundPlanNodesForAgent, updateBoundPlanNodesFromAgent } from "./plan-graph";
|
|
5
5
|
|
|
6
6
|
let unsubscribe: (() => void) | undefined;
|
|
@@ -48,11 +48,16 @@ function notifyBoundExit(event: RelayEvent, updatedPlans: ReturnType<typeof upda
|
|
|
48
48
|
const target = team?.coordinatorId ?? `team:${plan.teamId}`;
|
|
49
49
|
const blockedNodes = plan.nodes.filter((node) => node.statusSource === "bound" && node.ownerRef?.agentId === agentId && node.status === "blocked");
|
|
50
50
|
if (blockedNodes.length === 0) continue;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
routeNotification({
|
|
52
|
+
type: "plan.bound_agent_exited",
|
|
53
|
+
scope: { agentId, planId: plan.id, teamId: plan.teamId },
|
|
54
|
+
recipients: [target],
|
|
55
|
+
message: {
|
|
56
|
+
subject: "Bound plan node blocked",
|
|
57
|
+
body: `Agent \`${agentId}\` exited${reason ? ` (${reason})` : ""}; blocked plan node(s) ${blockedNodes.map((node) => `\`${node.id}\``).join(", ")} in \`${plan.id}\`.`,
|
|
58
|
+
payload: { kind: "plan.bound_agent_exited", planId: plan.id, agentId, nodeIds: blockedNodes.map((node) => node.id), reason },
|
|
59
|
+
replyExpected: false,
|
|
60
|
+
},
|
|
56
61
|
});
|
|
57
62
|
}
|
|
58
63
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createActivityEvent, listAgents } from "../db";
|
|
2
|
-
import {
|
|
2
|
+
import { routeNotification } from "../notification-router";
|
|
3
3
|
import { agentProviderState, isRateLimitHold, rateLimitResumeAt } from "../provider-state";
|
|
4
4
|
|
|
5
5
|
// #286 Phase 2: auto-resume agents held on a usage/rate-limit stall once their
|
|
@@ -52,16 +52,23 @@ export function resumeRateLimitedAgents(): Record<string, unknown> {
|
|
|
52
52
|
resumeTracker.set(agent.id, now);
|
|
53
53
|
|
|
54
54
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
// Short default TTL (per-type): a "limit reset, resume" push is pointless once stale — the
|
|
56
|
+
// window has moved on. One recipient per iteration keeps the per-agent rollback below intact.
|
|
57
|
+
routeNotification({
|
|
58
|
+
type: "agent.rate_limit_resume",
|
|
59
|
+
scope: { agentId: agent.id },
|
|
60
|
+
recipients: [agent.id],
|
|
61
|
+
message: {
|
|
62
|
+
subject: "Usage limit reset — resume",
|
|
63
|
+
body: RESUME_BODY,
|
|
64
|
+
payload: {
|
|
65
|
+
kind: "agent.rate_limit_resume",
|
|
66
|
+
agentId: agent.id,
|
|
67
|
+
resetAt: state?.resetAt,
|
|
68
|
+
errorType: state?.errorType,
|
|
69
|
+
},
|
|
70
|
+
replyExpected: false,
|
|
63
71
|
},
|
|
64
|
-
replyExpected: false,
|
|
65
72
|
});
|
|
66
73
|
} catch {
|
|
67
74
|
// A stale/unreachable target must never break the sweep; it store-aheads
|
|
@@ -208,19 +208,21 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
208
208
|
// resolves it. Absent (graceful) when the cwd is inside no registered root.
|
|
209
209
|
const callerProject = resolveProjectForCwd(resolvedCwd);
|
|
210
210
|
|
|
211
|
-
// #409
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
211
|
+
// #409 + #463 — one composition home for the prompt material handed to providers.
|
|
212
|
+
// Precedence is: project preamble, profile system, profile append entries, then
|
|
213
|
+
// transient caller/fork context. Spawns without profile/project material stay
|
|
214
|
+
// byte-identical to the caller-provided systemPromptAppend.
|
|
215
|
+
const appendBlocks: string[] = [];
|
|
216
216
|
if (input.projectInstructions && callerProject) {
|
|
217
217
|
const block = composeProjectInstructions(callerProject, {
|
|
218
218
|
...(input.projectInstructions.policy ? { policy: input.projectInstructions.policy } : {}),
|
|
219
219
|
});
|
|
220
|
-
if (block)
|
|
221
|
-
systemPromptAppend = systemPromptAppend ? `${block}\n\n${systemPromptAppend}` : block;
|
|
222
|
-
}
|
|
220
|
+
if (block.trim()) appendBlocks.push(block);
|
|
223
221
|
}
|
|
222
|
+
if (agentProfile?.instructions.system?.trim()) appendBlocks.push(agentProfile.instructions.system);
|
|
223
|
+
appendBlocks.push(...(agentProfile?.instructions.append.filter((entry) => entry.trim()) ?? []));
|
|
224
|
+
if (input.systemPromptAppend?.trim()) appendBlocks.push(input.systemPromptAppend);
|
|
225
|
+
const systemPromptAppend = appendBlocks.length ? appendBlocks.join("\n\n") : input.systemPromptAppend;
|
|
224
226
|
|
|
225
227
|
// Child runner token. `agentInitiated` (set whenever an agent is the caller) forces a
|
|
226
228
|
// non-spawn-capable child (no grandchildren); `spawnedBy` stamps authoritative lineage the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createCommand } from "../commands-db";
|
|
2
2
|
import { emitRelayEvent } from "../events";
|
|
3
3
|
import { createActivityEvent, getAgent, listAgents, listWorkspaces } from "../db";
|
|
4
|
-
import {
|
|
4
|
+
import { routeNotification } from "../notification-router";
|
|
5
5
|
import { isProviderBlocked } from "../provider-state";
|
|
6
6
|
import { TERMINAL_WORKSPACE_STATUSES } from "../workspace-phase";
|
|
7
7
|
|
|
@@ -47,11 +47,16 @@ function notifyTransientHeld(agentId: string, workspaceId: string | undefined, s
|
|
|
47
47
|
metadata: { source: "server", maintenanceJobId: "transient-agent-reaper", ...payload },
|
|
48
48
|
});
|
|
49
49
|
if (parent) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
routeNotification({
|
|
51
|
+
type: "agent.transient_land_hold",
|
|
52
|
+
scope: { agentId, parent, workspaceId },
|
|
53
|
+
recipients: [parent],
|
|
54
|
+
message: {
|
|
55
|
+
subject: "Transient worker held pending land",
|
|
56
|
+
body: `Transient agent \`${agentId}\` is idle but still unreaped because workspace \`${workspaceId ?? "unknown"}\` is \`${status ?? "unknown"}\` after ${Math.round(waitMs / 60_000)} min. Its work is being preserved; resolve the land before cleanup.`,
|
|
57
|
+
payload,
|
|
58
|
+
replyExpected: false,
|
|
59
|
+
},
|
|
55
60
|
});
|
|
56
61
|
}
|
|
57
62
|
}
|
package/src/token-db.ts
CHANGED
|
@@ -57,7 +57,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
|
|
|
57
57
|
name: "Provider Agent",
|
|
58
58
|
description: "Coding-agent runtime access for messages, commands, tasks, and scoped memory reads.",
|
|
59
59
|
role: "provider",
|
|
60
|
-
scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "plans:read", "plans:write", "teams:read", "teams:write", "blackboard:read", "blackboard:write", "project:read", "project:write", "insights:write"],
|
|
60
|
+
scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "plans:read", "plans:write", "teams:read", "teams:write", "blackboard:read", "blackboard:write", "project:read", "project:write", "notifications:read", "notifications:write", "insights:write"],
|
|
61
61
|
ttlSeconds: 24 * 60 * 60,
|
|
62
62
|
builtIn: true,
|
|
63
63
|
createdBy: "system",
|
|
@@ -67,7 +67,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
|
|
|
67
67
|
name: "Provider Child Agent",
|
|
68
68
|
description: "Delegated child-agent runtime access, constrained to its parent and spawn request.",
|
|
69
69
|
role: "provider",
|
|
70
|
-
scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "insights:write"],
|
|
70
|
+
scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "notifications:read", "notifications:write", "insights:write"],
|
|
71
71
|
constraints: { canDelegate: false },
|
|
72
72
|
ttlSeconds: 2 * 60 * 60,
|
|
73
73
|
builtIn: true,
|
|
@@ -78,7 +78,7 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
|
|
|
78
78
|
name: "Provider Interactive Agent",
|
|
79
79
|
description: "User-launched provider runtime access constrained to its own agent and cwd for long interactive sessions.",
|
|
80
80
|
role: "provider",
|
|
81
|
-
scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "insights:write"],
|
|
81
|
+
scope: ["agent:read", "agent:write", "message:read", "message:send", "command:read", "command:write", "task:read", "task:write", "memory:read", "artifact:read", "artifact:write", "mcp:use", "teams:read", "blackboard:read", "blackboard:write", "project:read", "project:write", "notifications:read", "notifications:write", "insights:write"],
|
|
82
82
|
constraints: { terminalAttach: false, logsRead: false, canDelegate: false },
|
|
83
83
|
ttlSeconds: 30 * 24 * 60 * 60,
|
|
84
84
|
builtIn: true,
|
|
@@ -64,15 +64,23 @@ const ACTIVE_COMMAND_STATUSES = new Set(["pending", "accepted", "running"]);
|
|
|
64
64
|
const PASSING_CONCLUSIONS = new Set(["SUCCESS", "NEUTRAL", "SKIPPED"]);
|
|
65
65
|
const PASSING_STATES = new Set(["SUCCESS"]);
|
|
66
66
|
const READY_MERGE_STATES = new Set(["CLEAN", "HAS_HOOKS"]);
|
|
67
|
+
const MAX_PR_BRANCH_REFRESH_ATTEMPTS = 2;
|
|
67
68
|
|
|
68
|
-
function relayPrMergeAlreadyHandled(workspace: WorkspaceRecord): boolean {
|
|
69
|
+
function relayPrMergeAlreadyHandled(workspace: WorkspaceRecord, preview: WorkspaceMergePreview): boolean {
|
|
69
70
|
const commandId = text(workspace.metadata.relayMergeCommandId);
|
|
70
|
-
if (commandId)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
if (!commandId) return false;
|
|
72
|
+
const command = getCommand(commandId);
|
|
73
|
+
if (!command) return false;
|
|
74
|
+
// PR-scope the idempotency guard (#453). A land-and-continue recycle (#423) repoints the
|
|
75
|
+
// workspace onto a fresh branch but leaves the PRIOR cycle's relayMerge* metadata on the row,
|
|
76
|
+
// so without this a succeeded merge for an already-closed PR would block every SUBSEQUENT PR
|
|
77
|
+
// on the recycled workspace forever (green PR, never merged, no steward). Only treat the merge
|
|
78
|
+
// as already-handled when the recorded command targets the SAME PR we're now considering.
|
|
79
|
+
const recordedPr = isRecord(command.params) && typeof command.params.prNumber === "number" ? command.params.prNumber : undefined;
|
|
80
|
+
const currentPr = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
81
|
+
if (typeof recordedPr === "number" && typeof currentPr === "number" && recordedPr !== currentPr) return false;
|
|
82
|
+
if (ACTIVE_COMMAND_STATUSES.has(command.status)) return true;
|
|
83
|
+
return command.status === "succeeded" && command.result?.relayMerged === true;
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
function nativeAutoMergeArmed(workspace: WorkspaceRecord): boolean {
|
|
@@ -99,7 +107,7 @@ function prReadyForRelayMerge(workspace: WorkspaceRecord, preview: WorkspaceMerg
|
|
|
99
107
|
if (preview.prMerged === true || preview.prState === "merged") return { ok: false, reason: "already-merged" };
|
|
100
108
|
if (!preview.prNumber && !preview.prUrl) return { ok: false, reason: "missing-pr" };
|
|
101
109
|
if (nativeAutoMergeArmed(workspace)) return { ok: false, reason: "native-auto-merge-armed" };
|
|
102
|
-
if (relayPrMergeAlreadyHandled(workspace)) return { ok: false, reason: "relay-merge-already-dispatched" };
|
|
110
|
+
if (relayPrMergeAlreadyHandled(workspace, preview)) return { ok: false, reason: "relay-merge-already-dispatched" };
|
|
103
111
|
|
|
104
112
|
const policy = prAutoMergePolicy(workspace, preview);
|
|
105
113
|
if (policy === "manual") return { ok: false, reason: "manual", policy };
|
|
@@ -112,6 +120,7 @@ function prReadyForRelayMerge(workspace: WorkspaceRecord, preview: WorkspaceMerg
|
|
|
112
120
|
export function preparePrCompletionScan(workspace: WorkspaceRecord, preview: WorkspaceMergePreview, orchestratorAgentId: string): {
|
|
113
121
|
armCommand?: Command;
|
|
114
122
|
notifiedOwner?: string;
|
|
123
|
+
refreshCommand?: Command;
|
|
115
124
|
mergeCommand?: Command;
|
|
116
125
|
waitReason?: string;
|
|
117
126
|
stop?: boolean;
|
|
@@ -121,6 +130,9 @@ export function preparePrCompletionScan(workspace: WorkspaceRecord, preview: Wor
|
|
|
121
130
|
if (action.armCommand) return { armCommand: action.armCommand, stop: true };
|
|
122
131
|
if (action.notifiedOwner) return { notifiedOwner: action.notifiedOwner, stop: true };
|
|
123
132
|
}
|
|
133
|
+
const refresh = prRefreshForRelayMerge(workspace, preview);
|
|
134
|
+
if (refresh.ok) return { refreshCommand: dispatchRelayPrRefreshCommand(workspace, preview, orchestratorAgentId, refresh.policy, refresh.refreshKey, refresh.attempts), stop: true };
|
|
135
|
+
if (refresh.blocked) return { waitReason: refresh.reason, stop: true };
|
|
124
136
|
const relayPr = prReadyForRelayMerge(workspace, preview);
|
|
125
137
|
if (relayPr.ok) return { mergeCommand: dispatchRelayPrMergeCommand(workspace, preview, orchestratorAgentId, relayPr.policy), stop: true };
|
|
126
138
|
return {
|
|
@@ -129,6 +141,49 @@ export function preparePrCompletionScan(workspace: WorkspaceRecord, preview: Wor
|
|
|
129
141
|
};
|
|
130
142
|
}
|
|
131
143
|
|
|
144
|
+
function prRefreshForRelayMerge(workspace: WorkspaceRecord, preview: WorkspaceMergePreview):
|
|
145
|
+
| { ok: true; policy: WorkspaceAutoMergePolicy; refreshKey: string; attempts: number }
|
|
146
|
+
| { ok: false; blocked?: boolean; reason?: string } {
|
|
147
|
+
if (preview.strategy !== "pr") return { ok: false };
|
|
148
|
+
if (preview.prMerged === true || preview.prState === "merged") return { ok: false };
|
|
149
|
+
if (!preview.prNumber && !preview.prUrl) return { ok: false };
|
|
150
|
+
if (nativeAutoMergeArmed(workspace)) return { ok: false };
|
|
151
|
+
if (relayPrMergeAlreadyHandled(workspace, preview)) return { ok: false };
|
|
152
|
+
|
|
153
|
+
const policy = prAutoMergePolicy(workspace, preview);
|
|
154
|
+
if (policy === "manual") return { ok: false };
|
|
155
|
+
if (policy === "on-approval" && preview.reviewDecision !== "APPROVED") return { ok: false };
|
|
156
|
+
if (preview.conflict === true) return { ok: false };
|
|
157
|
+
if ((preview.behind ?? 0) <= 0) return { ok: false };
|
|
158
|
+
|
|
159
|
+
const mergeable = text(preview.mergeable)?.toUpperCase();
|
|
160
|
+
if (mergeable && mergeable !== "MERGEABLE") return { ok: false };
|
|
161
|
+
|
|
162
|
+
const active = activePrRefreshCommand(workspace, preview);
|
|
163
|
+
if (active) return { ok: false, blocked: true, reason: "branch-refresh-already-dispatched" };
|
|
164
|
+
|
|
165
|
+
const refreshKey = prRefreshKey(preview);
|
|
166
|
+
const attempts = workspace.metadata.prRefreshKey === refreshKey && typeof workspace.metadata.prRefreshAttempts === "number" ? workspace.metadata.prRefreshAttempts : 0;
|
|
167
|
+
if (attempts >= MAX_PR_BRANCH_REFRESH_ATTEMPTS) return { ok: false, blocked: true, reason: "branch-refresh-retry-exhausted" };
|
|
168
|
+
return { ok: true, policy, refreshKey, attempts: attempts + 1 };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function activePrRefreshCommand(workspace: WorkspaceRecord, preview: WorkspaceMergePreview): boolean {
|
|
172
|
+
const commandId = text(workspace.metadata.prRefreshCommandId);
|
|
173
|
+
if (!commandId) return false;
|
|
174
|
+
const command = getCommand(commandId);
|
|
175
|
+
if (!command || !ACTIVE_COMMAND_STATUSES.has(command.status)) return false;
|
|
176
|
+
if (!isRecord(command.params)) return true;
|
|
177
|
+
const recordedPr = typeof command.params.prNumber === "number" ? command.params.prNumber : undefined;
|
|
178
|
+
const currentPr = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
179
|
+
return !(typeof recordedPr === "number" && typeof currentPr === "number" && recordedPr !== currentPr);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function prRefreshKey(preview: WorkspaceMergePreview): string {
|
|
183
|
+
const pr = preview.prNumber ? `pr:${preview.prNumber}` : `url:${text(preview.prUrl) ?? "unknown"}`;
|
|
184
|
+
return [pr, `base:${text(preview.baseRef) ?? "base"}`, `behind:${preview.behind ?? 0}`].join("|");
|
|
185
|
+
}
|
|
186
|
+
|
|
132
187
|
function dispatchRelayPrMergeCommand(workspace: WorkspaceRecord, preview: WorkspaceMergePreview, orchestratorAgentId: string, policy: WorkspaceAutoMergePolicy): Command {
|
|
133
188
|
const now = Date.now();
|
|
134
189
|
const prNumber = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
@@ -151,6 +206,28 @@ function dispatchRelayPrMergeCommand(workspace: WorkspaceRecord, preview: Worksp
|
|
|
151
206
|
return command;
|
|
152
207
|
}
|
|
153
208
|
|
|
209
|
+
function dispatchRelayPrRefreshCommand(workspace: WorkspaceRecord, preview: WorkspaceMergePreview, orchestratorAgentId: string, policy: WorkspaceAutoMergePolicy, refreshKey: string, attempts: number): Command {
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
const prNumber = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
212
|
+
const prUrl = preview.prUrl ?? text(workspace.metadata.prUrl);
|
|
213
|
+
const command = createCommand({
|
|
214
|
+
type: "workspace.pr-refresh",
|
|
215
|
+
source: "system",
|
|
216
|
+
target: orchestratorAgentId,
|
|
217
|
+
correlationId: workspace.id,
|
|
218
|
+
params: {
|
|
219
|
+
action: "pr-refresh", workspaceId: workspace.id, repoRoot: workspace.repoRoot, worktreePath: workspace.worktreePath,
|
|
220
|
+
branch: workspace.branch, baseRef: workspace.baseRef, prNumber, prUrl, policy, refreshKey, attempts,
|
|
221
|
+
requestedBy: "relay-pr-completion", requestedAt: now,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
patchWorkspaceMetadata(workspace.id, {
|
|
225
|
+
prRefreshCommandId: command.id, prRefreshDispatchedAt: now, prRefreshPolicy: policy, prRefreshPrNumber: prNumber,
|
|
226
|
+
prRefreshKey: refreshKey, prRefreshAttempts: attempts, prRefreshError: undefined, prRefreshFailedAt: undefined,
|
|
227
|
+
});
|
|
228
|
+
return command;
|
|
229
|
+
}
|
|
230
|
+
|
|
154
231
|
function statusChecksGreen(rollup: unknown): boolean {
|
|
155
232
|
if (!Array.isArray(rollup) || rollup.length === 0) return false;
|
|
156
233
|
return rollup.every(statusNodeGreen);
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{$t as t,En as n,Fn as r,L as i,Ln as a,Nn as o,Nt as s,P as c,Qt as l,St as u,Vt as d,Yt as f,an as p,b as m,c as h,cn as g,g as _,gn as v,i as y,in as b,ln as x,on as S,ot as C,p as w,qn as T,r as E,rt as D,un as O,xt as k}from"./lucide-react-H_-8G79A.js";import{n as A,p as j,t as M}from"./store-DHO_a8BD.js";import{D as N,I as P,M as F,W as I,it as L,j as R,w as z,z as ee}from"./display-CketawEF.js";import{a as te,i as ne,o as re,t as B,x as ie}from"./index-br_Y6Cvt.js";var V=e(T(),1),H={online:`came online`,offline:`went offline`,shutdown:`shut down`,restarted:`restarted`,"compaction-requested":`compaction requested`,compacting:`compaction started`,compacted:`context compacted`,"compaction-stalled":`compaction stalled — may need manual intervention`,"clear-requested":`clear requested`,"clearing-context":`clear started`,"context-cleared":`context cleared`,"clear-stalled":`context clear stalled — may need manual intervention`,"shutting-down":`shutting down`},ae=new Set(Object.keys(H)),oe=3e3;function U(e){return new Date(e.occurredAt??e.createdAt).getTime()}function se(e){if(e.kind!==`session`)return null;let t=e.payload?.session;return!t||t.type!==`narration`&&t.type!==`reasoning`&&t.type!==`tool`?null:{id:e.id,kind:t.type,label:typeof t.label==`string`?t.label:void 0,text:e.body,ts:U(e),turnId:typeof t.turnId==`string`?t.turnId:void 0}}function ce(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`notice`)return null;let n=typeof t.label==`string`&&t.label?t.label:`compacted`;return{id:`notice-${e.id}`,status:n,timestamp:U(e)}}function le(e){return e.kind===`system`&&e.from===`system`}var W=new Set([`agent.ready`,`agent.exited`,`agent.spawn_failed`]);function G(e){let t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`}function ue(e){let t=e.filter(e=>ae.has(e.status)).slice().sort((e,t)=>e.timestamp-t.timestamp),n=[];for(let e of t)n.some(t=>t.id===e.id)||n.some(t=>t.status===e.status&&Math.abs(t.timestamp-e.timestamp)<=oe)||n.push(e);return n}function de(e,t,n,r=[],i){let a=[];for(let e of r){let t=e.entries.reduce((e,t)=>Math.min(e,I(t.originalCreatedAt)),1/0),n=Number.isFinite(t)?t-1:e.importedAt;a.push({ts:n,entry:{type:`import-boundary`,history:e,timestamp:n}});for(let t of e.entries)a.push({ts:I(t.originalCreatedAt),entry:{type:`imported-message`,history:e,entry:t}})}if(n){let e=new Date(n).getTime();Number.isFinite(e)&&a.push({ts:e,entry:{type:`created`,timestamp:e}})}if(i?.spawnedBy){let e=n?new Date(n).getTime():0,t=Number.isFinite(e)?e:0;a.push({ts:t,entry:{type:`agent-action`,direction:`spawned-by`,agentId:i.spawnedBy.agentId,provider:i.spawnedBy.provider,status:`ready`,timestamp:t}})}let o=new Map;for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type===`response`&&typeof e.turnId==`string`){let n=o.get(e.turnId)??[];n.push(t.body.trim()),o.set(e.turnId,n)}}let s=new Map,c=[];for(let t of e){if(le(t)){let e=typeof t.payload?.kind==`string`?t.payload.kind:``;if(W.has(e)){let n=t.payload,r=typeof n.agentId==`string`?n.agentId:``,i=U(t);if(!r){a.push({ts:i,entry:{type:`notice`,msg:t}});continue}let o=e===`agent.ready`?`ready`:e===`agent.exited`?`exited`:`failed`,c=s.get(r);if(c)c.entry.status=o,typeof n.reason==`string`&&(c.entry.reason=n.reason),c.entry.timestamp=i,c.ts=i;else{let e={ts:i,entry:{type:`agent-action`,direction:`spawned-child`,agentId:r,provider:typeof n.provider==`string`?n.provider:void 0,status:o,reason:typeof n.reason==`string`?n.reason:void 0,timestamp:i}};s.set(r,e),a.push(e)}continue}a.push({ts:U(t),entry:{type:`notice`,msg:t}});continue}let e=ce(t);if(e){c.push(e);continue}let n=se(t);if(n){if(n.kind===`narration`&&n.turnId&&o.get(n.turnId)?.some(e=>ee(e,n.text)))continue;a.push({ts:n.ts,entry:{type:`activity`,steps:[n],timestamp:n.ts}});continue}a.push({ts:U(t),entry:{type:`message`,msg:t}})}for(let e of ue([...t,...c]))a.push({ts:e.timestamp,entry:{type:`status`,event:e}});let l={created:0,date:1,"import-boundary":2,"imported-message":3,message:4,activity:4,notice:4,"agent-action":4,status:5};a.sort((e,t)=>{let n=e.ts-t.ts;return Math.abs(n)<3e3&&l[e.entry.type]-l[t.entry.type]||n});let u=[],d=``;for(let{ts:e,entry:t}of a){if(t.type===`created`){u.push(t);continue}let n=G(e);if(n!==d&&(u.push({type:`date`,date:n,timestamp:e}),d=n),t.type===`activity`){let e=u[u.length-1];if(e&&e.type===`activity`&&fe(e,t)){e.steps.push(...t.steps);continue}}u.push(t)}return u}function fe(e,t){let n=e.steps[e.steps.length-1]?.turnId,r=t.steps[0]?.turnId;return n&&r?n===r:!n&&!r}function pe(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}function me(e,t){if(t.startsWith(`/`))return t;let n=`${e.replace(/\/+$/,``)}/${t}`.split(`/`),r=[];for(let e of n)!e||e===`.`||(e===`..`?r.pop():r.push(e));return`/`+r.join(`/`)}function he(e){let t=e.replace(/\/+$/,``),n=t.lastIndexOf(`/`);return n>0?t.slice(0,n):`/`}function ge(e,t){return e.filter(e=>e.status===`online`&&P(t,e.baseDir)).sort((e,t)=>R(t.baseDir).length-R(e.baseDir).length)[0]||e.find(e=>e.status===`online`)}function _e(){return typeof window<`u`&&window.matchMedia(`(min-width: 1280px)`).matches}function K(){return typeof window<`u`&&!!window.getSelection()?.toString()}function ve(e){let t=e.payload?.attachments;return Array.isArray(t)?t.filter(e=>!!(e&&typeof e==`object`&&typeof e.artifactId==`string`)):[]}var ye=[`👍`,`❤️`,`✅`,`👀`];function be(e){let t=new Map;for(let n of e.reactions||[]){let e=F(n.emoji),r=t.get(e)??{emoji:e,count:0,mine:!1,actors:[]};r.count+=1,r.mine=r.mine||n.actorId===`user`,r.actors.push(n.actorId),t.set(e,r)}return[...t.values()].sort((e,t)=>t.count-e.count||e.emoji.localeCompare(t.emoji))}function q(e){return z(e)}function xe(e,t){let n=e.readBy||[],r=e.resolvedToAgent||t||e.to,i=r?n.includes(r):!1,a=n.some(e=>e!==L);if(i||a){let e=i?r:n.find(e=>e!==L);return{label:`read`,title:e?`Read by ${e}`:`Read by agent`,tone:`ok`,icon:O}}if(e.deliveryStatus===`delivered`)return{label:`delivered`,title:`Delivered to agent inbox`,tone:`ok`,icon:O};if(e.deliveryStatus===`queued`)return{label:`queued`,title:`Queued until the target agent is available`,tone:`warn`,icon:f};if(e.deliveryStatus===`failed`||e.deliveryStatus===`dead`){let t=e.deliveryPoisonReason||e.deliveryLastError||e.deliveryStatus;return{label:e.deliveryStatus,title:t,tone:`danger`,icon:b}}return{label:`sent`,title:`Accepted by Relay`,tone:`muted`,icon:x}}function Se(e){let t=I(e.createdAt);return(e.reactions||[]).reduce((e,t)=>Math.max(e,I(t.updatedAt||t.createdAt)),t)}function Ce(e){return e?e.messages.reduce((e,t)=>Math.max(e,Se(t)),0):0}function J(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}function we(e){for(let t of e)t.previewUrl&&URL.revokeObjectURL(t.previewUrl)}function Te(e,t,n,r){if(e?.teamId)return{teamId:e.teamId};let i=[...t];n&&!i.some(e=>e.id===n.id)&&i.push(n);let a=i.filter(e=>e.threadId).reverse(),o=a.find(e=>!!r[A(e.threadId,e.channel)])??a[0];return o?.threadId?{threadId:o.threadId,...o.channel?{channel:o.channel}:{}}:null}var Y=r(),Ee={online:`text-emerald-400`,offline:`text-zinc-400`,shutdown:`text-zinc-400`,restarted:`text-blue-400`,"compaction-requested":`text-zinc-400`,compacting:`text-amber-400`,compacted:`text-amber-400`,"compaction-stalled":`text-red-400`,"clear-requested":`text-zinc-400`,"clearing-context":`text-amber-400`,"context-cleared":`text-amber-400`,"clear-stalled":`text-red-400`,"shutting-down":`text-zinc-400`};function De(e){let t=new Date,[n,r,i]=e.split(`-`).map(Number),a=new Date(n,r-1,i);if(e===G(t.getTime()))return`Today`;let o=new Date(t);return o.setDate(o.getDate()-1),e===G(o.getTime())?`Yesterday`:a.toLocaleDateString(void 0,{weekday:`short`,month:`short`,day:`numeric`,year:a.getFullYear()===t.getFullYear()?void 0:`numeric`})}function Oe({event:e}){return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,Y.jsx)(l,{className:o(`w-2 h-2 fill-current`,Ee[e.status]||`text-zinc-400`)}),(0,Y.jsx)(`span`,{children:H[e.status]||e.status}),(0,Y.jsx)(`span`,{className:`text-muted-foreground/60`,children:J(e.timestamp)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var ke={"branch.landed":k,"context.warning":w},Ae={"branch.landed":`branch landed`,"context.warning":`context warning`};function je({msg:e}){let t=typeof e.payload?.kind==`string`?e.payload.kind:``,n=ke[t]??c,r=Ae[t]??(t||`system signal`);return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground`,children:[(0,Y.jsx)(n,{className:`h-3 w-3`}),(0,Y.jsx)(`span`,{children:r}),(0,Y.jsx)(`span`,{className:`text-muted-foreground/60`,children:J(e.occurredAt??e.createdAt)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var Me={"spawned-child":{ready:`spawned`,exited:`spawned · exited`,failed:`spawn failed`},"spawned-by":{ready:`spawned by`,exited:`spawned by`,failed:`spawned by`}},Ne={"spawned-child-ready":i,"spawned-child-exited":D,"spawned-child-failed":b,"spawned-by-ready":n,"spawned-by-exited":n,"spawned-by-failed":n},Pe={"spawned-child-ready":`text-emerald-400`,"spawned-child-exited":`text-zinc-400`,"spawned-child-failed":`text-red-400`,"spawned-by-ready":`text-blue-400`,"spawned-by-exited":`text-blue-400`,"spawned-by-failed":`text-blue-400`};function Fe({entry:e}){let t=M(e=>e.agentsById),n=M(e=>e.openInboxThread),r=M(e=>e.switchView),i=t[e.agentId]||null,a=i?.label??e.agentId,s=`${e.direction}-${e.status}`,l=Me[e.direction]?.[e.status]??e.direction,u=Ne[s]??c,d=Pe[s]??`text-muted-foreground`,f=e.status===`failed`;function p(){n(e.agentId),r(`chat`)}return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:o(`flex items-center gap-1.5 rounded-full border bg-muted/30 px-2.5 py-1 text-xs`,f?`border-red-500/30`:`border-border`),children:[(0,Y.jsx)(u,{className:o(`h-3 w-3 shrink-0`,d)}),(0,Y.jsx)(`span`,{className:`text-muted-foreground`,children:l}),(0,Y.jsxs)(`button`,{type:`button`,className:`inline-flex items-center gap-1 rounded hover:text-foreground text-muted-foreground transition-colors hover:bg-muted/50 px-1`,onClick:p,title:`Open chat for ${a}`,children:[(0,Y.jsx)(ie,{agent:i,className:`w-4 h-4 text-[10px]`}),(0,Y.jsx)(`span`,{className:`max-w-[160px] truncate`,children:a})]}),e.reason&&f&&(0,Y.jsxs)(`span`,{className:`text-red-400/80 max-w-[120px] truncate`,title:e.reason,children:[`— `,e.reason]}),(0,Y.jsx)(`span`,{className:`text-muted-foreground/60 shrink-0`,children:J(e.timestamp)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Ie({blockedLabel:e,onInterrupt:n}){return e?(0,Y.jsx)(`div`,{className:`flex justify-start mb-3`,children:(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-red-500/30 px-4 py-2.5`,children:[(0,Y.jsx)(b,{className:`w-3.5 h-3.5 text-red-400`}),(0,Y.jsxs)(`span`,{className:`text-xs text-red-300`,children:[`blocked: `,e]})]})}):(0,Y.jsx)(`div`,{className:`flex justify-start mb-3`,children:(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-foreground/10 px-4 py-2.5`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,Y.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_infinite]`}),(0,Y.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.2s_infinite]`}),(0,Y.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.4s_infinite]`})]}),(0,Y.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`working`}),n&&(0,Y.jsxs)(`button`,{onClick:n,title:`Interrupt`,className:`ml-1 flex items-center gap-1 rounded-full px-1.5 py-0.5 text-xs text-muted-foreground hover:text-red-300 hover:bg-red-500/10 transition-colors`,children:[(0,Y.jsx)(t,{className:`w-3.5 h-3.5`}),`Stop`]})]})})}var X=`agent-relay:chat-hide-tools`,Z=(()=>{let e=new Set,t=(()=>{try{return window.localStorage.getItem(X)===`on`}catch{return!1}})(),n=()=>e.forEach(e=>e());return typeof window<`u`&&window.addEventListener(`storage`,e=>{e.key===X&&(t=e.newValue===`on`,n())}),{subscribe(t){return e.add(t),()=>e.delete(t)},get:()=>t,toggle(){t=!t;try{window.localStorage.setItem(X,t?`on`:`off`)}catch{}n()}}})();function Le(){return[(0,V.useSyncExternalStore)(Z.subscribe,Z.get,()=>!1),Z.toggle]}function Re({steps:e,showReasoning:t}){let[n,r]=Le(),i=t?e:e.filter(e=>e.kind!==`reasoning`);if(!i.length)return null;let a=i.filter(e=>e.kind===`tool`).length;return(0,Y.jsx)(`div`,{className:`flex justify-start mb-2`,children:(0,Y.jsxs)(`div`,{className:`max-w-[85%] md:max-w-[75%] min-w-0 space-y-1.5`,children:[i.map(e=>e.kind===`narration`?(0,Y.jsx)(B,{text:e.text,className:`text-sm leading-relaxed break-words text-foreground/90 min-w-0`},e.id):e.kind===`reasoning`?(0,Y.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground/80`,children:[(0,Y.jsx)(v,{className:`w-3.5 h-3.5 mt-0.5 shrink-0 opacity-70`}),(0,Y.jsx)(B,{text:e.text,className:`italic break-words min-w-0`})]},e.id):n?null:(0,Y.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground`,children:[(0,Y.jsx)(_,{className:`w-3 h-3 mt-0.5 shrink-0 opacity-70`}),(0,Y.jsxs)(`span`,{className:`min-w-0 break-words`,children:[(0,Y.jsx)(`span`,{className:`font-medium`,children:e.label||`tool`}),e.text?(0,Y.jsxs)(`span`,{className:`opacity-70`,children:[` — `,e.text]}):null]})]},e.id)),a>0&&(0,Y.jsxs)(`button`,{onClick:r,className:`flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left`,children:[(0,Y.jsx)(S,{className:o(`w-3 h-3 shrink-0 transition-transform`,!n&&`rotate-90`)}),(0,Y.jsx)(`span`,{children:n?`show ${a} tool step${a===1?``:`s`}`:`hide tool steps`})]})]})})}function Q({date:e}){return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-3 py-3 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground`,children:De(e)}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function ze({timestamp:e}){let t=new Date(e).toLocaleString(void 0,{month:`short`,day:`numeric`,year:`numeric`,hour:`2-digit`,minute:`2-digit`});return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`span`,{className:`text-xs text-muted-foreground/60`,children:[`created `,t]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Be({history:e}){let t=e.sourceAgentLabel||e.sourcePeerId,n=e.entries.length;return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-dashed border-border bg-muted/30 px-2 py-1 text-[11px] text-muted-foreground`,children:[(0,Y.jsx)(u,{className:`h-3 w-3`}),(0,Y.jsxs)(`span`,{children:[`forked history from `,t]}),(0,Y.jsxs)(`span`,{className:`text-muted-foreground/60`,children:[n,` `,n===1?`entry`:`entries`]})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Ve({entry:e,sourceLabel:t}){let n=e.message,r=N(n),i=e.originalFrom===L;return(0,Y.jsx)(`div`,{className:o(`flex mb-3 opacity-80`,i?`justify-end`:`justify-start`),children:(0,Y.jsx)(`div`,{className:`max-w-[85%] md:max-w-[75%]`,children:(0,Y.jsxs)(`div`,{className:o(`rounded-2xl border border-dashed px-3.5 py-2 text-sm select-text`,i?`border-primary/25 bg-primary/10 text-foreground rounded-br-sm`:`border-border bg-card/70 rounded-bl-sm`),children:[(0,Y.jsxs)(`div`,{className:o(`mb-1 text-[10px] uppercase tracking-normal`,i?`text-primary/70`:`text-muted-foreground`),children:[`forked history · `,i?`you`:t||e.originalFrom]}),(0,Y.jsx)(B,{text:r,rawBody:n.body,className:`leading-relaxed`}),(0,Y.jsxs)(`p`,{className:o(`mt-1 flex items-center justify-end gap-1.5 text-xs`,i?`text-primary/60`:`text-muted-foreground`),children:[(0,Y.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.originalMessageId]}),(0,Y.jsx)(`span`,{children:J(e.originalCreatedAt)})]})]})})})}function He({scrollRef:e,timeline:t}){let[n,r]=(0,V.useState)(!0),[i,a]=(0,V.useState)(null),[s,c]=(0,V.useState)(null),[l,u]=(0,V.useState)(!0),d=(0,V.useMemo)(()=>{for(let e=t.length-1;e>=0;e--){let n=t[e];if(!(!n||n.type!==`message`)&&n.msg.from===`user`)return{id:n.msg.id,body:N(n.msg)}}return null},[t]);if((0,V.useEffect)(()=>{let t=e.current;if(!d||!t){r(!0);return}let n=t.querySelector(`[data-msg-id="${d.id}"]`);if(!n){r(!0);return}let i=new IntersectionObserver(([e])=>{if(e&&(r(e.isIntersecting),!e.isIntersecting)){let n=e.rootBounds?.top??t.getBoundingClientRect().top;u(e.boundingClientRect.top<n)}},{root:t,threshold:0});i.observe(n);let a=()=>{let e=n.getBoundingClientRect(),r=t.getBoundingClientRect();u(e.top<r.top)};return t.addEventListener(`scroll`,a,{passive:!0}),()=>{i.disconnect(),t.removeEventListener(`scroll`,a)}},[d?.id,e]),n||!d||!d.body.trim())return null;let f=i?.id===d.id?i.value:!1,m=s?.id===d.id?s.value:!1;function h(){let t=e.current;!t||!d||t.querySelector(`[data-msg-id="${d.id}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`})}let _=l?`↑`:`↓`;return m?(0,Y.jsx)(`div`,{className:`sticky top-0 z-10 -mx-3 md:-mx-4 -mt-3 md:-mt-4 bg-background`,children:(0,Y.jsxs)(`button`,{type:`button`,className:`w-full flex items-center justify-center gap-1.5 py-1 text-[11px] text-primary/60 hover:text-primary border-b border-primary/10 transition-colors`,onClick:()=>c({id:d.id,value:!1}),children:[(0,Y.jsx)(`span`,{children:`Your prompt`}),(0,Y.jsx)(g,{className:`w-3 h-3`})]})}):(0,Y.jsxs)(`div`,{className:`sticky top-0 z-10 -mx-3 md:-mx-4 -mt-3 md:-mt-4 px-2 md:px-3 pt-2 pb-1.5 bg-background border-b border-primary/10`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] text-muted-foreground mb-0.5`,children:[(0,Y.jsx)(`span`,{className:`font-medium text-primary/70`,children:`Your prompt`}),(0,Y.jsxs)(`button`,{type:`button`,className:`ml-auto hover:text-foreground transition-colors`,onClick:h,children:[_,` scroll to original`]}),(0,Y.jsx)(`button`,{type:`button`,className:`hover:text-foreground transition-colors`,onClick:()=>c({id:d.id,value:!0}),title:`Minimize`,children:(0,Y.jsx)(p,{className:`w-3 h-3`})})]}),(0,Y.jsx)(`p`,{className:o(`text-sm text-foreground leading-snug cursor-pointer`,!f&&`line-clamp-2`),onClick:()=>a({id:d.id,value:!f}),children:d.body})]})}function Ue(){return(0,V.useSyncExternalStore)(e=>j.subscribe(e),()=>j.getPlayingKey(),()=>null)}function We({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[i,c]=(0,V.useState)(null),[l,u]=(0,V.useState)(null),[f,p]=(0,V.useState)(!1),[m,h]=(0,V.useState)(!1);(0,V.useEffect)(()=>{let t=!1,n=null;async function r(){p(!0),u(null),c(null);try{let r=await a(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),c(n)}catch(e){t||u(e.message)}finally{t||p(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function g(){if(!i)return;let e=document.createElement(`a`);e.href=i,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(`button`,{type:`button`,onClick:()=>i&&h(!0),disabled:!i,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:i?(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(`img`,{src:i,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,Y.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,Y.jsx)(E,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,Y.jsxs)(`span`,{className:o(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,l?`text-red-400`:`text-muted-foreground`),children:[f?(0,Y.jsx)(C,{className:`h-4 w-4 animate-spin`}):(0,Y.jsx)(b,{className:`h-4 w-4`}),l||`Loading image`]})}),i&&(0,Y.jsx)(ne,{open:m,onOpenChange:h,children:(0,Y.jsxs)(te,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,Y.jsx)(re,{className:`sr-only`,children:r}),(0,Y.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,Y.jsx)(`img`,{src:i,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,Y.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,Y.jsx)(`button`,{type:`button`,onClick:g,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,Y.jsx)(d,{className:`w-4 h-4`})}),(0,Y.jsx)(`button`,{type:`button`,onClick:()=>h(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,Y.jsx)(y,{className:`w-4 h-4`})})]})]})]})})]}):(0,Y.jsxs)(`button`,{type:`button`,onClick:g,disabled:!i,className:o(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[f?(0,Y.jsx)(C,{className:`h-4 w-4 shrink-0 animate-spin`}):l?(0,Y.jsx)(b,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,Y.jsx)(s,{className:`h-4 w-4 shrink-0`}),(0,Y.jsx)(`span`,{className:o(`min-w-0 flex-1 truncate`,l&&`text-red-400`),children:l||r}),(0,Y.jsx)(d,{className:`h-3.5 w-3.5 shrink-0`})]})}function $({open:e,onToggle:t,onReact:n}){return(0,Y.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,Y.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:o(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,Y.jsx)(m,{className:`h-3 w-3`})}),e&&(0,Y.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:ye.map(e=>(0,Y.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}var Ge=(0,V.memo)(function({msg:e,peer:n,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){let s=e.from!==`user`&&e.to!==`user`,c=s?e.from===n:e.from===L,l=s?c?`→ ${e.to}`:`from ${e.from}`:null,u=M(e=>e.reactToMessage),d=M(e=>e.voiceTtsEnabled),f=Ue(),p=M(e=>{let t=e.agentsById[n]?.meta?.cwd;return typeof t==`string`?t:``}),[m,g]=(0,V.useState)(!1),_=(0,V.useRef)(null),v=(0,V.useRef)(null),y=(0,V.useRef)(!1),b=N(e),x=J(e.occurredAt??e.createdAt),S=ve(e),C=be(e),w=c?xe(e,n):null,T=w?.icon,E=f===String(e.id),D=d&&!c&&!s&&!q(e)&&b.trim().length>0;function O(t){t.stopPropagation(),E?j.bargeIn():j.speak(e.body,String(e.id))}(0,V.useEffect)(()=>{if(!m)return;function e(e){K()||_.current&&!_.current.contains(e.target)&&g(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[m]);function k(e){return me(p||`/`,e)}function A(e,t){let n=k(e);r?.(n,t)}function P(e,t,n){let r=k(e);i?.(r,t,n)}function F(t){u(e,t),g(!1)}function I(e){e.button===0&&(v.current={x:e.clientX,y:e.clientY},y.current=!1)}function R(e){let t=v.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(y.current=!0)}function z(t){if(!(q(e)||c||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(y.current||K()){y.current=!1;return}g(e=>!e)}}return(0,Y.jsx)(`div`,{"data-msg-id":e.id,className:o(`group/msg flex mb-3`,c?`justify-end`:`justify-start`),children:(0,Y.jsxs)(`div`,{ref:_,className:`relative max-w-[85%] md:max-w-[75%]`,children:[D&&(0,Y.jsx)(`button`,{type:`button`,title:E?`Stop playback`:`Play aloud`,onClick:O,className:o(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,E?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:E?(0,Y.jsx)(t,{className:`h-3.5 w-3.5 animate-pulse`}):(0,Y.jsx)(h,{className:`h-3.5 w-3.5`})}),(0,Y.jsxs)(`div`,{className:o(`rounded-2xl px-3.5 py-2 text-sm select-text`,!c&&!q(e)&&!s&&`cursor-pointer`,c?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:I,onPointerMove:R,onClick:z,children:[l&&(0,Y.jsx)(`p`,{className:o(`text-xs font-medium mb-1 opacity-60`,c?`text-primary-foreground`:`text-muted-foreground`),children:l}),e.subject&&(0,Y.jsx)(`p`,{className:o(`text-xs font-semibold mb-1`,c?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,Y.jsx)(B,{text:b,rawBody:e.body,className:`leading-relaxed`,onOpenPath:A,onPreviewPath:P,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${k(e)}${t?`:${t}`:``}`}),S.map(t=>(0,Y.jsx)(We,{attachment:t,outbound:c},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,Y.jsxs)(`p`,{className:o(`text-xs mt-1 flex items-center gap-1.5`,c?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,Y.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,Y.jsx)(`span`,{children:x}),w&&T&&(0,Y.jsxs)(`span`,{className:o(`inline-flex items-center gap-1`,w.tone===`ok`&&`text-emerald-300`,w.tone===`warn`&&`text-amber-200`,w.tone===`danger`&&`text-red-200`),title:w.title,children:[(0,Y.jsx)(T,{className:`h-3 w-3`}),w.label]})]})]}),!q(e)&&C.length>0&&(0,Y.jsxs)(`div`,{className:o(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,c?`justify-end pr-2`:`justify-start pl-2`),children:[!c&&(0,Y.jsx)($,{open:m,onToggle:()=>g(e=>!e),onReact:F}),C.map(e=>(0,Y.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>F(e.emoji),className:o(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,Y.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,Y.jsx)(`span`,{children:e.count})]},e.emoji))]}),!q(e)&&!c&&C.length===0&&(0,Y.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,Y.jsx)($,{open:m,onToggle:()=>g(e=>!e),onReact:F})})]})})});export{_e as _,ze as a,Ve as c,He as d,pe as f,we as g,Te as h,Ie as i,je as l,he as m,Re as n,Q as o,ge as p,Fe as r,Be as s,Ge as t,Oe as u,Ce as v,de as y};
|
|
2
|
-
//# sourceMappingURL=MessageBubble-BrEKymoV.js.map
|