agent-relay-server 0.104.5 → 0.105.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 +141 -1
- package/package.json +3 -2
- package/public/assets/MessageBubble-CHor4Sxp.js +4 -0
- package/public/assets/{MessageBubble-BpiTbbnp.js.map → MessageBubble-CHor4Sxp.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-k8Aa98g5.js → PlanGraphPanel-jHfkQBX6.js} +2 -2
- package/public/assets/{PlanGraphPanel-k8Aa98g5.js.map → PlanGraphPanel-jHfkQBX6.js.map} +1 -1
- package/public/assets/{activity-CxKi5Erb.js → activity-DDXS6LsX.js} +2 -2
- package/public/assets/{activity-CxKi5Erb.js.map → activity-DDXS6LsX.js.map} +1 -1
- package/public/assets/{agent-profiles-DMZXdN6K.js → agent-profiles-Cn8lCTkg.js} +2 -2
- package/public/assets/{agent-profiles-DMZXdN6K.js.map → agent-profiles-Cn8lCTkg.js.map} +1 -1
- package/public/assets/agents-DBTdcFFz.js +2 -0
- package/public/assets/{agents-BrvpH3gn.js.map → agents-DBTdcFFz.js.map} +1 -1
- package/public/assets/{analytics-1fvh-9ok.js → analytics-VximqXQ9.js} +3 -3
- package/public/assets/{analytics-1fvh-9ok.js.map → analytics-VximqXQ9.js.map} +1 -1
- package/public/assets/{automation-BCgX5WOH.js → automation-L9wq4y5F.js} +2 -2
- package/public/assets/{automation-BCgX5WOH.js.map → automation-L9wq4y5F.js.map} +1 -1
- package/public/assets/{badge-BLar6FjP.js → badge-D8fABzMs.js} +2 -2
- package/public/assets/{badge-BLar6FjP.js.map → badge-D8fABzMs.js.map} +1 -1
- package/public/assets/{branch-state-badge-DYA7bU0i.js → branch-state-badge-CcLZ_u86.js} +2 -2
- package/public/assets/{branch-state-badge-DYA7bU0i.js.map → branch-state-badge-CcLZ_u86.js.map} +1 -1
- package/public/assets/{button-BrIB69qd.js → button-KASZcscA.js} +2 -2
- package/public/assets/{button-BrIB69qd.js.map → button-KASZcscA.js.map} +1 -1
- package/public/assets/card-Cds0MWME.js +2 -0
- package/public/assets/{card-lhnFnBoz.js.map → card-Cds0MWME.js.map} +1 -1
- package/public/assets/{channels-CK80oJYG.js → channels-CcSnLzCG.js} +2 -2
- package/public/assets/{channels-CK80oJYG.js.map → channels-CcSnLzCG.js.map} +1 -1
- package/public/assets/{chat-D_2PEz06.js → chat-BzMj_PO6.js} +2 -2
- package/public/assets/{chat-D_2PEz06.js.map → chat-BzMj_PO6.js.map} +1 -1
- package/public/assets/{connectors-ChGyht5m.js → connectors-DbPOlQhL.js} +2 -2
- package/public/assets/{connectors-ChGyht5m.js.map → connectors-DbPOlQhL.js.map} +1 -1
- package/public/assets/copy-button-d9DFNX9v.js +2 -0
- package/public/assets/{copy-button-Cd-LbSbg.js.map → copy-button-d9DFNX9v.js.map} +1 -1
- package/public/assets/{display-D1xB8NJZ.js → display-BWi8DQCp.js} +2 -2
- package/public/assets/display-BWi8DQCp.js.map +1 -0
- package/public/assets/{dist-BnbGIUmi.js → dist-BbUJ1Vht.js} +2 -2
- package/public/assets/{dist-BnbGIUmi.js.map → dist-BbUJ1Vht.js.map} +1 -1
- package/public/assets/{dist-Bp10H3oR.js → dist-BlZvUZZB.js} +2 -2
- package/public/assets/{dist-Bp10H3oR.js.map → dist-BlZvUZZB.js.map} +1 -1
- package/public/assets/{dist-DDUGmmoi.js → dist-BwwlmyBq.js} +2 -2
- package/public/assets/{dist-DDUGmmoi.js.map → dist-BwwlmyBq.js.map} +1 -1
- package/public/assets/dist-CBVZk9U8.js +2 -0
- package/public/assets/{dist-BEzpMqLy.js.map → dist-CBVZk9U8.js.map} +1 -1
- package/public/assets/{dist-B5VvlkhI.js → dist-CgqfdHSt.js} +2 -2
- package/public/assets/{dist-B5VvlkhI.js.map → dist-CgqfdHSt.js.map} +1 -1
- package/public/assets/dist-DC-TqTz2.js +2 -0
- package/public/assets/{dist-30weaXto.js.map → dist-DC-TqTz2.js.map} +1 -1
- package/public/assets/{dist-DZ97OosY.js → dist-DOAjFOS4.js} +2 -2
- package/public/assets/{dist-DZ97OosY.js.map → dist-DOAjFOS4.js.map} +1 -1
- package/public/assets/{dist-BjOBkHHY.js → dist-DTXKbk2K.js} +2 -2
- package/public/assets/{dist-BjOBkHHY.js.map → dist-DTXKbk2K.js.map} +1 -1
- package/public/assets/dist-DreO5AcZ.js +2 -0
- package/public/assets/{dist-Cgy0FmP-.js.map → dist-DreO5AcZ.js.map} +1 -1
- package/public/assets/{dist-CQF3Jiqg.js → dist-DybScJmm.js} +2 -2
- package/public/assets/{dist-CQF3Jiqg.js.map → dist-DybScJmm.js.map} +1 -1
- package/public/assets/{dist-DAl28ejX.js → dist-UhEAFdvZ.js} +2 -2
- package/public/assets/{dist-DAl28ejX.js.map → dist-UhEAFdvZ.js.map} +1 -1
- package/public/assets/{es2015-B8Z9p8oQ.js → es2015-DUjFyjDp.js} +2 -2
- package/public/assets/{es2015-B8Z9p8oQ.js.map → es2015-DUjFyjDp.js.map} +1 -1
- package/public/assets/{formatted-body-impl-BJ4WCF5J.js → formatted-body-impl-CFWHLkCY.js} +2 -2
- package/public/assets/{formatted-body-impl-BJ4WCF5J.js.map → formatted-body-impl-CFWHLkCY.js.map} +1 -1
- package/public/assets/index-BjjzI2LT.css +2 -0
- package/public/assets/{index-CN7QZlMb.js → index-aCfZQ7LY.js} +13 -13
- package/public/assets/index-aCfZQ7LY.js.map +1 -0
- package/public/assets/{input-B_fUHtLD.js → input-CtHfLn1i.js} +2 -2
- package/public/assets/{input-B_fUHtLD.js.map → input-CtHfLn1i.js.map} +1 -1
- package/public/assets/{insights-BCZRV0hX.js → insights-BuPFE0tf.js} +2 -2
- package/public/assets/{insights-BCZRV0hX.js.map → insights-BuPFE0tf.js.map} +1 -1
- package/public/assets/{integrations-C7UGs00U.js → integrations-hkSW8CzM.js} +2 -2
- package/public/assets/{integrations-C7UGs00U.js.map → integrations-hkSW8CzM.js.map} +1 -1
- package/public/assets/{lib-ng6WZYjE.js → lib-l-JkMVR9.js} +2 -2
- package/public/assets/{lib-ng6WZYjE.js.map → lib-l-JkMVR9.js.map} +1 -1
- package/public/assets/{lucide-react-BYZuAhQs.js → lucide-react-C4eXYCwx.js} +2 -2
- package/public/assets/{lucide-react-BYZuAhQs.js.map → lucide-react-C4eXYCwx.js.map} +1 -1
- package/public/assets/{maintenance-ChYno3Fm.js → maintenance-wYwJ7w6h.js} +2 -2
- package/public/assets/{maintenance-ChYno3Fm.js.map → maintenance-wYwJ7w6h.js.map} +1 -1
- package/public/assets/{managed-agents-DjxZQNIR.js → managed-agents-CKaYD93I.js} +4 -4
- package/public/assets/{managed-agents-DjxZQNIR.js.map → managed-agents-CKaYD93I.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-DFa5HF1H.js → markdown-preview-impl-bf4eDQUM.js} +2 -2
- package/public/assets/{markdown-preview-impl-DFa5HF1H.js.map → markdown-preview-impl-bf4eDQUM.js.map} +1 -1
- package/public/assets/memory-DIneEza9.js +2 -0
- package/public/assets/{memory-CTw6kadT.js.map → memory-DIneEza9.js.map} +1 -1
- package/public/assets/{messages-Cjr816D_.js → messages-DP9Vv_wh.js} +2 -2
- package/public/assets/{messages-Cjr816D_.js.map → messages-DP9Vv_wh.js.map} +1 -1
- package/public/assets/{orchestrators-CdmFKA7K.js → orchestrators-CAnKjxdO.js} +2 -2
- package/public/assets/{orchestrators-CdmFKA7K.js.map → orchestrators-CAnKjxdO.js.map} +1 -1
- package/public/assets/{overview-DBOnjfpP.js → overview-B98U3C-y.js} +2 -2
- package/public/assets/{overview-DBOnjfpP.js.map → overview-B98U3C-y.js.map} +1 -1
- package/public/assets/{pairs-BkcdKdF7.js → pairs-DVeeuxhY.js} +2 -2
- package/public/assets/{pairs-BkcdKdF7.js.map → pairs-DVeeuxhY.js.map} +1 -1
- package/public/assets/{projects-ClKbDQB1.js → projects-vt63bwHN.js} +2 -2
- package/public/assets/{projects-ClKbDQB1.js.map → projects-vt63bwHN.js.map} +1 -1
- package/public/assets/prompt-settings-Df-0ZMJ5.js +2 -0
- package/public/assets/prompt-settings-Df-0ZMJ5.js.map +1 -0
- package/public/assets/{react-dom-Dbhs84ug.js → react-dom-BMF4hNzG.js} +2 -2
- package/public/assets/{react-dom-Dbhs84ug.js.map → react-dom-BMF4hNzG.js.map} +1 -1
- package/public/assets/{registry-Bsf3RtOO.js → registry-DlLZyW7_.js} +2 -2
- package/public/assets/{registry-Bsf3RtOO.js.map → registry-DlLZyW7_.js.map} +1 -1
- package/public/assets/{security-Iyu5bhJw.js → security-Bo4Ty3lz.js} +2 -2
- package/public/assets/{security-Iyu5bhJw.js.map → security-Bo4Ty3lz.js.map} +1 -1
- package/public/assets/{select-Bi6tNJDx.js → select-BguZ80M8.js} +2 -2
- package/public/assets/{select-Bi6tNJDx.js.map → select-BguZ80M8.js.map} +1 -1
- package/public/assets/settings-CpX9Xw2j.js +6 -0
- package/public/assets/{settings-CbHp2F9r.js.map → settings-CpX9Xw2j.js.map} +1 -1
- package/public/assets/{store-zeyDiSKE.js → store-DUfrGvOE.js} +3 -3
- package/public/assets/{store-zeyDiSKE.js.map → store-DUfrGvOE.js.map} +1 -1
- package/public/assets/{tasks-CPlf-5Tr.js → tasks-Cg1HXY9l.js} +2 -2
- package/public/assets/{tasks-CPlf-5Tr.js.map → tasks-Cg1HXY9l.js.map} +1 -1
- package/public/assets/{teams-D2uUNhlY.js → teams-BFcYunxB.js} +2 -2
- package/public/assets/{teams-D2uUNhlY.js.map → teams-BFcYunxB.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-uRzbrlXy.js → terminal-viewer-impl-CF2EBa2K.js} +3 -3
- package/public/assets/{terminal-viewer-impl-uRzbrlXy.js.map → terminal-viewer-impl-CF2EBa2K.js.map} +1 -1
- package/public/assets/types-DorDDvEb.js.map +1 -1
- package/public/assets/{use-keyboard-viewport-D8_NYDkh.js → use-keyboard-viewport-RYMALvu1.js} +2 -2
- package/public/assets/{use-keyboard-viewport-D8_NYDkh.js.map → use-keyboard-viewport-RYMALvu1.js.map} +1 -1
- package/public/assets/user-avatar-D1ffSClz.js +2 -0
- package/public/assets/{user-avatar-GFWMuAGK.js.map → user-avatar-D1ffSClz.js.map} +1 -1
- package/public/assets/{work-queue-B30SNmuq.js → work-queue-DV36cxjp.js} +2 -2
- package/public/assets/{work-queue-B30SNmuq.js.map → work-queue-DV36cxjp.js.map} +1 -1
- package/public/assets/workspaces-CIsQqQby.js +3 -0
- package/public/assets/workspaces-CIsQqQby.js.map +1 -0
- package/public/index.html +22 -22
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +114 -23
- package/runner/plugins/claude/monitors/relay-monitor.ts +2 -2
- package/runner/src/adapter.ts +4 -1
- package/src/agent-lifecycle-events.ts +21 -5
- package/src/automations/reconcile.ts +4 -1
- package/src/branch-landed.ts +22 -4
- package/src/config-store.ts +17 -21
- package/src/context-advisory.ts +10 -3
- package/src/db/index.ts +1 -0
- package/src/db/projects.ts +29 -0
- package/src/db/prompt-templates-schema.ts +20 -0
- package/src/db/prompt-templates.ts +86 -0
- package/src/db/schema.ts +5 -4
- package/src/db/tasks.ts +2 -1
- package/src/db/workspaces.ts +41 -1
- package/src/index.ts +3 -0
- package/src/project-instructions.ts +7 -14
- package/src/prompt-resolver.ts +530 -0
- package/src/routes/index.ts +5 -0
- package/src/routes/prompt-templates.ts +60 -0
- package/src/routes/workspaces.ts +4 -3
- package/src/routing-policy-push.ts +4 -1
- package/src/security.ts +3 -0
- package/src/self-resume.ts +8 -15
- package/src/services/prompt-templates.ts +19 -0
- package/src/services/register-agent.ts +2 -0
- package/src/services/spawn-issue-activity.ts +107 -0
- package/src/spawn-targets.ts +3 -11
- package/src/steward.ts +5 -23
- package/src/workspace-auto-merge.ts +19 -2
- package/src/workspace-phase.ts +15 -28
- package/public/assets/MessageBubble-BpiTbbnp.js +0 -4
- package/public/assets/agents-BrvpH3gn.js +0 -2
- package/public/assets/card-lhnFnBoz.js +0 -2
- package/public/assets/copy-button-Cd-LbSbg.js +0 -2
- package/public/assets/display-D1xB8NJZ.js.map +0 -1
- package/public/assets/dist-30weaXto.js +0 -2
- package/public/assets/dist-BEzpMqLy.js +0 -2
- package/public/assets/dist-Cgy0FmP-.js +0 -2
- package/public/assets/index-BMO_Sblt.css +0 -2
- package/public/assets/index-CN7QZlMb.js.map +0 -1
- package/public/assets/memory-CTw6kadT.js +0 -2
- package/public/assets/settings-CbHp2F9r.js +0 -6
- package/public/assets/user-avatar-GFWMuAGK.js +0 -2
- package/public/assets/workspaces-CSpmgbPT.js +0 -3
- package/public/assets/workspaces-CSpmgbPT.js.map +0 -1
package/src/routes/workspaces.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { RELAY_TOKEN_HEADER, isRecord } from "agent-relay-sdk";
|
|
3
3
|
import { TERMINAL_WORKSPACE_STATUSES, describeWorkspacePhase, landReceipt } from "../workspace-phase";
|
|
4
4
|
import { VALID_WORKSPACE_STATUSES, cleanMeta, cleanString, optionalEnum } from "../validation";
|
|
5
|
-
import { ValidationError, deleteWorkspace, getAgent, getWorkspace, listMergeLeases, listOrchestrators, listRepoStewards, listWorkspaces, updateWorkspaceStatus } from "../db";
|
|
5
|
+
import { ValidationError, deleteWorkspace, getAgent, getWorkspace, listMergeLeases, listOrchestrators, listRepoStewards, listWorkspaces, updateWorkspaceStatus, withResolvedWorkspaceProjects } from "../db";
|
|
6
6
|
import { WORKSPACE_ACTIONS, applyWorkspaceAction, buildWorkspaceCleanupCommand, waitForWorkspaceStatus } from "../workspace-actions";
|
|
7
7
|
import { auditEvent, authAuditMetadata, authorizeRoute, emitCommand, error, json, parseBody, type Handler } from "./_shared";
|
|
8
8
|
import { collectWorkspaceOrphans } from "../workspace-orphans";
|
|
@@ -21,8 +21,9 @@ export const getWorkspaces: Handler = (req) => {
|
|
|
21
21
|
const url = new URL(req.url);
|
|
22
22
|
const repoRoot = cleanString(url.searchParams.get("repoRoot") ?? undefined, "repoRoot", { max: 1000 });
|
|
23
23
|
const ownerAgentId = cleanString(url.searchParams.get("agentId") ?? undefined, "agentId", { max: 240 });
|
|
24
|
+
const projectId = cleanString(url.searchParams.get("projectId") ?? undefined, "projectId", { max: 120 });
|
|
24
25
|
const status = optionalEnum(url.searchParams.get("status") ?? undefined, "status", VALID_WORKSPACE_STATUSES) as WorkspaceStatus | undefined;
|
|
25
|
-
return json(listWorkspaces({ repoRoot, ownerAgentId, status }).map(withOwnerOnline));
|
|
26
|
+
return json(withResolvedWorkspaceProjects(listWorkspaces({ repoRoot, ownerAgentId, status }), { projectId }).map(withOwnerOnline));
|
|
26
27
|
} catch (e) {
|
|
27
28
|
if (e instanceof ValidationError) return error(e.message, 400);
|
|
28
29
|
throw e;
|
|
@@ -32,7 +33,7 @@ export const getWorkspaces: Handler = (req) => {
|
|
|
32
33
|
export const getWorkspaceById: Handler = (_req, params) => {
|
|
33
34
|
const workspace = getWorkspace(params.id!);
|
|
34
35
|
if (!workspace) return error("workspace not found", 404);
|
|
35
|
-
return json(withOwnerOnline(workspace));
|
|
36
|
+
return json(withOwnerOnline(withResolvedWorkspaceProjects([workspace])[0]!));
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
export const getWorkspaceStewards: Handler = () => json({ stewards: listRepoStewards(), mergeLeases: listMergeLeases() });
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { getDb } from "./db";
|
|
15
15
|
import { emitRelayEvent } from "./events";
|
|
16
16
|
import { routeNotification } from "./notification-router";
|
|
17
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
17
18
|
import { parseJson } from "./utils";
|
|
18
19
|
import { isRecord } from "agent-relay-sdk";
|
|
19
20
|
import type { RoutingPolicy } from "agent-relay-sdk";
|
|
@@ -43,7 +44,9 @@ export function notifyRoutingPolicyChanged(policy: RoutingPolicy, opts: { update
|
|
|
43
44
|
ttlMs: PUSH_TTL_MS,
|
|
44
45
|
message: {
|
|
45
46
|
subject: "Routing policy updated",
|
|
46
|
-
body:
|
|
47
|
+
body: renderTemplate("notify.routing.policy-changed", {
|
|
48
|
+
updatedBy: opts.updatedBy ? ` by ${opts.updatedBy}` : "",
|
|
49
|
+
}),
|
|
47
50
|
payload: { kind: "routing.policy.changed", policy: policy as unknown as Record<string, unknown>, updatedBy: opts.updatedBy ?? null },
|
|
48
51
|
replyExpected: false,
|
|
49
52
|
},
|
package/src/security.ts
CHANGED
|
@@ -202,6 +202,7 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
202
202
|
if (pathname === "/api/provider-quotas") return method === "GET" ? "agent:read" : "quota:write";
|
|
203
203
|
if (pathname === "/api/routing-policy") return method === "GET" ? "agent:read" : "settings:write:routing";
|
|
204
204
|
if (pathname === "/api/teams/config") return method === "GET" ? "teams:read" : "settings:write:teams";
|
|
205
|
+
if (pathname === "/api/settings/prompts" || pathname.startsWith("/api/settings/prompts/")) return method === "GET" ? "agent:read" : "system:admin";
|
|
205
206
|
if (pathname.startsWith("/api/teams")) return method === "GET" ? "teams:read" : "teams:write";
|
|
206
207
|
if (pathname.startsWith("/api/blackboard")) return method === "GET" ? "blackboard:read" : "blackboard:write";
|
|
207
208
|
if (pathname.startsWith("/api/issue-associations")) return method === "GET" ? "issue:read" : "issue:write";
|
|
@@ -297,6 +298,7 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
297
298
|
return method === "GET" ? "agent:read" : "settings:write:routing";
|
|
298
299
|
}
|
|
299
300
|
if (pathname === "/api/teams/config") return method === "GET" ? "teams:read" : "settings:write:teams";
|
|
301
|
+
if (pathname === "/api/settings/prompts" || pathname.startsWith("/api/settings/prompts/")) return method === "GET" ? "agent:read" : "system:admin";
|
|
300
302
|
if (pathname.startsWith("/api/messages") || pathname.startsWith("/api/inbox")) return method === "GET" ? "message:read" : "message:send";
|
|
301
303
|
if (pathname.startsWith("/api/agent-profiles")) return method === "GET" ? "agent:read" : "agent:write";
|
|
302
304
|
if (pathname.startsWith("/api/provisioning")) return method === "GET" ? "provisioning:read" : "provisioning:write";
|
|
@@ -326,6 +328,7 @@ const CONFIG_SETTINGS_NAMESPACES = new Set(["steward", "insights", "notification
|
|
|
326
328
|
|
|
327
329
|
function requiredSettingsScopeFor(method: string, pathname: string): string | null | undefined {
|
|
328
330
|
if (pathname === "/api/settings") return method === "GET" ? "settings:read" : null;
|
|
331
|
+
if (pathname === "/api/settings/prompts" || pathname.startsWith("/api/settings/prompts/")) return undefined;
|
|
329
332
|
const settingsMatch = pathname.match(/^\/api\/settings\/([^/]+)(?:\/[^/]+)?$/);
|
|
330
333
|
if (settingsMatch) return settingsScope(method, decodeURIComponent(settingsMatch[1]!));
|
|
331
334
|
if (pathname === "/api/steward-config") return settingsScope(method, "steward");
|
package/src/self-resume.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "./db";
|
|
14
14
|
import { emitRelayEvent } from "./events";
|
|
15
15
|
import { routeNotification } from "./notification-router";
|
|
16
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
16
17
|
import type { AgentCard, Command, ContinuationEnvelope, ContinuationLedgerEntry, SelfResumeMode } from "./types";
|
|
17
18
|
|
|
18
19
|
interface CompactAndResumeInput {
|
|
@@ -149,21 +150,13 @@ function renderContinuation(envelope: ContinuationEnvelope): string {
|
|
|
149
150
|
const ledger = envelope.ledger.length
|
|
150
151
|
? envelope.ledger.map((entry) => `- gen ${entry.gen} @ ${new Date(entry.at).toISOString()}: ${entry.ruledOut}${entry.why ? `; why: ${entry.why}` : ""}`).join("\n")
|
|
151
152
|
: "- none";
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
envelope.
|
|
157
|
-
"",
|
|
158
|
-
|
|
159
|
-
ledger,
|
|
160
|
-
"",
|
|
161
|
-
"Working state:",
|
|
162
|
-
envelope.workingState,
|
|
163
|
-
"",
|
|
164
|
-
"Archive refs:",
|
|
165
|
-
envelope.archiveRefs.length ? envelope.archiveRefs.join("\n") : "- none",
|
|
166
|
-
].join("\n");
|
|
153
|
+
return renderTemplate("continuation.envelope", {
|
|
154
|
+
generation: String(envelope.generation),
|
|
155
|
+
objective: envelope.objective,
|
|
156
|
+
ruledOut: ledger,
|
|
157
|
+
workingState: envelope.workingState,
|
|
158
|
+
archiveRefs: envelope.archiveRefs.length ? envelope.archiveRefs.join("\n") : "- none",
|
|
159
|
+
});
|
|
167
160
|
}
|
|
168
161
|
|
|
169
162
|
function assertResumeSupported(agent: AgentCard, mode: SelfResumeMode): void {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getTemplate, listTemplates, resetTemplate, upsertTemplate, type PromptTemplateRecord } from "../db";
|
|
2
|
+
|
|
3
|
+
export function listPromptTemplates(category?: string): PromptTemplateRecord[] {
|
|
4
|
+
return listTemplates(category);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getPromptTemplate(slug: string): PromptTemplateRecord | null {
|
|
8
|
+
return getTemplate(slug);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function updatePromptTemplate(slug: string, template: string): PromptTemplateRecord | null {
|
|
12
|
+
upsertTemplate(slug, template);
|
|
13
|
+
return getTemplate(slug);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resetPromptTemplate(slug: string): PromptTemplateRecord | null {
|
|
17
|
+
resetTemplate(slug);
|
|
18
|
+
return getTemplate(slug);
|
|
19
|
+
}
|
|
@@ -18,6 +18,7 @@ import { notifyAgentReady } from "../agent-lifecycle-events";
|
|
|
18
18
|
import { resolveSpawnLineage, resolveTeamLineage } from "../security";
|
|
19
19
|
import { flushQueuedDirectMessages, markManagedAgentRunning } from "./managed-running";
|
|
20
20
|
import { isAgentUnavailable } from "../db/agent-predicates";
|
|
21
|
+
import { recordSpawnPromptIssueActivity } from "./spawn-issue-activity";
|
|
21
22
|
import type { AuthContext } from "./auth-context";
|
|
22
23
|
import type { AgentCard, RegisterAgentInput } from "../types";
|
|
23
24
|
|
|
@@ -86,6 +87,7 @@ export function registerAgent(input: RegisterAgentInput, ctx: AuthContext): Agen
|
|
|
86
87
|
const promptMirror = seedSpawnPromptMirror(agent.id, spawnRequestId);
|
|
87
88
|
if (promptMirror?.created || promptMirror?.updated) emitNewMessage(promptMirror.message);
|
|
88
89
|
seedSpawnReplyObligation(agent.id, agent.spawnedBy, spawnRequestId);
|
|
90
|
+
recordSpawnPromptIssueActivity(agent, spawnRequestId);
|
|
89
91
|
createActivityEvent({
|
|
90
92
|
clientId: `server-agent-${agent.id}-registered`,
|
|
91
93
|
kind: "state",
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { isRecord } from "agent-relay-sdk";
|
|
2
|
+
import { getDb, resolveProjectForCwd } from "../db";
|
|
3
|
+
import { resolveIssueRefFromString } from "../db/issue-refs";
|
|
4
|
+
import { defaultIssueRepo } from "../config";
|
|
5
|
+
import { parseJson } from "../utils";
|
|
6
|
+
import { recordAndEmitIssueActivity } from "./issue-activity";
|
|
7
|
+
import type { AgentCard, WorkspaceRecord } from "../types";
|
|
8
|
+
|
|
9
|
+
const BARE_ISSUE_REF_RE = /#([0-9]+)/g;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse spawn prompt for bare #NNN refs and record "edit" activity for the
|
|
13
|
+
* newly registered agent. Called once on first registration. Silently ignores
|
|
14
|
+
* prompts with no refs or refs that can't be resolved (no issueRepo configured).
|
|
15
|
+
* Uses a stable clientId so repeated registration never double-records.
|
|
16
|
+
*/
|
|
17
|
+
export function recordSpawnPromptIssueActivity(agent: AgentCard, spawnRequestId: string | undefined): void {
|
|
18
|
+
if (!spawnRequestId) return;
|
|
19
|
+
try {
|
|
20
|
+
const row = getDb().query(`
|
|
21
|
+
SELECT params FROM commands
|
|
22
|
+
WHERE type = 'agent.spawn' AND correlation_id = ?
|
|
23
|
+
ORDER BY created_at DESC
|
|
24
|
+
LIMIT 1
|
|
25
|
+
`).get(spawnRequestId) as { params: string } | undefined;
|
|
26
|
+
const params = row ? parseJson<unknown>(row.params, {}) : {};
|
|
27
|
+
if (!isRecord(params) || typeof params.prompt !== "string") return;
|
|
28
|
+
const prompt = params.prompt;
|
|
29
|
+
if (!prompt.trim()) return;
|
|
30
|
+
|
|
31
|
+
const cwd = typeof agent.meta?.cwd === "string" ? agent.meta.cwd : undefined;
|
|
32
|
+
const issueRepo = (cwd ? resolveProjectForCwd(cwd)?.issueRepo : undefined) ?? defaultIssueRepo();
|
|
33
|
+
|
|
34
|
+
const seen = new Set<string>();
|
|
35
|
+
for (const match of prompt.matchAll(BARE_ISSUE_REF_RE)) {
|
|
36
|
+
const num = match[1];
|
|
37
|
+
if (!num || seen.has(num)) continue;
|
|
38
|
+
seen.add(num);
|
|
39
|
+
try {
|
|
40
|
+
const issueRef = resolveIssueRefFromString({ ref: `#${num}`, defaultRepo: issueRepo });
|
|
41
|
+
recordAndEmitIssueActivity({
|
|
42
|
+
agentId: agent.id,
|
|
43
|
+
action: "edit",
|
|
44
|
+
issueRefId: issueRef.id,
|
|
45
|
+
clientId: `spawn-issue:${agent.id}:${issueRef.id}`,
|
|
46
|
+
title: "Issue referenced in spawn prompt",
|
|
47
|
+
metadata: { source: "spawn-prompt", spawnRequestId },
|
|
48
|
+
});
|
|
49
|
+
} catch {
|
|
50
|
+
// Unresolvable ref (no issueRepo configured) — skip silently.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Must never break registration.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Inspect a newly created isolated workspace for issue refs from:
|
|
60
|
+
* 1. metadata.issueNumber (set by some spawn flows)
|
|
61
|
+
* 2. branch name (e.g. "agent/build-389-p1-..." → 389)
|
|
62
|
+
* Records "edit" activity for the owning agent. Uses the same stable clientId
|
|
63
|
+
* as recordSpawnPromptIssueActivity so prompt + branch refs for the same issue
|
|
64
|
+
* are deduplicated at the DB layer.
|
|
65
|
+
*/
|
|
66
|
+
export function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): void {
|
|
67
|
+
const agentId = workspace.ownerAgentId;
|
|
68
|
+
if (!agentId || workspace.mode !== "isolated") return;
|
|
69
|
+
|
|
70
|
+
const seen = new Set<string>();
|
|
71
|
+
|
|
72
|
+
const issueNumber = workspace.metadata?.issueNumber;
|
|
73
|
+
if (typeof issueNumber === "number") {
|
|
74
|
+
seen.add(String(Math.trunc(issueNumber)));
|
|
75
|
+
} else if (typeof issueNumber === "string") {
|
|
76
|
+
const t = issueNumber.trim();
|
|
77
|
+
if (/^[0-9]+$/.test(t)) seen.add(t);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (workspace.branch) {
|
|
81
|
+
// Match the first 3+-digit number surrounded by separators in the branch name
|
|
82
|
+
// (e.g. agent/build-389-p1-foo → "389"). Skip 1–2-digit numbers to avoid
|
|
83
|
+
// partition or sequence suffixes (e.g. issue-activity-auto-01 → skip "01").
|
|
84
|
+
const match = workspace.branch.match(/(?:^|[-/])([0-9]{3,})(?:[-/]|$)/);
|
|
85
|
+
if (match?.[1]) seen.add(match[1]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (seen.size === 0) return;
|
|
89
|
+
|
|
90
|
+
const issueRepo = resolveProjectForCwd(workspace.repoRoot)?.issueRepo ?? defaultIssueRepo();
|
|
91
|
+
|
|
92
|
+
for (const num of seen) {
|
|
93
|
+
try {
|
|
94
|
+
const issueRef = resolveIssueRefFromString({ ref: `#${num}`, defaultRepo: issueRepo });
|
|
95
|
+
recordAndEmitIssueActivity({
|
|
96
|
+
agentId,
|
|
97
|
+
action: "edit",
|
|
98
|
+
issueRefId: issueRef.id,
|
|
99
|
+
clientId: `spawn-issue:${agentId}:${issueRef.id}`,
|
|
100
|
+
title: "Issue referenced in workspace",
|
|
101
|
+
metadata: { source: "workspace", workspaceId: workspace.id, branch: workspace.branch },
|
|
102
|
+
});
|
|
103
|
+
} catch {
|
|
104
|
+
// Unresolvable ref — skip silently.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/spawn-targets.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { countLiveSpawnedAgents, getAgent, getOrchestrator, listOrchestrators, listProviderQuotas, ValidationError } from "./db";
|
|
2
2
|
import { DEFAULT_SPAWN_PROFILE_NAME, listAgentProfiles } from "./config-store";
|
|
3
3
|
import { effectiveProviderCatalogList } from "./provider-catalog-store";
|
|
4
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
4
5
|
import { getRoutingPolicy } from "./routing-policy-store";
|
|
5
6
|
import { isPathWithinBase } from "./utils";
|
|
6
7
|
import { McpNotFoundError } from "./mcp-errors";
|
|
@@ -436,17 +437,8 @@ function spawnAlternatives(provider?: SpawnProvider): string {
|
|
|
436
437
|
// stays lazy — fetched only when the agent actually decides to spawn. Returns "" for non-spawn agents.
|
|
437
438
|
export function spawnCapablePrimer(input: { canSpawn: boolean; quota?: number }): string {
|
|
438
439
|
if (!input.canSpawn) return "";
|
|
439
|
-
const
|
|
440
|
-
return
|
|
441
|
-
`You can spawn long-living child agents${quotaLabel}. Shut your own down with relay_shutdown_agent.`,
|
|
442
|
-
"- BEFORE spawning, call relay_spawn_targets: it returns the LIVE matrix of hosts × providers × models × per-model effort, advisory quota burn/penalty metadata, your remaining quota, and named profiles. Don't guess — that tool is the discovery path.",
|
|
443
|
-
"- Default to your OWN host. Only set orchestratorId to spawn onto another host when it runs a provider/model yours doesn't, or the work must run there.",
|
|
444
|
-
`- Prefer a named profile (profile: "<name>") over hand-assembling provider+model+effort+approvalMode. Omitting profile applies ${DEFAULT_SPAWN_PROFILE_NAME}; omitting model/effort uses the provider default. Lean to the cheapest provider/model/effort that fits; escalate only for complex work.`,
|
|
445
|
-
"- Set spawnRequestId for idempotency so a retried spawn doesn't double-create.",
|
|
446
|
-
"- After spawning, use relay_my_agents for your self-scoped child roster + quota and copy a returned spawnRequestId into relay_shutdown_agent when you need to reap.",
|
|
447
|
-
"- Spawn is fire-and-forget: pass waitForRegistrationMs:0 and keep working — you'll be PUSHED agent.ready when the child is genuinely ready (past onboarding), or agent.spawn_failed / agent.exited otherwise. Don't block-poll.",
|
|
448
|
-
"- Spawned children cannot themselves spawn (no grandchildren).",
|
|
449
|
-
].join("\n");
|
|
440
|
+
const quota = typeof input.quota === "number" && input.quota > 0 ? ` (live-children quota: ${input.quota})` : "";
|
|
441
|
+
return renderTemplate("spawn.primer", { quota, defaultProfile: DEFAULT_SPAWN_PROFILE_NAME });
|
|
450
442
|
}
|
|
451
443
|
|
|
452
444
|
// === Orchestrator selection ===
|
package/src/steward.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { stewardFallbackTarget } from "./config";
|
|
|
4
4
|
import { getSpawnPolicy, getStewardConfig, setConfig } from "./config-store";
|
|
5
5
|
import { listOrchestrators } from "./db";
|
|
6
6
|
import { getLifecycleManager } from "./lifecycle-manager";
|
|
7
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
7
8
|
import type { Orchestrator, SpawnPolicy, StewardConfig } from "./types";
|
|
8
9
|
import { isPathWithinBase } from "./utils";
|
|
9
10
|
|
|
@@ -25,29 +26,10 @@ export function buildStewardPrompt(repoRoot: string): string {
|
|
|
25
26
|
// wake/task message via #640), not a static `system` dead-end. The fallback target below is
|
|
26
27
|
// used ONLY when a wake message names no coordinator.
|
|
27
28
|
const fallback = stewardFallbackTarget();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"Multiple agents work this repo in isolated git worktrees. The relay auto-merges any branch that rebases cleanly — including ones whose base moved on; it hands YOU only what it can't land deterministically: real merge conflicts, or an unknown/ambiguous merge state. Your job is to land that work with no human in the loop whenever you safely can.",
|
|
33
|
-
"",
|
|
34
|
-
"Each workspace belongs to a BRANCH AGENT (the owner who did the work) who in turn has a COORDINATOR (the agent that spawned it and is responsible for the merge landing). Your wake/task message names both — the `ownerAgentId` and the `coordinatorAgentId` — and `agent-relay steward inspect <id>` shows them. When you can't land work yourself, the coordinator is who you escalate to.",
|
|
35
|
-
"",
|
|
36
|
-
"Use the `agent-relay` CLI for EVERY relay interaction — it wraps the relay API and is the supported toolkit, and it already attaches your session token with the right scopes. Do NOT hand-curl the HTTP API (`/api/workspaces`, …): the CLI exists so you never have to. If you ever truly must call the API directly, authenticate with the `X-Agent-Relay-Token: <your session token>` header — NOT `Authorization: Bearer $AGENT_RELAY_TOKEN`; the admin env token is not present in your environment, so Bearer will 401.",
|
|
37
|
-
"Handle ONE workspace at a time — the relay's per-repo merge lease serializes you with any other merger.",
|
|
38
|
-
"",
|
|
39
|
-
"1. See the queue: `agent-relay steward queue` (workspaces in conflict / review_requested / merge_planned for this repo). The wake message(s) also name a `workspaceId`.",
|
|
40
|
-
"2. CLAIM it FIRST: `agent-relay workspace claim --id <id> --purpose steward`. This stops the deterministic auto-merge from racing you while you validate. The claim auto-expires, so a crash never blocks the repo.",
|
|
41
|
-
"3. Understand it: `agent-relay steward inspect <id>` — a diagnostics briefing (the branch agent owner + its coordinator, owner liveness, live git state, ahead/behind, recorded-vs-live branch mismatch, and a recommended action). `agent-relay steward checks <id>` suggests check commands from the changed files.",
|
|
42
|
-
"4. `cd` into the workspace's `worktreePath` and rebase the branch onto its `baseRef` (`git rebase <baseRef>`). Resolve conflicts faithfully — preserve the intent of EVERY side; never silently drop a change. If a conflict needs product judgment you can't make confidently, stop and escalate to the coordinator.",
|
|
43
|
-
"5. Run the repo's checks/tests (the suggested ones, plus the project's documented commands). Fix trivial breakage you caused while rebasing; do not paper over real failures.",
|
|
44
|
-
"6. If green, land it: `agent-relay workspace land --id <id> --strategy rebase-ff`. The relay rebases onto the latest base, fast-forwards, and pushes to origin under the lease. The land consumes your claim.",
|
|
45
|
-
"7. If you must stop without landing, RELEASE the claim so automation can resume: `agent-relay workspace release --id <id>`, then send a clear, specific summary to the workspace's COORDINATOR (the `coordinatorAgentId` from the wake message) — they own the work and can act on it. Reply to the wake message or message that agent id directly.",
|
|
46
|
-
"",
|
|
47
|
-
`Escalate (do NOT merge) when: checks fail and you can't fix them, the conflict resolution is genuinely ambiguous, or anything looks risky. ALWAYS escalate to the branch agent's coordinator (the \`coordinatorAgentId\` in the wake message). Only if the wake message names NO coordinator, escalate to ${fallbackTarget}. Never force-merge a red branch, never \`git push --force\` to a shared base, never discard committed work.`,
|
|
48
|
-
"",
|
|
49
|
-
"Be terminal-efficient and decisive. When the queue is empty, you're done — you'll be woken again when there's more.",
|
|
50
|
-
].join("\n");
|
|
29
|
+
return renderTemplate("steward.prompt", {
|
|
30
|
+
repoRoot,
|
|
31
|
+
escalationTarget: fallback ? `\`${fallback}\`` : "the human operator who configured this relay",
|
|
32
|
+
});
|
|
51
33
|
}
|
|
52
34
|
|
|
53
35
|
function desiredStewardPolicy(repoRoot: string, owner: Orchestrator, config: StewardConfig): SpawnPolicy {
|
|
@@ -31,6 +31,7 @@ import { emitCommandEvent } from "./command-events";
|
|
|
31
31
|
import { getLandingConfig, getStewardConfig } from "./config-store";
|
|
32
32
|
import { getLifecycleManager } from "./lifecycle-manager";
|
|
33
33
|
import { routeNotification } from "./notification-router";
|
|
34
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
34
35
|
import { isAwaitingReviewerApproval } from "./reviewer-pipeline";
|
|
35
36
|
import { ensureRepoSteward } from "./steward";
|
|
36
37
|
import { requestWorkspaceMerge } from "./workspace-merge";
|
|
@@ -115,7 +116,17 @@ export function wakeRepoSteward(ws: WorkspaceRecord, reason: string): string | n
|
|
|
115
116
|
recipients: [`policy:${policyName}`],
|
|
116
117
|
message: {
|
|
117
118
|
subject: `Steward: ${ws.status} workspace needs attention`,
|
|
118
|
-
body:
|
|
119
|
+
body: renderTemplate("steward.wake", {
|
|
120
|
+
branch: ws.branch ?? ws.id,
|
|
121
|
+
wsId: ws.id,
|
|
122
|
+
repoRoot: ws.repoRoot,
|
|
123
|
+
status: ws.status,
|
|
124
|
+
reason,
|
|
125
|
+
ownerLine,
|
|
126
|
+
worktreePath: ws.worktreePath ?? ws.sourceCwd,
|
|
127
|
+
baseRef: ws.baseRef ?? "base",
|
|
128
|
+
coordinatorLine,
|
|
129
|
+
}),
|
|
119
130
|
payload: { kind: "workspace.steward-task", workspaceId: ws.id, repoRoot: ws.repoRoot, worktreePath: ws.worktreePath, branch: ws.branch, baseRef: ws.baseRef, status: ws.status, reason, ownerAgentId, coordinatorAgentId },
|
|
120
131
|
},
|
|
121
132
|
});
|
|
@@ -202,7 +213,13 @@ function escalateNoProgressMerge(ws: WorkspaceRecord): void {
|
|
|
202
213
|
recipients,
|
|
203
214
|
message: {
|
|
204
215
|
subject: "Workspace can't auto-land — needs attention",
|
|
205
|
-
body:
|
|
216
|
+
body: renderTemplate("steward.merge-stall", {
|
|
217
|
+
branch: `\`${ws.branch ?? ws.id}\``,
|
|
218
|
+
repoRoot: ws.repoRoot,
|
|
219
|
+
attempts: String(MAX_NO_PROGRESS_MERGE_ATTEMPTS),
|
|
220
|
+
mergeError,
|
|
221
|
+
baseRef: ws.baseRef ?? "base",
|
|
222
|
+
}),
|
|
206
223
|
payload: { kind: "workspace.auto-merge-stuck", workspaceId: ws.id, repoRoot: ws.repoRoot, branch: ws.branch, baseRef: ws.baseRef, attempts: MAX_NO_PROGRESS_MERGE_ATTEMPTS, reason: mergeError },
|
|
207
224
|
},
|
|
208
225
|
});
|
package/src/workspace-phase.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// anti-panic signal.
|
|
16
16
|
|
|
17
17
|
import { TERMINAL_WORKSPACE_STATUS_VALUES } from "agent-relay-sdk";
|
|
18
|
+
import { renderTemplate } from "./prompt-resolver";
|
|
18
19
|
import type { BranchState, WorkspaceRecord, WorkspaceStatus } from "./types";
|
|
19
20
|
import { workspaceActiveClaim } from "./workspace-claim";
|
|
20
21
|
|
|
@@ -148,8 +149,8 @@ export function describeWorkspacePhase(
|
|
|
148
149
|
case "active":
|
|
149
150
|
return {
|
|
150
151
|
phase: "working",
|
|
151
|
-
headline: "
|
|
152
|
-
hint: "
|
|
152
|
+
headline: renderTemplate("workspace.phase.active.headline"),
|
|
153
|
+
hint: renderTemplate("workspace.phase.active.hint"),
|
|
153
154
|
actionNeeded: true,
|
|
154
155
|
nextActions: [READY_ACTION],
|
|
155
156
|
blockers: [],
|
|
@@ -192,8 +193,8 @@ export function describeWorkspacePhase(
|
|
|
192
193
|
}
|
|
193
194
|
return {
|
|
194
195
|
phase: "land-pending",
|
|
195
|
-
headline: "
|
|
196
|
-
hint: "
|
|
196
|
+
headline: renderTemplate("workspace.phase.ready.headline"),
|
|
197
|
+
hint: renderTemplate("workspace.phase.ready.hint"),
|
|
197
198
|
actionNeeded: false,
|
|
198
199
|
nextActions: [CHECK_STATUS_ACTION],
|
|
199
200
|
blockers: [],
|
|
@@ -202,8 +203,8 @@ export function describeWorkspacePhase(
|
|
|
202
203
|
case "merge_planned":
|
|
203
204
|
return {
|
|
204
205
|
phase: "landing",
|
|
205
|
-
headline: "
|
|
206
|
-
hint: "
|
|
206
|
+
headline: renderTemplate("workspace.phase.merge-planned.headline"),
|
|
207
|
+
hint: renderTemplate("workspace.phase.merge-planned.hint"),
|
|
207
208
|
actionNeeded: false,
|
|
208
209
|
nextActions: [WAIT_ACTION],
|
|
209
210
|
blockers: [],
|
|
@@ -211,8 +212,8 @@ export function describeWorkspacePhase(
|
|
|
211
212
|
case "conflict":
|
|
212
213
|
return {
|
|
213
214
|
phase: "steward",
|
|
214
|
-
headline: "
|
|
215
|
-
hint: "
|
|
215
|
+
headline: renderTemplate("workspace.phase.conflict.headline"),
|
|
216
|
+
hint: renderTemplate("workspace.phase.conflict.hint"),
|
|
216
217
|
actionNeeded: false,
|
|
217
218
|
nextActions: [WAIT_ACTION],
|
|
218
219
|
blockers: ["rebase/merge conflict against the base — a steward is handling it"],
|
|
@@ -220,8 +221,8 @@ export function describeWorkspacePhase(
|
|
|
220
221
|
case "merged":
|
|
221
222
|
return {
|
|
222
223
|
phase: "landed",
|
|
223
|
-
headline: "
|
|
224
|
-
hint: "
|
|
224
|
+
headline: renderTemplate("workspace.phase.merged.headline"),
|
|
225
|
+
hint: renderTemplate("workspace.phase.merged.hint"),
|
|
225
226
|
actionNeeded: false,
|
|
226
227
|
nextActions: [],
|
|
227
228
|
blockers: [],
|
|
@@ -231,8 +232,8 @@ export function describeWorkspacePhase(
|
|
|
231
232
|
case "cleaned":
|
|
232
233
|
return {
|
|
233
234
|
phase: "closed",
|
|
234
|
-
headline: "
|
|
235
|
-
hint: "
|
|
235
|
+
headline: renderTemplate("workspace.phase.abandoned.headline"),
|
|
236
|
+
hint: renderTemplate("workspace.phase.abandoned.hint"),
|
|
236
237
|
actionNeeded: false,
|
|
237
238
|
nextActions: [],
|
|
238
239
|
blockers: [],
|
|
@@ -306,14 +307,7 @@ export function deriveBranchState(
|
|
|
306
307
|
// ready, so the whole "what happens next" is stated up front instead of being
|
|
307
308
|
// decoded from status enums over the following minutes (#235).
|
|
308
309
|
export function readyContract(workspace: Pick<WorkspaceRecord, "status">): string {
|
|
309
|
-
return
|
|
310
|
-
`Marked ready. Status is now \`${workspace.status}\` — this is the normal, healthy hand-off state, not an escalation.`,
|
|
311
|
-
"Relay auto-merges clean rebases roughly every 2 minutes. A steward agent is spawned (after a short delay) ONLY if it can't land deterministically — so no steward appearing means it's working, not stuck.",
|
|
312
|
-
"STOP this turn now and go idle. Do not call `relay_workspace_status wait:true`, `agent-relay workspace status --wait`, or any polling loop after marking ready.",
|
|
313
|
-
"Relay will wake you with `landed-success` when the merge lands and your branch is refreshed, or `landed-failure` if landing fatally fails.",
|
|
314
|
-
"On `landed-success` you'll be on a fresh rebased branch (its name gains a `--N` suffix — expected). Keep working there.",
|
|
315
|
-
"Do NOT merge, push, rebase, resolve conflicts, or `cd` into the main checkout — Relay (and the steward) own all of that.",
|
|
316
|
-
].join("\n");
|
|
310
|
+
return renderTemplate("workspace.ready.contract", { status: workspace.status });
|
|
317
311
|
}
|
|
318
312
|
|
|
319
313
|
// Mode-tailored MCP `initialize` instructions primer (#214 §3). The relay's MCP
|
|
@@ -325,14 +319,7 @@ export function readyContract(workspace: Pick<WorkspaceRecord, "status">): strin
|
|
|
325
319
|
export function worktreeMcpInstructions(workspace: Pick<WorkspaceRecord, "branch" | "baseRef">): string {
|
|
326
320
|
const branch = workspace.branch ? `\`${workspace.branch}\`` : "an isolated agent branch";
|
|
327
321
|
const base = workspace.baseRef ? `\`${workspace.baseRef}\`` : "the base branch";
|
|
328
|
-
return
|
|
329
|
-
`You are in an isolated git worktree on branch ${branch}, based on ${base} — NOT the main checkout. ${base} moves under you as other agents land in parallel; that's expected.`,
|
|
330
|
-
"Your shell's working directory is already this worktree — run commands directly without prefixing `cd`.",
|
|
331
|
-
"Changes reach the base via: commit your work, then call `relay_workspace_ready`. Relay rebases onto the latest base, lands, and pushes for you.",
|
|
332
|
-
"Do NOT push, rebase, merge, resolve conflicts, or `cd` into the main checkout — Relay (and a steward, spawned only if a clean auto-merge isn't possible) own all of that.",
|
|
333
|
-
"After `ready` the status is `ready` (a normal, healthy hand-off state, not a stall). Stop your turn and go idle; Relay wakes you with `landed-success` when your branch lands and refreshes, or `landed-failure` if landing fatally fails.",
|
|
334
|
-
"Call `relay_workspace_status` anytime to see where you are and the exact next step.",
|
|
335
|
-
].join("\n");
|
|
322
|
+
return renderTemplate("workspace.mcp.instructions", { branch, baseRef: base });
|
|
336
323
|
}
|
|
337
324
|
|
|
338
325
|
// Positive land receipt for the wait-result path: when a long-poll observes the
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{$t as t,An as n,B as r,Cn as i,Dt as a,Fn as o,Hn as s,L as c,Ot as l,Qn as u,Qt as d,Sn as f,Un as p,V as m,Vt as h,Xn as g,_n as _,a as v,b as y,c as ee,dr as b,er as x,ft as S,g as C,hn as te,i as ne,ln as w,lt as T,m as E,on as D,r as re,un as O,vn as k,x as A,xn as ie,yn as j}from"./lucide-react-BYZuAhQs.js";import{r as M,t as N,y as P}from"./store-zeyDiSKE.js";import{t as F}from"./types-DorDDvEb.js";import{E as I,H as ae,N as L,P as R,R as oe,S as se,V as ce,_ as le,d as ue,f as de,k as z,q as B,rt as V,v as fe}from"./display-D1xB8NJZ.js";import{D as H,a as pe,i as me,l as he,t as U}from"./index-CN7QZlMb.js";import{t as ge}from"./user-avatar-GFWMuAGK.js";var W=e(b(),1),_e={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`},ve=new Set(Object.keys(_e)),ye=3e3;function G(e){return new Date(e.occurredAt??e.createdAt).getTime()}function be(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:G(e),turnId:typeof t.turnId==`string`?t.turnId:void 0}}function xe(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:G(e)}}function Se(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`relay-injection`)return null;let n=t.relayInjection&&typeof t.relayInjection==`object`&&!Array.isArray(t.relayInjection)?t.relayInjection:{},r=typeof n.category==`string`&&[`memory`,`instructions`,`tools`,`comms`,`knowledge`,`context`].includes(n.category)?n.category:`context`,i=typeof n.timestamp==`number`&&Number.isFinite(n.timestamp)?n.timestamp:G(e),a=n.detail&&typeof n.detail==`object`&&!Array.isArray(n.detail)?n.detail:{};return{id:e.id,category:r,summary:typeof n.summary==`string`&&n.summary.trim()?n.summary:e.body,detail:a,agentId:typeof n.agentId==`string`&&n.agentId?n.agentId:e.from,timestamp:i}}function Ce(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`prompt-resolution`)return null;let n=t.promptResolution&&typeof t.promptResolution==`object`&&!Array.isArray(t.promptResolution)?t.promptResolution:{},r=n.kind===`questions`||n.kind===`plan`||n.kind===`command`||n.kind===`tool`?n.kind:`tool`,i=typeof n.title==`string`&&n.title.trim()?n.title:`Prompt resolved`,a=typeof n.decisionLabel==`string`&&n.decisionLabel.trim()?n.decisionLabel:typeof t.label==`string`&&t.label.trim()?t.label:`Resolved`,o=n.answers&&typeof n.answers==`object`&&!Array.isArray(n.answers)?n.answers:{};return{id:e.id,title:i,kind:r,body:typeof n.promptBody==`string`?n.promptBody:typeof n.body==`string`?n.body:e.body,toolName:typeof n.toolName==`string`&&n.toolName.trim()?n.toolName:i,decisionLabel:a,questions:we(n.questions,o,a),timestamp:G(e)}}function we(e,t,n){let r=(Array.isArray(e)?e:[]).map((e,r)=>{let i=e&&typeof e==`object`&&!Array.isArray(e)?e:{},a=typeof i.question==`string`&&i.question.trim()?i.question.trim():typeof i.header==`string`&&i.header.trim()?i.header.trim():`Question ${r+1}`,o=t[a]??t[String(i.question??``)]??t[String(i.header??``)];return{question:a,answer:typeof o==`string`&&o.trim()?o.trim():n}});return r.length?r:Object.entries(t).map(([e,t])=>({question:e,answer:t}))}function Te(e,t){let n=e.trim();for(let e of t){let t=e.trim();if(t){if(n===t)break;n.startsWith(`${t}\n\n`)&&(n=n.slice(t.length+2).trimStart())}}return n.trim()||e.trim()}function Ee(e){return e.kind===`system`&&e.from===`system`}var De=new Set([`agent.ready`,`agent.exited`,`agent.spawn_failed`]);function K(e){let t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`}function Oe(e){let t=e.filter(e=>ve.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)<=ye)||n.push(e);return n}function ke(e,t,n,r=[],i){let a=[];for(let e of r){let t=e.entries.reduce((e,t)=>Math.min(e,B(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:B(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=i?.spawnedBy?.agentId,s=null,c=1/0;for(let t of e){if(t.kind===`session`||Ee(t)||I(t)||!(t.from===`user`||o!==void 0&&t.from===o)||!z(t).trim())continue;let e=G(t);e<c&&(c=e,s=t.id)}let l=new Map,u=new Map,d=new Map;for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type===`narration`&&typeof e.turnId==`string`){let n=u.get(e.turnId)??[];n.push({id:t.id,body:t.body.trim()}),u.set(e.turnId,n)}else if(e?.type===`response`&&typeof e.turnId==`string`){let n=l.get(e.turnId)??[];n.push(t.body.trim()),l.set(e.turnId,n)}}let f=new Map;for(let[e,t]of l){let n=(u.get(e)??[]).map(e=>e.body);f.set(e,t.map(e=>Te(e,n)))}for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type!==`response`||typeof e.turnId!=`string`)continue;let n=Te(t.body,(u.get(e.turnId)??[]).map(e=>e.body));n&&n!==t.body&&d.set(t.id,n)}let p=new Set;for(let[e,t]of f){let n=u.get(e)??[];for(let e of t)for(let t=n.length-1;t>=0;t--){let r=n[t];if(!p.has(r.id)&&ae(e,r.body)){p.add(r.id);break}}}let m=new Map,h=[];for(let t of e){if(Ee(t)){let e=typeof t.payload?.kind==`string`?t.payload.kind:``;if(De.has(e)){let n=t.payload,r=typeof n.agentId==`string`?n.agentId:``,i=G(t);if(!r){a.push({ts:i,entry:{type:`notice`,msg:t}});continue}let o=e===`agent.ready`?`ready`:e===`agent.exited`?`exited`:`failed`,s=m.get(r);if(s)s.entry.status=o,typeof n.reason==`string`&&(s.entry.reason=n.reason),s.entry.timestamp=i,s.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}};m.set(r,e),a.push(e)}continue}a.push({ts:G(t),entry:{type:`notice`,msg:t}});continue}let e=xe(t);if(e){h.push(e);continue}let n=be(t);if(n){if(n.kind===`narration`&&p.has(n.id))continue;a.push({ts:n.ts,entry:{type:`activity`,steps:[n],timestamp:n.ts}});continue}let r=Se(t);if(r){a.push({ts:r.timestamp,entry:{type:`relay-injection`,event:r}});continue}let i=Ce(t);if(i){a.push({ts:i.timestamp,entry:{type:`prompt-resolution`,event:i}});continue}if(t.kind===`session`){let e=t.payload?.session;if(e?.type===`response`&&typeof e.turnId==`string`){let e=d.get(t.id);if(e&&e!==t.body){a.push({ts:G(t),entry:{type:`message`,msg:{...t,body:e}}});continue}}}let o=t.id===s?`initial-prompt`:`message`;a.push({ts:G(t),entry:{type:o,msg:t}})}for(let e of Oe([...t,...h]))a.push({ts:e.timestamp,entry:{type:`status`,event:e}});let g={created:0,date:1,"import-boundary":2,"imported-message":3,"initial-prompt":4,message:4,activity:4,notice:4,"agent-action":4,"relay-injection":4,"prompt-resolution":4,status:5};a.sort((e,t)=>{let n=e.ts-t.ts;return Math.abs(n)<3e3&&g[e.entry.type]-g[t.entry.type]||n});let _=[],v=``;for(let{ts:e,entry:t}of a){if(t.type===`created`){_.push(t);continue}let n=K(e);if(n!==v&&(_.push({type:`date`,date:n,timestamp:e}),v=n),t.type===`activity`){let e=_[_.length-1];if(e&&e.type===`activity`&&Me(e,t)){e.steps.push(...t.steps);continue}}_.push(t)}return _}function Ae(e){return e.type===`message`?`msg-${e.msg.id}`:e.type===`initial-prompt`?`initprompt-${e.msg.id}`:e.type===`notice`?`notice-${e.msg.id}`:e.type===`relay-injection`?`relay-injection-${e.event.id}`:e.type===`prompt-resolution`?`prompt-resolution-${e.event.id}`:e.type===`agent-action`?`agent-action-${e.direction}-${e.agentId}`:e.type===`activity`?`act-${e.steps[0]?.id??e.timestamp}`:e.type===`import-boundary`?`import-${e.history.id}`:e.type===`imported-message`?`import-msg-${e.history.id}-${e.entry.originalMessageId}-${e.entry.position}`:e.type===`status`?`status-${e.event.id}`:e.type===`date`?`date-${e.date}`:`created-${e.timestamp}`}function je(e){return e.type===`message`||e.type===`initial-prompt`||e.type===`notice`?(e.msg.subject?`${e.msg.subject} `:``)+z(e.msg):e.type===`imported-message`?z(e.entry.message):e.type===`activity`?e.steps.map(e=>[e.label,e.text].filter(Boolean).join(` `)).join(`
|
|
2
|
-
`):e.type===`relay-injection`?`${e.event.category} ${e.event.summary}\n${JSON.stringify(e.event.detail)}`:e.type===`prompt-resolution`?[e.event.title,e.event.decisionLabel,e.event.body,...e.event.questions.map(e=>`${e.question} ${e.answer}`)].filter(Boolean).join(`
|
|
3
|
-
`):``}function Me(e,t){let n=e.steps[e.steps.length-1]?.turnId,r=t.steps[0]?.turnId;return n&&r?n===r:!n&&!r}function Ne(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}function Pe(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 Fe(e){let t=e.replace(/\/+$/,``),n=t.lastIndexOf(`/`);return n>0?t.slice(0,n):`/`}function Ie(e,t){return e.filter(e=>e.status===`online`&&oe(t,e.baseDir)).sort((e,t)=>L(t.baseDir).length-L(e.baseDir).length)[0]||e.find(e=>e.status===`online`)}function Le(){return typeof window<`u`&&window.matchMedia(`(min-width: 1280px)`).matches}function Re(){return typeof window<`u`&&!!window.getSelection()?.toString()}function ze(e){let t=e.payload?.attachments;return Array.isArray(t)?t.filter(e=>!!(e&&typeof e==`object`&&typeof e.artifactId==`string`)):[]}var Be=[`👍`,`❤️`,`✅`,`👀`];function Ve(e){let t=new Map;for(let n of e.reactions||[]){let e=R(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 I(e)}function He(e,t){let n=e.readBy||[],r=e.resolvedToAgent||t||e.to,a=r?n.includes(r):!1,o=n.some(e=>e!==V);if(a||o){let e=a?r:n.find(e=>e!==V);return{label:`read`,title:e?`Read by ${e}`:`Read by agent`,tone:`ok`,icon:i}}if(e.deliveryStatus===`delivered`)return{label:`delivered`,title:`Delivered to agent inbox`,tone:`ok`,icon:i};if(e.deliveryStatus===`queued`)return{label:`queued`,title:`Queued until the target agent is available`,tone:`warn`,icon:D};if(e.deliveryStatus===`failed`||e.deliveryStatus===`dead`){let t=e.deliveryPoisonReason||e.deliveryLastError||e.deliveryStatus;return{label:e.deliveryStatus,title:t,tone:`danger`,icon:_}}return{label:`sent`,title:`Accepted by Relay`,tone:`muted`,icon:f}}function Ue(e){let t=B(e.createdAt);return(e.reactions||[]).reduce((e,t)=>Math.max(e,B(t.updatedAt||t.createdAt)),t)}function We(e){return e?e.messages.reduce((e,t)=>Math.max(e,Ue(t)),0):0}function Ge(e,t,n){return(e-t)*n}function Ke(e){if(!e)return 0;let t=0;for(let n of e.messages)se(n)&&(t=Math.max(t,B(n.createdAt)));return t}function J(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}function qe(e){for(let t of e)t.previewUrl&&URL.revokeObjectURL(t.previewUrl)}function Je(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[M(e.threadId,e.channel)])??a[0];return o?.threadId?{threadId:o.threadId,...o.channel?{channel:o.channel}:{}}:null}var Y=u(),Ye={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 Xe(e){let t=new Date,[n,r,i]=e.split(`-`).map(Number),a=new Date(n,r-1,i);if(e===K(t.getTime()))return`Today`;let o=new Date(t);return o.setDate(o.getDate()-1),e===K(o.getTime())?`Yesterday`:a.toLocaleDateString(void 0,{weekday:`short`,month:`short`,day:`numeric`,year:a.getFullYear()===t.getFullYear()?void 0:`numeric`})}function Ze({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)(w,{className:g(`w-2 h-2 fill-current`,Ye[e.status]||`text-zinc-400`)}),(0,Y.jsx)(`span`,{children:_e[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 Qe={"branch.landed":a,"context.warning":E,"agent.context_threshold":E,"agent.resume_budget_exhausted":E,"agent.rate_limit_resume":m},$e={"branch.landed":`branch landed`,"context.warning":`context warning`,"agent.context_threshold":`context threshold reached`};function et(e){return e&&(e.includes(`.`)?e.slice(e.indexOf(`.`)+1):e).replace(/[._-]+/g,` `).trim()||`system signal`}function X({agentId:e,fallbackLabel:t,className:n,noIcon:r}){let i=N(t=>t.agentsById[e])||null,a=N(e=>e.openInboxThread),o=N(e=>e.switchView),s=i?ue(i):t||e;return(0,Y.jsxs)(`button`,{type:`button`,className:g(`inline-flex max-w-full items-center gap-1 rounded px-1 text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground`,n),onClick:t=>{t.stopPropagation(),a(e),o(`chat`)},title:`Open chat for ${s}`,children:[!r&&(0,Y.jsx)(H,{agent:i,className:`w-4 h-4 text-[10px]`}),(0,Y.jsx)(`span`,{className:`max-w-[160px] truncate`,children:s})]})}function tt({msg:e}){let t=e.payload??{},n=rt(e),r=typeof t.sha==`string`?t.sha:``,i=r?r.slice(0,7):``,o=typeof t.author==`string`?t.author:``,s=typeof t.branch==`string`?t.branch:``,c=typeof t.base==`string`?t.base:``,l=t.pushed;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 max-w-full flex-wrap items-center justify-center gap-x-1.5 gap-y-0.5 rounded-full border border-emerald-600/35 bg-emerald-500/10 px-2.5 py-1 text-xs text-emerald-700 dark:border-emerald-500/25 dark:text-emerald-300/90`,children:[(0,Y.jsx)(a,{className:`h-3 w-3 shrink-0 text-emerald-600 dark:text-emerald-400`}),(0,Y.jsx)(`span`,{className:`text-emerald-700 dark:text-emerald-300`,children:`branch landed`}),s&&c&&(0,Y.jsxs)(`span`,{className:`font-mono text-emerald-700/80 dark:text-emerald-200/70`,title:`${s} → ${c}`,children:[s,(0,Y.jsx)(`span`,{className:`opacity-50`,children:` → `}),c]}),i&&(0,Y.jsx)(`span`,{className:`rounded bg-emerald-500/15 px-1 font-mono text-emerald-800 dark:text-emerald-200`,title:r,children:i}),o&&(0,Y.jsxs)(`span`,{className:`inline-flex items-center gap-1 text-emerald-700/80 dark:text-emerald-300/80`,children:[(0,Y.jsx)(`span`,{className:`opacity-70`,children:`by`}),(0,Y.jsx)(X,{agentId:o,className:`text-emerald-700 hover:text-emerald-900 dark:text-emerald-300/90 dark:hover:text-emerald-100`})]}),l===!1&&(0,Y.jsx)(`span`,{className:`text-amber-600 dark:text-amber-300/80`,title:`Local-only land — not pushed to origin`,children:`· local only`}),n&&(0,Y.jsxs)(`span`,{className:`max-w-[220px] truncate text-emerald-700/70 dark:text-emerald-300/70`,title:n,children:[`· because `,n]}),(0,Y.jsx)(`span`,{className:`text-emerald-700/60 dark:text-emerald-300/50`,children:J(e.occurredAt??e.createdAt)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function nt({msg:e}){let t=typeof e.payload?.kind==`string`?e.payload.kind:``;if(t===`branch.landed`)return(0,Y.jsx)(tt,{msg:e});let n=Qe[t]??c,r=$e[t]??et(t),i=rt(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 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}),i&&(0,Y.jsxs)(`span`,{className:`max-w-[220px] truncate text-muted-foreground/80`,title:i,children:[`· because `,i]}),(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`})]})}function rt(e){let t=e.payload?.routing;if(!t||typeof t!=`object`||Array.isArray(t))return``;let n=t.reason;return typeof n==`string`?n:``}var it={"spawned-child":{ready:`spawned`,exited:`spawned · exited`,failed:`spawn failed`},"spawned-by":{ready:`spawned by`,exited:`spawned by`,failed:`spawned by`}},at={"spawned-child-ready":r,"spawned-child-exited":T,"spawned-child-failed":_,"spawned-by-ready":p,"spawned-by-exited":p,"spawned-by-failed":p},ot={"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 st({entry:e}){let t=`${e.direction}-${e.status}`,n=it[e.direction]?.[e.status]??e.direction,r=at[t]??c,i=ot[t]??`text-muted-foreground`,a=e.status===`failed`;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:g(`flex items-center gap-1.5 rounded-full border bg-muted/30 px-2.5 py-1 text-xs`,a?`border-red-500/30`:`border-border`),children:[(0,Y.jsx)(r,{className:g(`h-3 w-3 shrink-0`,i)}),(0,Y.jsx)(`span`,{className:`text-muted-foreground`,children:n}),(0,Y.jsx)(X,{agentId:e.agentId}),e.reason&&a&&(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`})]})}var ct={memory:n,instructions:h,tools:v,comms:c,knowledge:o,context:t},lt={memory:`border-emerald-500/25 bg-emerald-500/10 text-emerald-300`,instructions:`border-sky-500/25 bg-sky-500/10 text-sky-300`,tools:`border-amber-500/25 bg-amber-500/10 text-amber-300`,comms:`border-violet-500/25 bg-violet-500/10 text-violet-300`,knowledge:`border-cyan-500/25 bg-cyan-500/10 text-cyan-300`,context:`border-zinc-500/25 bg-zinc-500/10 text-zinc-300`};function ut(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function dt({entry:e}){let[t,n]=(0,W.useState)(!1),r=e.event,i=ct[r.category]??y;return(0,Y.jsx)(`div`,{className:`flex justify-center py-2 my-1`,children:(0,Y.jsxs)(`div`,{className:g(`w-full max-w-[85%] md:max-w-[75%] rounded-lg border px-2.5 py-1.5 text-xs shadow-sm`,lt[r.category]??lt.context),children:[(0,Y.jsxs)(`button`,{type:`button`,className:`flex w-full min-w-0 items-center gap-1.5 text-left`,onClick:()=>n(e=>!e),children:[(0,Y.jsx)(i,{className:`h-3.5 w-3.5 shrink-0`}),(0,Y.jsx)(`span`,{className:`shrink-0 font-medium`,children:r.category}),(0,Y.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-foreground/80`,children:r.summary}),(0,Y.jsx)(`span`,{className:`shrink-0 text-muted-foreground/60`,children:J(r.timestamp)}),(0,Y.jsx)(j,{className:g(`h-3.5 w-3.5 shrink-0 transition-transform`,t&&`rotate-90`)})]}),t&&(0,Y.jsx)(`pre`,{className:`mt-2 max-h-72 overflow-auto rounded-md border border-border/60 bg-background/75 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground whitespace-pre-wrap break-words`,children:ut(r.detail)})]})})}function ft({blockedLabel:e,onInterrupt:t}){return e?(0,Y.jsx)(`div`,{className:`flex justify-start mb-3 animate-in fade-in slide-in-from-bottom-1 duration-150`,children:(0,Y.jsxs)(`div`,{className:`flex h-9 items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-red-500/30 px-4`,children:[(0,Y.jsx)(_,{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 animate-in fade-in slide-in-from-bottom-1 duration-150`,children:(0,Y.jsxs)(`div`,{className:`flex h-9 items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-foreground/10 px-4`,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`}),t&&(0,Y.jsxs)(`button`,{onClick:t,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)(O,{className:`w-3.5 h-3.5`}),`Stop`]})]})})}var Z=`agent-relay:chat-hide-tools`,Q=(()=>{let e=new Set,t=(()=>{try{return window.localStorage.getItem(Z)===`on`}catch{return!1}})(),n=()=>e.forEach(e=>e());return typeof window<`u`&&window.addEventListener(`storage`,e=>{e.key===Z&&(t=e.newValue===`on`,n())}),{subscribe(t){return e.add(t),()=>e.delete(t)},get:()=>t,toggle(){t=!t;try{window.localStorage.setItem(Z,t?`on`:`off`)}catch{}n()}}})();function pt(){return[(0,W.useSyncExternalStore)(Q.subscribe,Q.get,()=>!1),Q.toggle]}function mt({steps:e,showReasoning:t,issueRepo:r}){let[i,a]=pt(),o=t?e:e.filter(e=>e.kind!==`reasoning`);if(!o.length)return null;let s=o.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:[o.map(e=>e.kind===`narration`?(0,Y.jsx)(U,{text:e.text,issueRepo:r,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)(n,{className:`w-3.5 h-3.5 mt-0.5 shrink-0 opacity-70`}),(0,Y.jsx)(U,{text:e.text,issueRepo:r,className:`italic break-words min-w-0`})]},e.id):i?null:(0,Y.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground`,children:[(0,Y.jsx)(C,{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&&e.text.trim()&&e.text.trim()!==(e.label??``).trim()?(0,Y.jsxs)(`span`,{className:`opacity-70`,children:[` — `,e.text]}):null]})]},e.id)),s>0&&(0,Y.jsxs)(`button`,{onClick:a,className:`flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left`,children:[(0,Y.jsx)(j,{className:g(`w-3 h-3 shrink-0 transition-transform`,!i&&`rotate-90`)}),(0,Y.jsx)(`span`,{children:i?`show ${s} tool step${s===1?``:`s`}`:`hide tool steps`})]})]})})}function ht({entry:e}){let{event:t}=e,n=t.kind===`questions`,r=n?t.title:t.kind===`plan`?`Plan prompt`:`${t.toolName} permission`;return(0,Y.jsx)(`div`,{className:`flex justify-center py-1.5`,children:(0,Y.jsxs)(`div`,{className:`w-full max-w-2xl rounded-md border border-border bg-muted/20 px-3 py-2 text-sm`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,Y.jsx)(te,{className:`h-3.5 w-3.5 text-emerald-500`}),(0,Y.jsx)(`span`,{className:`font-medium text-foreground`,children:r}),(0,Y.jsx)(`span`,{children:t.decisionLabel}),(0,Y.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/60`,children:J(t.timestamp)})]}),n&&t.questions.length>0?(0,Y.jsx)(`div`,{className:`mt-2 space-y-1.5`,children:t.questions.map((e,t)=>(0,Y.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,Y.jsx)(`div`,{className:`text-xs text-muted-foreground`,children:e.question}),(0,Y.jsx)(`div`,{className:`text-sm text-foreground`,children:e.answer})]},`${e.question}-${t}`))}):t.body.trim()?(0,Y.jsx)(`div`,{className:`mt-2 max-h-48 overflow-auto rounded border border-border/70 bg-background/40 px-2 py-1.5 text-sm`,children:(0,Y.jsx)(U,{text:t.body})}):null]})})}function gt({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:Xe(e)}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function _t({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 vt({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)(l,{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 yt({entry:e,sourceLabel:t,issueRepo:n}){let r=e.message,i=z(r),a=e.originalFrom===V;return(0,Y.jsx)(`div`,{className:g(`flex mb-3 opacity-80`,a?`justify-end`:`justify-start`),children:(0,Y.jsx)(`div`,{className:`max-w-[85%] md:max-w-[75%]`,children:(0,Y.jsxs)(`div`,{className:g(`rounded-2xl border border-dashed px-3.5 py-2 text-sm select-text`,a?`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:g(`mb-1 text-[10px] uppercase tracking-normal`,a?`text-primary/70`:`text-muted-foreground`),children:[`forked history · `,a?`you`:t||e.originalFrom]}),(0,Y.jsx)(U,{text:i,rawBody:r.body,issueRepo:n,className:`leading-relaxed`}),(0,Y.jsxs)(`p`,{className:g(`mt-1 flex items-center justify-end gap-1.5 text-xs`,a?`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 $({msg:e,issueRepo:t}){let n=z(e),r=J(e.occurredAt??e.createdAt);return(0,Y.jsx)(`div`,{"data-msg-id":e.id,className:`mb-3`,children:(0,Y.jsxs)(`div`,{className:`rounded-xl border border-primary/30 bg-primary/[0.06] px-3.5 py-2.5 shadow-sm`,children:[(0,Y.jsxs)(`div`,{className:`mb-1.5 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-primary/80`,children:[(0,Y.jsx)(y,{className:`h-3.5 w-3.5`}),(0,Y.jsx)(`span`,{children:`Initial prompt`}),(0,Y.jsx)(`span`,{className:`ml-auto text-[10px] font-normal normal-case text-muted-foreground/60`,children:r})]}),(0,Y.jsx)(U,{text:n,rawBody:e.body,issueRepo:t,className:`text-sm leading-relaxed text-foreground`})]})})}function bt({scrollEl:e,timeline:t}){let[n,r]=(0,W.useState)(!0),[i,a]=(0,W.useState)(null),[o,s]=(0,W.useState)(null),[c,l]=(0,W.useState)(!1),u=(0,W.useMemo)(()=>{for(let e=t.length-1;e>=0;e--){let n=t[e];if(!(!n||n.type!==`message`&&n.type!==`initial-prompt`)&&n.msg.from===`user`)return{id:n.msg.id,body:z(n.msg)}}return null},[t]);if((0,W.useEffect)(()=>{if(!u||!e){r(!0);return}let t=null,n=null,i=()=>{let i=e.querySelector(`[data-msg-id="${u.id}"]`);if(!i){t?.disconnect(),t=null,n=null,r(!1);return}let a=i.getBoundingClientRect(),o=e.getBoundingClientRect();l(a.top<o.top),i!==n&&(t?.disconnect(),n=i,t=new IntersectionObserver(([e])=>{e&&r(e.isIntersecting)},{root:e,threshold:0}),t.observe(i))};return i(),e.addEventListener(`scroll`,i,{passive:!0}),()=>{t?.disconnect(),e.removeEventListener(`scroll`,i)}},[u?.id,e]),n||!u||!u.body.trim())return null;let d=i?.id===u.id?i.value:!1,f=o?.id===u.id?o.value:!1;function p(){!e||!u||e.querySelector(`[data-msg-id="${u.id}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`})}let m=c?`↑`:`↓`;return f?(0,Y.jsx)(`div`,{className:`flex shrink-0 bg-card/45 border-b border-border`,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 transition-colors`,onClick:()=>s({id:u.id,value:!1}),children:[(0,Y.jsx)(`span`,{children:`Your prompt`}),(0,Y.jsx)(ie,{className:`w-3 h-3`})]})}):(0,Y.jsxs)(`section`,{className:`flex shrink-0 flex-col border-b border-border bg-card/45 px-3 py-2`,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:p,children:[m,` scroll to original`]}),(0,Y.jsx)(`button`,{type:`button`,className:`hover:text-foreground transition-colors`,onClick:()=>s({id:u.id,value:!0}),title:`Minimize`,children:(0,Y.jsx)(k,{className:`w-3 h-3`})})]}),(0,Y.jsx)(`p`,{className:g(`text-sm text-foreground leading-snug cursor-pointer`,!d&&`line-clamp-2`),onClick:()=>a({id:u.id,value:!d}),children:u.body})]})}function xt(){return(0,W.useSyncExternalStore)(e=>P.subscribe(e),()=>P.getPlayingKey(),()=>null)}function St({msg:e,peer:t}){return(0,Y.jsx)($,{msg:e,issueRepo:N(e=>e.agentsById[t]?.issueRepo)})}function Ct({agent:e,onClick:t}){let n=!!(t&&e);return(0,Y.jsx)(`button`,{type:`button`,title:e?e.label||e.name||e.id:void 0,onClick:t,disabled:!n,className:g(`shrink-0 self-end transition-opacity`,n?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,Y.jsx)(H,{agent:e})})}function wt({agentId:e,onOpen:t}){let n=N(t=>t.agentsById[e]??null);return(0,Y.jsx)(Ct,{agent:n,onClick:n?()=>t(n):void 0})}function Tt({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[i,a]=(0,W.useState)(null),[o,s]=(0,W.useState)(null),[c,l]=(0,W.useState)(!1),[u,f]=(0,W.useState)(!1);(0,W.useEffect)(()=>{let t=!1,n=null;async function r(){l(!0),s(null),a(null);try{let r=await x(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),a(n)}catch(e){t||s(e.message)}finally{t||l(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function p(){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&&f(!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)(re,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,Y.jsxs)(`span`,{className:g(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,o?`text-red-400`:`text-muted-foreground`),children:[c?(0,Y.jsx)(S,{className:`h-4 w-4 animate-spin`}):(0,Y.jsx)(_,{className:`h-4 w-4`}),o||`Loading image`]})}),i&&(0,Y.jsx)(me,{open:u,onOpenChange:f,children:(0,Y.jsxs)(pe,{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)(he,{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:p,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:()=>f(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,Y.jsx)(ne,{className:`w-4 h-4`})})]})]})]})})]}):(0,Y.jsxs)(`button`,{type:`button`,onClick:p,disabled:!i,className:g(`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:[c?(0,Y.jsx)(S,{className:`h-4 w-4 shrink-0 animate-spin`}):o?(0,Y.jsx)(_,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,Y.jsx)(h,{className:`h-4 w-4 shrink-0`}),(0,Y.jsx)(`span`,{className:g(`min-w-0 flex-1 truncate`,o&&`text-red-400`),children:o||r}),(0,Y.jsx)(d,{className:`h-3.5 w-3.5 shrink-0`})]})}function Et({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:g(`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)(A,{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:Be.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))})]})}function Dt({msg:e,peer:t}){let[n,r]=(0,W.useState)(!1),i=N(e=>e.agentsById),a=N(e=>e.openAgentDetail),o=e.from===t,c=(o?e.to:e.from)||``,l=i[o?t:c]?.issueRepo??i[t]?.issueRepo,u=!c||c===`broadcast`||c.includes(`:`),d=z(e),f=J(e.occurredAt??e.createdAt),m=o?s:p,h=i[e.from??``]??null;function _(e){e.stopPropagation(),h&&a(h)}return(0,Y.jsx)(`div`,{"data-msg-id":e.id,className:`flex justify-center mb-1.5`,children:(0,Y.jsxs)(`div`,{className:`w-full max-w-[94%] rounded-md border border-border/50 bg-muted/15 px-2.5 py-1`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs cursor-pointer select-none`,onClick:()=>r(e=>!e),children:[(0,Y.jsx)(j,{className:g(`h-3 w-3 shrink-0 text-muted-foreground/60 transition-transform`,n&&`rotate-90`)}),(0,Y.jsx)(`button`,{type:`button`,title:h?h.label||h.name||h.id:void 0,disabled:!h,onClick:_,className:g(`shrink-0 transition-opacity`,h?`cursor-pointer hover:opacity-80`:`cursor-default opacity-60`),children:(0,Y.jsx)(H,{agent:h,className:`!w-4 !h-4 !rounded-sm opacity-70`})}),(0,Y.jsx)(m,{className:`h-3 w-3 shrink-0 text-muted-foreground/70`}),(0,Y.jsxs)(`span`,{className:`shrink-0 text-muted-foreground`,children:[`Message `,o?`to`:`from`]}),u?(0,Y.jsx)(`span`,{className:`shrink-0 font-medium text-muted-foreground`,children:de(c,i)}):(0,Y.jsx)(X,{agentId:c,className:`shrink-0 px-0.5 text-foreground/80`,noIcon:!0}),!n&&(0,Y.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-muted-foreground/50`,children:d}),(0,Y.jsx)(`span`,{className:`ml-auto shrink-0 text-muted-foreground/40`,children:f})]}),n&&(0,Y.jsx)(`div`,{className:`mt-1.5 pl-5`,children:(0,Y.jsx)(U,{text:d,rawBody:e.body,issueRepo:l,className:`text-sm leading-relaxed text-foreground/90`})})]})})}var Ot=(0,W.memo)(function({msg:e,peer:t,showAvatar:n=!1,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){if(!F(e.from)&&!F(e.to))return(0,Y.jsx)(Dt,{msg:e,peer:t});let o=e.kind===`session`?e.payload?.session:void 0;if(o?.type===`prompt`&&o?.origin===`provider`)return(0,Y.jsx)(St,{msg:e,peer:t});let s=F(e.from),c=N(e=>e.reactToMessage),l=N(e=>e.voiceTtsEnabled),u=xt(),d=N(e=>{let n=e.agentsById[t]?.meta?.cwd;return typeof n==`string`?n:``}),f=N(e=>e.agentsById[t]?.issueRepo),p=N(e=>e.usersById),m=N(e=>e.users.length>1),h=s?ce(e.from,p):void 0,_=N(e=>e.openAgentDetail),[v,y]=(0,W.useState)(!1),b=(0,W.useRef)(null),x=(0,W.useRef)(null),S=(0,W.useRef)(!1),C=z(e),te=J(e.occurredAt??e.createdAt),ne=ze(e),w=Ve(e),T=s?He(e,t):null,E=T?.icon,D=u===String(e.id),re=l&&!s&&!q(e)&&C.trim().length>0;function k(t){t.stopPropagation(),D?P.bargeIn():P.speak(e.body,String(e.id))}(0,W.useEffect)(()=>{if(!v)return;function e(e){Re()||b.current&&!b.current.contains(e.target)&&y(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[v]);function A(e){return Pe(d||`/`,e)}function ie(e,t){let n=A(e);r?.(n,t)}function j(e,t,n){let r=A(e);i?.(r,t,n)}function M(t){c(e,t),y(!1)}function I(e){e.button===0&&(x.current={x:e.clientX,y:e.clientY},S.current=!1)}function ae(e){let t=x.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(S.current=!0)}function L(t){if(!(q(e)||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(S.current||Re()){S.current=!1;return}y(e=>!e)}}let R=n&&!q(e);return(0,Y.jsxs)(`div`,{"data-msg-id":e.id,className:g(`group/msg flex mb-3 items-end`,s?`justify-end`:`justify-start`,R&&`gap-1.5`),children:[!s&&R&&(0,Y.jsx)(wt,{agentId:t,onOpen:_}),(0,Y.jsxs)(`div`,{ref:b,className:g(`relative`,R?`max-w-[calc(85%-2rem)] md:max-w-[calc(75%-2rem)]`:`max-w-[85%] md:max-w-[75%]`),children:[re&&(0,Y.jsx)(`button`,{type:`button`,title:D?`Stop playback`:`Play aloud`,onClick:k,className:g(`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`,D?`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:D?(0,Y.jsx)(O,{className:`h-3.5 w-3.5 animate-pulse`}):(0,Y.jsx)(ee,{className:`h-3.5 w-3.5`})}),(0,Y.jsxs)(`div`,{className:g(`rounded-2xl px-3.5 py-2 text-sm select-text`,!s&&!q(e)&&`cursor-pointer`,s?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:I,onPointerMove:ae,onClick:L,children:[s&&m&&(0,Y.jsx)(`p`,{className:g(`text-[11px] font-semibold mb-0.5 text-right`,le(e.from)),children:fe(e.from,p)}),e.subject&&(0,Y.jsx)(`p`,{className:g(`text-xs font-semibold mb-1`,s?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,Y.jsx)(U,{text:C,rawBody:e.body,issueRepo:f,className:`leading-relaxed`,onPrimary:s,onOpenPath:ie,onPreviewPath:j,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${A(e)}${t?`:${t}`:``}`}),ne.map(t=>(0,Y.jsx)(Tt,{attachment:t,outbound:s},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,Y.jsxs)(`p`,{className:g(`text-xs mt-1 flex items-center gap-1.5`,s?`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:te}),T&&E&&(0,Y.jsxs)(`span`,{className:g(`inline-flex items-center gap-1`,T.tone===`ok`&&`text-emerald-300`,T.tone===`warn`&&`text-amber-200`,T.tone===`danger`&&`text-red-200`),title:T.title,children:[(0,Y.jsx)(E,{className:`h-3 w-3`}),T.label]})]})]}),!q(e)&&w.length>0&&(0,Y.jsxs)(`div`,{className:g(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,s?`justify-end pr-2`:`justify-start pl-2`),children:[!s&&(0,Y.jsx)(Et,{open:v,onToggle:()=>y(e=>!e),onReact:M}),w.map(e=>(0,Y.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>M(e.emoji),className:g(`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)&&!s&&w.length===0&&(0,Y.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,Y.jsx)(Et,{open:v,onToggle:()=>y(e=>!e),onReact:M})})]}),s&&R&&(0,Y.jsx)(`span`,{className:`shrink-0 self-end opacity-60`,children:(0,Y.jsx)(ge,{user:h,address:e.from,distinct:m})})]})});export{We as C,je as E,Le as S,Ae as T,Ke as _,_t as a,Je as b,yt as c,ht as d,dt as f,Ne as g,Ge as h,ft as i,$ as l,bt as m,mt as n,gt as o,Ze as p,st as r,vt as s,Ot as t,nt as u,Ie as v,ke as w,qe as x,Fe as y};
|
|
4
|
-
//# sourceMappingURL=MessageBubble-BpiTbbnp.js.map
|