agent-relay-server 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/public/index.html +36 -2
- package/src/agent-ref.ts +3 -3
- package/src/cli.ts +3 -3
- package/src/contracts.ts +1 -1
- package/src/db.ts +5 -5
- package/src/http-body.ts +1 -1
- package/src/insights-db.ts +2 -2
- package/src/lifecycle-manager.ts +4 -0
- package/src/managed-policy.ts +1 -1
- package/src/notify.ts +1 -1
- package/src/orchestrator-lookup.ts +1 -1
- package/src/routes.ts +22 -6
- package/src/runtime-tokens.ts +2 -2
- package/src/security.ts +8 -0
- package/src/spawn-command.ts +2 -2
- package/src/token-db.ts +3 -3
- package/src/upgrade.ts +1 -1
- package/src/workspace-actions.ts +5 -5
- package/src/workspace-claim.ts +2 -2
- package/src/workspace-merge.ts +2 -2
- package/src/workspace-orphans.ts +1 -1
- package/src/workspace-phase.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Lightweight HTTP message relay for inter-agent communication across machines",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"CONTRIBUTING.md"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"agent-relay-sdk": "0.2.
|
|
36
|
+
"agent-relay-sdk": "0.2.18"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"prepack": "bun run build:dashboard:bundle >&2",
|
package/public/index.html
CHANGED
|
@@ -128066,8 +128066,42 @@ function BusyIndicator({ blockedLabel, onInterrupt }) {
|
|
|
128066
128066
|
})
|
|
128067
128067
|
});
|
|
128068
128068
|
}
|
|
128069
|
+
var HIDE_TOOLS_KEY = "agent-relay:chat-hide-tools";
|
|
128070
|
+
var hideToolsStore = (() => {
|
|
128071
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
128072
|
+
let value = (() => {
|
|
128073
|
+
try {
|
|
128074
|
+
return window.localStorage.getItem(HIDE_TOOLS_KEY) === "on";
|
|
128075
|
+
} catch {
|
|
128076
|
+
return false;
|
|
128077
|
+
}
|
|
128078
|
+
})();
|
|
128079
|
+
const emit = () => listeners.forEach((l) => l());
|
|
128080
|
+
if (typeof window !== "undefined") window.addEventListener("storage", (e) => {
|
|
128081
|
+
if (e.key !== HIDE_TOOLS_KEY) return;
|
|
128082
|
+
value = e.newValue === "on";
|
|
128083
|
+
emit();
|
|
128084
|
+
});
|
|
128085
|
+
return {
|
|
128086
|
+
subscribe(cb) {
|
|
128087
|
+
listeners.add(cb);
|
|
128088
|
+
return () => listeners.delete(cb);
|
|
128089
|
+
},
|
|
128090
|
+
get: () => value,
|
|
128091
|
+
toggle() {
|
|
128092
|
+
value = !value;
|
|
128093
|
+
try {
|
|
128094
|
+
window.localStorage.setItem(HIDE_TOOLS_KEY, value ? "on" : "off");
|
|
128095
|
+
} catch {}
|
|
128096
|
+
emit();
|
|
128097
|
+
}
|
|
128098
|
+
};
|
|
128099
|
+
})();
|
|
128100
|
+
function useHideTools() {
|
|
128101
|
+
return [(0, import_react.useSyncExternalStore)(hideToolsStore.subscribe, hideToolsStore.get, () => false), hideToolsStore.toggle];
|
|
128102
|
+
}
|
|
128069
128103
|
function ActivityTrace({ steps, showReasoning }) {
|
|
128070
|
-
const [hideTools,
|
|
128104
|
+
const [hideTools, toggleHideTools] = useHideTools();
|
|
128071
128105
|
const visible = showReasoning ? steps : steps.filter((s) => s.kind !== "reasoning");
|
|
128072
128106
|
if (!visible.length) return null;
|
|
128073
128107
|
const toolCount = visible.filter((s) => s.kind === "tool").length;
|
|
@@ -128102,7 +128136,7 @@ function ActivityTrace({ steps, showReasoning }) {
|
|
|
128102
128136
|
})]
|
|
128103
128137
|
}, step.id);
|
|
128104
128138
|
}), toolCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
128105
|
-
onClick:
|
|
128139
|
+
onClick: toggleHideTools,
|
|
128106
128140
|
className: "flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left",
|
|
128107
128141
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: cn$2("w-3 h-3 shrink-0 transition-transform", !hideTools && "rotate-90") }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: hideTools ? `show ${toolCount} tool step${toolCount === 1 ? "" : "s"}` : "hide tool steps" })]
|
|
128108
128142
|
})]
|
package/src/agent-ref.ts
CHANGED
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
import { STALE_TTL_MS } from "./config";
|
|
15
15
|
import type { AgentCard } from "./types";
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
interface ResolveOptions {
|
|
18
18
|
/** Exclude this agent id from matches (e.g. the requester, when pairing). */
|
|
19
19
|
excludeId?: string;
|
|
20
20
|
/** Clock injection for tests. */
|
|
21
21
|
now?: number;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
type ResolveResult =
|
|
25
25
|
| { status: "resolved"; agent: AgentCard }
|
|
26
26
|
| { status: "ambiguous"; candidates: AgentCard[] }
|
|
27
27
|
| { status: "not_found"; offlineMatches: AgentCard[] };
|
|
@@ -136,7 +136,7 @@ export interface DeliveryReceipt {
|
|
|
136
136
|
reason?: string;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
type SendPlan =
|
|
140
140
|
// resolved/fan-out/passthrough carry the (possibly rewritten) canonical `to`
|
|
141
141
|
| { kind: "direct" | "fanout" | "passthrough"; to: string; receipt: DeliveryReceipt }
|
|
142
142
|
| { kind: "not_found"; message: string }
|
package/src/cli.ts
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from "./upgrade";
|
|
45
45
|
import { formatMemoryBrokerSmokeResult, runMemoryBrokerSmoke } from "./memory-broker-smoke";
|
|
46
46
|
import { MAX_BODY_BYTES, VERSION } from "./config";
|
|
47
|
-
import {
|
|
47
|
+
import { runContextProbe } from "agent-relay-sdk/context-probe";
|
|
48
48
|
import { shellQuote } from "agent-relay-sdk/shell-utils";
|
|
49
49
|
import { errMessage, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
|
|
50
50
|
import type { WorkspaceDepsRefreshResult } from "agent-relay-sdk";
|
|
@@ -686,7 +686,7 @@ async function handleContextProbeCommand(args: string[]): Promise<void> {
|
|
|
686
686
|
let wrapCommand: string | undefined;
|
|
687
687
|
let wrapRequested = false;
|
|
688
688
|
let agentId: string | undefined;
|
|
689
|
-
let stateDir
|
|
689
|
+
let stateDir: string | undefined;
|
|
690
690
|
let standalone = false;
|
|
691
691
|
|
|
692
692
|
for (let i = 0; i < inputArgs.length; i++) {
|
|
@@ -718,7 +718,7 @@ async function handleContextProbeCommand(args: string[]): Promise<void> {
|
|
|
718
718
|
"context-probe",
|
|
719
719
|
...(wrapRequested ? ["--wrap", ...(wrapCommand ? [shellQuote(wrapCommand)] : [])] : ["--standalone"]),
|
|
720
720
|
...(agentId ? ["--agent-id", shellQuote(agentId)] : []),
|
|
721
|
-
...(stateDir
|
|
721
|
+
...(stateDir ? ["--state-dir", shellQuote(stateDir)] : []),
|
|
722
722
|
].join(" ");
|
|
723
723
|
console.log(command);
|
|
724
724
|
return;
|
package/src/contracts.ts
CHANGED
package/src/db.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { isRecord, stringValue } from "agent-relay-sdk";
|
|
3
|
+
import { isRecord, stringValue, isMechanicalMessageKind } from "agent-relay-sdk";
|
|
4
4
|
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "./config.ts";
|
|
5
5
|
import { parseJson } from "./utils";
|
|
6
6
|
import {
|
|
@@ -4331,7 +4331,7 @@ function messageRequiresReply(message: Message): boolean {
|
|
|
4331
4331
|
// Server-owned notification flag (#283) wins over every kind/sender heuristic below: an
|
|
4332
4332
|
// explicit replyExpected:false is a fire-and-forget message that must never become an obligation.
|
|
4333
4333
|
if (message.replyExpected === false) return false;
|
|
4334
|
-
if (message.kind
|
|
4334
|
+
if (isMechanicalMessageKind(message.kind)) return false;
|
|
4335
4335
|
if (message.from === "user") return true;
|
|
4336
4336
|
if (message.kind === "task" || message.kind === "channel.event") return true;
|
|
4337
4337
|
return Boolean(message.payload?.source);
|
|
@@ -4793,7 +4793,7 @@ export type AnalyticsPeriod = keyof typeof ANALYTICS_PERIODS;
|
|
|
4793
4793
|
// Message → category, the ONE place this mapping lives (server-side SQL). Order is
|
|
4794
4794
|
// significant: a claimable/system/pair/channel message is classified as such even
|
|
4795
4795
|
// when it is also a reply; only an otherwise-plain reply counts as "Replies".
|
|
4796
|
-
|
|
4796
|
+
const ANALYTICS_CATEGORIES = ["Messages", "Replies", "Work items", "System", "Pair", "Channel"] as const;
|
|
4797
4797
|
export type AnalyticsCategory = (typeof ANALYTICS_CATEGORIES)[number];
|
|
4798
4798
|
const ANALYTICS_CATEGORY_SQL = `
|
|
4799
4799
|
CASE
|
|
@@ -5453,7 +5453,7 @@ export function setWorkspaceBranch(id: string, branch: string, baseSha?: string)
|
|
|
5453
5453
|
// of these is a candidate steward; the repo is worth coordinating.
|
|
5454
5454
|
const STEWARD_LIVE_STATUSES = "'active', 'ready', 'conflict', 'review_requested', 'merge_planned'";
|
|
5455
5455
|
|
|
5456
|
-
|
|
5456
|
+
interface RepoStewardRecord {
|
|
5457
5457
|
repoRoot: string;
|
|
5458
5458
|
stewardAgentId?: string;
|
|
5459
5459
|
lastStewardAgentId?: string;
|
|
@@ -5570,7 +5570,7 @@ function electWorkspaceStewardsForAgent(agentId: string): void {
|
|
|
5570
5570
|
|
|
5571
5571
|
// --- Per-repo merge serialization lease (issue #157) -----------------------
|
|
5572
5572
|
|
|
5573
|
-
|
|
5573
|
+
interface MergeLeaseRecord {
|
|
5574
5574
|
repoRoot: string;
|
|
5575
5575
|
workspaceId: string;
|
|
5576
5576
|
commandId?: string;
|
package/src/http-body.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Concatenate body chunks into a single contiguous Uint8Array. */
|
|
2
|
-
|
|
2
|
+
function concatBytes(chunks: Uint8Array[]): Uint8Array {
|
|
3
3
|
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
4
4
|
const output = new Uint8Array(total);
|
|
5
5
|
let offset = 0;
|
package/src/insights-db.ts
CHANGED
|
@@ -46,7 +46,7 @@ function rowToObservation(row: ObservationRow): InsightObservation {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
interface RecordObservationInput {
|
|
50
50
|
sessionId: string;
|
|
51
51
|
agentId?: string;
|
|
52
52
|
project?: string;
|
|
@@ -99,7 +99,7 @@ export function getObservation(id: number): InsightObservation | null {
|
|
|
99
99
|
return row ? rowToObservation(row) : null;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
interface ListObservationsQuery {
|
|
103
103
|
project?: string;
|
|
104
104
|
signal?: string;
|
|
105
105
|
sessionId?: string;
|
package/src/lifecycle-manager.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
upsertManagedAgentState,
|
|
9
9
|
} from "./config-store";
|
|
10
10
|
import { emitRelayEvent } from "./events";
|
|
11
|
+
import { emitMessageDeliveryUpdated } from "./sse";
|
|
11
12
|
import { emitCommandEvent } from "./command-events";
|
|
12
13
|
import { buildManagedSpawnParams } from "./managed-policy";
|
|
13
14
|
import { generateSpawnRequestId } from "./spawn-command";
|
|
@@ -95,6 +96,9 @@ export class LifecycleManager {
|
|
|
95
96
|
subject: `policy:${meta.policyName}`,
|
|
96
97
|
data: { policyName: meta.policyName, agentId, messageIds: available.map((message) => message.id), count: available.length },
|
|
97
98
|
});
|
|
99
|
+
// queued → pending changed delivery_status; refresh the dashboard delivery
|
|
100
|
+
// badge now rather than letting it sit stale until the next poll (#265).
|
|
101
|
+
for (const message of available) emitMessageDeliveryUpdated(message);
|
|
98
102
|
}
|
|
99
103
|
}
|
|
100
104
|
|
package/src/managed-policy.ts
CHANGED
|
@@ -20,7 +20,7 @@ export function effectiveManagedPolicyWorkspaceMode(policy: SpawnPolicy): Worksp
|
|
|
20
20
|
return policy.binding?.type === "channel" ? "shared" : "inherit";
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
interface ManagedSpawnContext {
|
|
24
24
|
createdBy: string;
|
|
25
25
|
requestedAt?: number;
|
|
26
26
|
}
|
package/src/notify.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { sendMessage } from "./db";
|
|
|
2
2
|
import { emitNewMessage } from "./sse";
|
|
3
3
|
import type { Message, MessageKind } from "./types";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
interface SystemNotifyOptions {
|
|
6
6
|
subject?: string;
|
|
7
7
|
body: string;
|
|
8
8
|
payload?: Record<string, unknown>;
|
package/src/routes.ts
CHANGED
|
@@ -163,7 +163,7 @@ import { planSend } from "./agent-ref";
|
|
|
163
163
|
import { defaultProviderConfig, loadProviderConfig, providerConfigPublic, writeProviderConfig } from "../runner/src/config";
|
|
164
164
|
import type { ProviderConfig } from "../runner/src/adapter";
|
|
165
165
|
import { type ProviderEffort } from "agent-relay-sdk/provider-catalog";
|
|
166
|
-
import { errMessage, isRecord, SPAWN_PROVIDERS, VALID_WORKSPACE_MODES, VALID_EFFORTS, APPROVAL_MODES, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
|
|
166
|
+
import { errMessage, isRecord, SPAWN_PROVIDERS, VALID_WORKSPACE_MODES, VALID_EFFORTS, APPROVAL_MODES, RELAY_TOKEN_HEADER, isMechanicalMessageKind, isReservedAgentId } from "agent-relay-sdk";
|
|
167
167
|
import { effectiveProviderCatalogList } from "./provider-catalog-store";
|
|
168
168
|
import { buildManagedSpawnParams, effectiveManagedPolicyWorkspaceMode } from "./managed-policy";
|
|
169
169
|
import { buildSpawnCommand, generateSpawnRequestId, resolveSpawnModelParams, type SpawnModelParams } from "./spawn-command";
|
|
@@ -1282,7 +1282,13 @@ const postAgent: Handler = async (req) => {
|
|
|
1282
1282
|
const available = resolveQueuedPolicyMessages(policyName, agent.id);
|
|
1283
1283
|
if (available.length) {
|
|
1284
1284
|
emitMessageAvailable(policyName, agent.id, available);
|
|
1285
|
-
for (const message of available)
|
|
1285
|
+
for (const message of available) {
|
|
1286
|
+
emitNewMessage(message);
|
|
1287
|
+
// queued → pending flips delivery_status; the dashboard dedups message.new
|
|
1288
|
+
// by id (the message already shows as "queued"), so without an explicit
|
|
1289
|
+
// delivery_updated the badge stays stale until the next poll (#265).
|
|
1290
|
+
emitMessageDeliveryUpdated(message);
|
|
1291
|
+
}
|
|
1286
1292
|
}
|
|
1287
1293
|
}
|
|
1288
1294
|
}
|
|
@@ -5181,9 +5187,20 @@ const postMessage: Handler = async (req) => {
|
|
|
5181
5187
|
}
|
|
5182
5188
|
applyReplyRouting(input);
|
|
5183
5189
|
if (!input.to) return error("to is required (or provide replyTo to auto-route)");
|
|
5190
|
+
// Mechanical lifecycle/observability posts (system/control/session) addressed to a
|
|
5191
|
+
// reserved sink ("user"/"system") are the relay's own lane, not agent-directed
|
|
5192
|
+
// messaging. A managed token's recipient constraints (targets/policies/agents) gate
|
|
5193
|
+
// which *agents* it may message — they must NOT gate a session-mirror capture to the
|
|
5194
|
+
// reserved sink, or constrained tokens (telegram policy, codex steward) 403 → the
|
|
5195
|
+
// runner's outbox retries 12× and poisons the record → the dashboard silently loses
|
|
5196
|
+
// the turn (#284, same outbox-poison failure mode as #184). The message:send scope
|
|
5197
|
+
// and any channel constraint still apply; we only drop the target/agentId predicate.
|
|
5198
|
+
const reservedSinkPost = isMechanicalMessageKind(input.kind) && isReservedAgentId(input.to);
|
|
5184
5199
|
const denied = authorizeRoute(req, {
|
|
5185
5200
|
scope: "message:send",
|
|
5186
|
-
resource:
|
|
5201
|
+
resource: reservedSinkPost
|
|
5202
|
+
? { channel: input.channel }
|
|
5203
|
+
: { target: input.to, channel: input.channel, agentId: input.from },
|
|
5187
5204
|
});
|
|
5188
5205
|
if (denied) return denied;
|
|
5189
5206
|
// Resolve the target through the shared planner — the SAME matcher the MCP send tool
|
|
@@ -5198,8 +5215,7 @@ const postMessage: Handler = async (req) => {
|
|
|
5198
5215
|
// "session" = observed assistant turn (Phase 1 live-session lane). It is captured
|
|
5199
5216
|
// from the provider transcript and stored for the dashboard chat; it must persist
|
|
5200
5217
|
// regardless of target liveness and never be re-delivered into a session.
|
|
5201
|
-
|
|
5202
|
-
if (!bypassKinds.includes(input.kind ?? "")) {
|
|
5218
|
+
if (!isMechanicalMessageKind(input.kind)) {
|
|
5203
5219
|
const plan = planSend(input.to, listAgents());
|
|
5204
5220
|
if (plan.kind === "ambiguous") return error(plan.message, 409);
|
|
5205
5221
|
if (plan.kind !== "not_found") input.to = plan.to;
|
|
@@ -5249,7 +5265,7 @@ const postMessage: Handler = async (req) => {
|
|
|
5249
5265
|
};
|
|
5250
5266
|
|
|
5251
5267
|
function automaticMemoryTarget(message: { to: string; resolvedToAgent?: string; kind: string }): string | null {
|
|
5252
|
-
if (message.kind
|
|
5268
|
+
if (isMechanicalMessageKind(message.kind)) return null;
|
|
5253
5269
|
const target = message.resolvedToAgent ?? message.to;
|
|
5254
5270
|
if (!isDirectTarget(target)) return null;
|
|
5255
5271
|
const agent = getAgent(target);
|
package/src/runtime-tokens.ts
CHANGED
|
@@ -118,7 +118,7 @@ export function issueRunnerRuntimeToken(input: {
|
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
function issueChildRunnerRuntimeToken(input: {
|
|
122
122
|
parentAgentId: string;
|
|
123
123
|
orchestratorId: string;
|
|
124
124
|
cwd: string;
|
|
@@ -239,7 +239,7 @@ export function runnerRuntimeTokenEnv(input: {
|
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
|
|
242
|
+
function childRunnerRuntimeTokenEnv(input: {
|
|
243
243
|
parentAgentId: string;
|
|
244
244
|
orchestratorId: string;
|
|
245
245
|
cwd: string;
|
package/src/security.ts
CHANGED
|
@@ -200,6 +200,9 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
200
200
|
if (pathname.startsWith("/api/maintenance")) return "system:admin";
|
|
201
201
|
if (pathname.startsWith("/api/tasks")) return method === "GET" ? "task:read" : "task:write";
|
|
202
202
|
if (pathname.startsWith("/api/pairs")) return method === "GET" ? "pairs:read" : "pairs:write";
|
|
203
|
+
// Insights config (the feature toggle) stays admin-only via the default; only the
|
|
204
|
+
// mechanical observation feed is writable by lower-privilege callers.
|
|
205
|
+
if (pathname === "/api/insights/observations") return method === "GET" ? "insights:read" : "insights:write";
|
|
203
206
|
if (pathname.startsWith("/api/system/")) return "system:admin";
|
|
204
207
|
return null;
|
|
205
208
|
}
|
|
@@ -268,6 +271,11 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
268
271
|
if (pathname.startsWith("/api/agent-profiles")) return method === "GET" ? "agent:read" : "agent:write";
|
|
269
272
|
if (pathname.startsWith("/api/tasks")) return method === "GET" ? "task:read" : "task:write";
|
|
270
273
|
if (pathname.startsWith("/api/orchestrators")) return method === "GET" ? "agent:read" : "command:write";
|
|
274
|
+
// The Runner posts the #184 context-gathering signal here (source:"server") via its
|
|
275
|
+
// provider token. Without this case the path fell through to the system:admin default
|
|
276
|
+
// below and every observation was 403-dropped — the whole Insights feed stayed empty.
|
|
277
|
+
// Config (the feature toggle) intentionally keeps falling through to system:admin.
|
|
278
|
+
if (pathname === "/api/insights/observations") return method === "GET" ? "insights:read" : "insights:write";
|
|
271
279
|
if (pathname.startsWith("/api/system/")) return "system:admin";
|
|
272
280
|
return "system:admin";
|
|
273
281
|
}
|
package/src/spawn-command.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function generateSpawnRequestId(): string {
|
|
|
17
17
|
return `sp_${randomUUID()}`;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
interface ResolveSpawnModelParamsOptions {
|
|
21
21
|
/**
|
|
22
22
|
* What to do when provider-catalog resolution throws (e.g. unknown model, or
|
|
23
23
|
* effort without a model):
|
|
@@ -69,7 +69,7 @@ export function resolveSpawnModelParams(
|
|
|
69
69
|
* Every field that can appear in a spawn command-bus payload.
|
|
70
70
|
* Optional fields are omitted from the result when undefined.
|
|
71
71
|
*/
|
|
72
|
-
|
|
72
|
+
interface BuildSpawnCommandOptions {
|
|
73
73
|
provider: SpawnProvider | string;
|
|
74
74
|
cwd: string;
|
|
75
75
|
/** Correlation id; omitted from the payload when absent (e.g. automation spawns use automationRunId instead). */
|
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"],
|
|
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", "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"],
|
|
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", "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"],
|
|
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", "insights:write"],
|
|
82
82
|
constraints: { terminalAttach: false, logsRead: false, canDelegate: false },
|
|
83
83
|
ttlSeconds: 30 * 24 * 60 * 60,
|
|
84
84
|
builtIn: true,
|
package/src/upgrade.ts
CHANGED
|
@@ -249,7 +249,7 @@ export function createUpgradePlan(snapshot: UpgradeSnapshot, options: UpgradeOpt
|
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
type ExecuteUpgradeOptions = {
|
|
253
253
|
dryRun?: boolean;
|
|
254
254
|
runner?: Runner;
|
|
255
255
|
/** Re-register grace window for post-restart version checks (default 30s). */
|
package/src/workspace-actions.ts
CHANGED
|
@@ -41,7 +41,7 @@ export const WORKSPACE_ACTIONS = [
|
|
|
41
41
|
] as const;
|
|
42
42
|
export type WorkspaceAction = (typeof WORKSPACE_ACTIONS)[number];
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
interface ApplyWorkspaceActionInput {
|
|
45
45
|
action: WorkspaceAction;
|
|
46
46
|
agentId?: string;
|
|
47
47
|
detail?: string;
|
|
@@ -62,7 +62,7 @@ export interface ApplyWorkspaceActionInput {
|
|
|
62
62
|
auditMetadata?: Record<string, unknown>;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
type WorkspaceActionResult =
|
|
66
66
|
| {
|
|
67
67
|
ok: true;
|
|
68
68
|
httpStatus: number;
|
|
@@ -320,10 +320,10 @@ export function buildWorkspaceDepsRefreshCommand(
|
|
|
320
320
|
return { ok: true, command };
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
const DEFAULT_WORKSPACE_WAIT_MS = 300_000;
|
|
324
|
+
const MAX_WORKSPACE_WAIT_MS = 600_000;
|
|
325
325
|
|
|
326
|
-
|
|
326
|
+
interface WaitForWorkspaceResult {
|
|
327
327
|
workspace: WorkspaceRecord | null;
|
|
328
328
|
/** The status when the wait began. */
|
|
329
329
|
fromStatus?: WorkspaceStatus;
|
package/src/workspace-claim.ts
CHANGED
|
@@ -4,9 +4,9 @@ import type { WorkspaceRecord } from "./types";
|
|
|
4
4
|
// auto-merge (Layer 0) doesn't race it (#208 / steward report §1). The claim is a
|
|
5
5
|
// TTL'd lease stored in row metadata, so a dead steward can't block the workspace
|
|
6
6
|
// forever — it expires and auto-merge resumes. Renew by re-claiming.
|
|
7
|
-
|
|
7
|
+
const STEWARD_CLAIM_TTL_MS = Number(process.env.AGENT_RELAY_WORKSPACE_CLAIM_TTL_MS) || 15 * 60_000;
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface WorkspaceClaim {
|
|
10
10
|
by?: string;
|
|
11
11
|
purpose?: string;
|
|
12
12
|
claimedAt?: number;
|
package/src/workspace-merge.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { Command, WorkspaceMergeStrategy, WorkspaceRecord } from "./types";
|
|
11
11
|
import { isPathWithinBase } from "./utils";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
interface RequestWorkspaceMergeOptions {
|
|
14
14
|
/** Who asked for the merge (lease holder + audit). e.g. an agent id, "dashboard", "auto-merge". */
|
|
15
15
|
requestedBy: string;
|
|
16
16
|
/** Merge strategy; "auto" lets the host pick pr-vs-rebase-ff. Defaults to "auto". */
|
|
@@ -26,7 +26,7 @@ export interface RequestWorkspaceMergeOptions {
|
|
|
26
26
|
metadata?: Record<string, unknown>;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
type RequestWorkspaceMergeResult =
|
|
30
30
|
| { ok: true; command: Command; workspace: WorkspaceRecord }
|
|
31
31
|
| { ok: false; status: number; error: string };
|
|
32
32
|
|
package/src/workspace-orphans.ts
CHANGED
|
@@ -141,7 +141,7 @@ function knownRepoRoots(workspaces: WorkspaceRecord[]): string[] {
|
|
|
141
141
|
return [...new Set(workspaces.map((ws) => ws.repoRoot).filter(Boolean))];
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
interface CollectOrphansResult {
|
|
145
145
|
orphans: WorkspaceOrphan[];
|
|
146
146
|
/** Live isolated rows whose worktree is missing on disk (DB→disk drift). */
|
|
147
147
|
missingWorktrees: Array<{
|
package/src/workspace-phase.ts
CHANGED
|
@@ -71,7 +71,7 @@ export function worktreeReapable(state: WorktreeReapState | null | undefined): b
|
|
|
71
71
|
// instead of the old behavior where it looked healthy for 90 minutes.
|
|
72
72
|
export const LAND_PENDING_STALL_MS = 15 * 60 * 1000;
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
type WorkspacePhase =
|
|
75
75
|
| "working" // active — your turn: commit, then mark ready
|
|
76
76
|
| "land-pending" // ready | review_requested — handed off; auto-merge will land it
|
|
77
77
|
| "landing" // merge_planned — merge dispatched, in progress
|
|
@@ -79,7 +79,7 @@ export type WorkspacePhase =
|
|
|
79
79
|
| "landed" // merged — on the base; a fresh rebased branch is coming
|
|
80
80
|
| "closed"; // abandoned | cleanup_requested | cleaned — torn down
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
interface WorkspaceNextAction {
|
|
83
83
|
/** MCP tool to call (when the agent is on the MCP surface). */
|
|
84
84
|
tool?: string;
|
|
85
85
|
/** Equivalent CLI invocation (when the agent is on the shell surface). */
|