agent-relay-server 0.60.0 → 0.61.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/docs/openapi.json +516 -1
- package/package.json +3 -2
- package/public/assets/MessageBubble-CX63ZV4X.js +2 -0
- package/public/assets/MessageBubble-CX63ZV4X.js.map +1 -0
- package/public/assets/{PlanGraphPanel-CXix1C-H.js → PlanGraphPanel-Dby7wq9c.js} +2 -2
- package/public/assets/{PlanGraphPanel-CXix1C-H.js.map → PlanGraphPanel-Dby7wq9c.js.map} +1 -1
- package/public/assets/{activity-CRafQ1Je.js → activity-2xPOScu_.js} +2 -2
- package/public/assets/{activity-CRafQ1Je.js.map → activity-2xPOScu_.js.map} +1 -1
- package/public/assets/{agent-profiles-DW4o5-ND.js → agent-profiles-BfGTTbeY.js} +2 -2
- package/public/assets/{agent-profiles-DW4o5-ND.js.map → agent-profiles-BfGTTbeY.js.map} +1 -1
- package/public/assets/agents-Bg3WOBRV.js +2 -0
- package/public/assets/{agents-Cbegv0Hm.js.map → agents-Bg3WOBRV.js.map} +1 -1
- package/public/assets/{analytics-DX6T7JX3.js → analytics-De0cEZGx.js} +3 -3
- package/public/assets/{analytics-DX6T7JX3.js.map → analytics-De0cEZGx.js.map} +1 -1
- package/public/assets/{automation-CzSBv4gg.js → automation-CHxhzq2t.js} +2 -2
- package/public/assets/{automation-CzSBv4gg.js.map → automation-CHxhzq2t.js.map} +1 -1
- package/public/assets/{badge-Cy6qO_Jz.js → badge-DhLyCPag.js} +2 -2
- package/public/assets/{badge-Cy6qO_Jz.js.map → badge-DhLyCPag.js.map} +1 -1
- package/public/assets/branch-state-badge-DSyWKluD.js +2 -0
- package/public/assets/{branch-state-badge-CRA50r81.js.map → branch-state-badge-DSyWKluD.js.map} +1 -1
- package/public/assets/{button-73wpRw65.js → button-K20wAZG1.js} +2 -2
- package/public/assets/{button-73wpRw65.js.map → button-K20wAZG1.js.map} +1 -1
- package/public/assets/card-CcZ9hLI7.js +2 -0
- package/public/assets/{card-BU6PvVHv.js.map → card-CcZ9hLI7.js.map} +1 -1
- package/public/assets/{channels-B1MQCPCT.js → channels-BG0Nib7y.js} +2 -2
- package/public/assets/{channels-B1MQCPCT.js.map → channels-BG0Nib7y.js.map} +1 -1
- package/public/assets/chat-CjP_crSl.js +2 -0
- package/public/assets/{chat-BqIaFYGJ.js.map → chat-CjP_crSl.js.map} +1 -1
- package/public/assets/{connectors-C9aFJIrh.js → connectors-DWh7MwV_.js} +2 -2
- package/public/assets/{connectors-C9aFJIrh.js.map → connectors-DWh7MwV_.js.map} +1 -1
- package/public/assets/{copy-button-HRopUwui.js → copy-button-eF3cKp4Q.js} +2 -2
- package/public/assets/{copy-button-HRopUwui.js.map → copy-button-eF3cKp4Q.js.map} +1 -1
- package/public/assets/display-DzNvOu1j.js +3 -0
- package/public/assets/{display-CketawEF.js.map → display-DzNvOu1j.js.map} +1 -1
- package/public/assets/{dist-Kkpnjx_M.js → dist-1dhfdhm9.js} +2 -2
- package/public/assets/{dist-Kkpnjx_M.js.map → dist-1dhfdhm9.js.map} +1 -1
- package/public/assets/dist-BLBF7qqr.js +2 -0
- package/public/assets/{dist-De2oguBm.js.map → dist-BLBF7qqr.js.map} +1 -1
- package/public/assets/{dist-T7u7IZGJ.js → dist-BrQsqBbc.js} +2 -2
- package/public/assets/{dist-T7u7IZGJ.js.map → dist-BrQsqBbc.js.map} +1 -1
- package/public/assets/{dist-Cko2KPW8.js → dist-COQuunEv.js} +2 -2
- package/public/assets/{dist-Cko2KPW8.js.map → dist-COQuunEv.js.map} +1 -1
- package/public/assets/{dist-CLHMeXYi.js → dist-Ci5Y37nj.js} +2 -2
- package/public/assets/{dist-CLHMeXYi.js.map → dist-Ci5Y37nj.js.map} +1 -1
- package/public/assets/{dist-Ci1MM51P.js → dist-CoROafNg.js} +2 -2
- package/public/assets/{dist-Ci1MM51P.js.map → dist-CoROafNg.js.map} +1 -1
- package/public/assets/{dist-CC5f7oYw.js → dist-I-MFUv51.js} +2 -2
- package/public/assets/{dist-CC5f7oYw.js.map → dist-I-MFUv51.js.map} +1 -1
- package/public/assets/{dist-DFLciw0h.js → dist-UQGKwqZH.js} +2 -2
- package/public/assets/{dist-DFLciw0h.js.map → dist-UQGKwqZH.js.map} +1 -1
- package/public/assets/dist-YpHfnnIA.js +2 -0
- package/public/assets/{dist-DUoZnlB2.js.map → dist-YpHfnnIA.js.map} +1 -1
- package/public/assets/dist-qd198ib9.js +2 -0
- package/public/assets/{dist-D41tzJYg.js.map → dist-qd198ib9.js.map} +1 -1
- package/public/assets/{dist-DmN94rc8.js → dist-yGOq6sB3.js} +2 -2
- package/public/assets/{dist-DmN94rc8.js.map → dist-yGOq6sB3.js.map} +1 -1
- package/public/assets/{es2015-Df08Opjx.js → es2015-CRxz9lOj.js} +2 -2
- package/public/assets/{es2015-Df08Opjx.js.map → es2015-CRxz9lOj.js.map} +1 -1
- package/public/assets/{formatted-body-impl-DOXH7oDF.js → formatted-body-impl-u0W_hWYA.js} +2 -2
- package/public/assets/{formatted-body-impl-DOXH7oDF.js.map → formatted-body-impl-u0W_hWYA.js.map} +1 -1
- package/public/assets/index-BwfVOzUG.js +21 -0
- package/public/assets/{index-br_Y6Cvt.js.map → index-BwfVOzUG.js.map} +1 -1
- package/public/assets/index-Dn_GDXbi.css +2 -0
- package/public/assets/{input-B2EDz-BE.js → input-BHYNpXAG.js} +2 -2
- package/public/assets/{input-B2EDz-BE.js.map → input-BHYNpXAG.js.map} +1 -1
- package/public/assets/{insights-B1edq5lk.js → insights-CAF9Hi5D.js} +2 -2
- package/public/assets/{insights-B1edq5lk.js.map → insights-CAF9Hi5D.js.map} +1 -1
- package/public/assets/{integrations-Cm4fjp7_.js → integrations-CG9qyHUf.js} +2 -2
- package/public/assets/{integrations-Cm4fjp7_.js.map → integrations-CG9qyHUf.js.map} +1 -1
- package/public/assets/{lib-BZrjIp-o.js → lib-Df4aph5u.js} +2 -2
- package/public/assets/{lib-BZrjIp-o.js.map → lib-Df4aph5u.js.map} +1 -1
- package/public/assets/lucide-react-L2UxFDic.js +9 -0
- package/public/assets/lucide-react-L2UxFDic.js.map +1 -0
- package/public/assets/{maintenance-D6zvw0fU.js → maintenance-DgHA35ve.js} +2 -2
- package/public/assets/{maintenance-D6zvw0fU.js.map → maintenance-DgHA35ve.js.map} +1 -1
- package/public/assets/{managed-agents-CzfEznM1.js → managed-agents-Rk6ghKR9.js} +2 -2
- package/public/assets/{managed-agents-CzfEznM1.js.map → managed-agents-Rk6ghKR9.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-DZlCaTHQ.js → markdown-preview-impl-NWMtJdYz.js} +2 -2
- package/public/assets/{markdown-preview-impl-DZlCaTHQ.js.map → markdown-preview-impl-NWMtJdYz.js.map} +1 -1
- package/public/assets/memory-DamwWa9A.js +2 -0
- package/public/assets/{memory-CUUAzJDc.js.map → memory-DamwWa9A.js.map} +1 -1
- package/public/assets/{messages-6QZH46LG.js → messages--NU-YIL6.js} +2 -2
- package/public/assets/{messages-6QZH46LG.js.map → messages--NU-YIL6.js.map} +1 -1
- package/public/assets/{orchestrators-CrcqkPuc.js → orchestrators-DQnIn6qx.js} +2 -2
- package/public/assets/{orchestrators-CrcqkPuc.js.map → orchestrators-DQnIn6qx.js.map} +1 -1
- package/public/assets/{overview-418xXZO_.js → overview-DCmNgXDf.js} +2 -2
- package/public/assets/{overview-418xXZO_.js.map → overview-DCmNgXDf.js.map} +1 -1
- package/public/assets/{pairs-CnedATgT.js → pairs-DNELeC9P.js} +2 -2
- package/public/assets/{pairs-CnedATgT.js.map → pairs-DNELeC9P.js.map} +1 -1
- package/public/assets/{react-dom-WI6TjVe7.js → react-dom-CrP_AtcL.js} +2 -2
- package/public/assets/{react-dom-WI6TjVe7.js.map → react-dom-CrP_AtcL.js.map} +1 -1
- package/public/assets/{security-DS13pbag.js → security-Dd3Kh0oC.js} +2 -2
- package/public/assets/{security-DS13pbag.js.map → security-Dd3Kh0oC.js.map} +1 -1
- package/public/assets/{select-BOprGZDS.js → select-DQbeJZX0.js} +2 -2
- package/public/assets/{select-BOprGZDS.js.map → select-DQbeJZX0.js.map} +1 -1
- package/public/assets/settings-BMAJrBIH.js +4 -0
- package/public/assets/settings-BMAJrBIH.js.map +1 -0
- package/public/assets/store-B8xP6z6T.js +9 -0
- package/public/assets/store-B8xP6z6T.js.map +1 -0
- package/public/assets/tasks-BYR4L9GP.js +2 -0
- package/public/assets/{tasks-B-4oCPuv.js.map → tasks-BYR4L9GP.js.map} +1 -1
- package/public/assets/teams-CoTifrq_.js +2 -0
- package/public/assets/{teams-sCQAs_QA.js.map → teams-CoTifrq_.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-BhqS2XpS.js → terminal-viewer-impl-B1YzfpE6.js} +3 -3
- package/public/assets/{terminal-viewer-impl-BhqS2XpS.js.map → terminal-viewer-impl-B1YzfpE6.js.map} +1 -1
- package/public/assets/{use-keyboard-viewport-fOL_3RHB.js → use-keyboard-viewport-DrRH_QzG.js} +2 -2
- package/public/assets/{use-keyboard-viewport-fOL_3RHB.js.map → use-keyboard-viewport-DrRH_QzG.js.map} +1 -1
- package/public/assets/{work-queue-BDhjlwUH.js → work-queue-x57vlgyZ.js} +2 -2
- package/public/assets/{work-queue-BDhjlwUH.js.map → work-queue-x57vlgyZ.js.map} +1 -1
- package/public/assets/{workspaces-6I-C69nf.js → workspaces-CYm5bLg9.js} +3 -3
- package/public/assets/{workspaces-6I-C69nf.js.map → workspaces-CYm5bLg9.js.map} +1 -1
- package/public/index.html +22 -22
- package/runner/src/config.ts +66 -2
- package/src/automations.ts +3 -4
- package/src/cli/index.ts +11 -0
- package/src/cli/merges.ts +89 -0
- package/src/config-store.ts +5 -6
- package/src/config.ts +4 -0
- package/src/connectors.ts +5 -0
- package/src/db/merge-lease.ts +95 -1
- package/src/db/migrations.ts +21 -0
- package/src/db/schema.ts +1 -1
- package/src/index.ts +5 -2
- package/src/maintenance.ts +8 -8
- package/src/mcp-merge-tools.ts +66 -0
- package/src/mcp-provider-catalog.ts +9 -0
- package/src/mcp.ts +7 -8
- package/src/merge-pause-maintenance.ts +13 -0
- package/src/provider-catalog-store.ts +33 -4
- package/src/provider-config-store.ts +216 -0
- package/src/recipe-runner.ts +2 -1
- package/src/recipe-validator.ts +4 -3
- package/src/routes/agent-sessions.ts +4 -2
- package/src/routes/agents-spawn.ts +2 -2
- package/src/routes/commands.ts +20 -0
- package/src/routes/index.ts +19 -2
- package/src/routes/insights.ts +4 -3
- package/src/routes/merges.ts +95 -0
- package/src/routes/provider-config.ts +89 -41
- package/src/routes/settings.ts +127 -0
- package/src/routes/steward.ts +5 -5
- package/src/routes/tokens.ts +32 -1
- package/src/security.ts +36 -37
- package/src/services/merge-pause.ts +37 -0
- package/src/services/spawn-agent.ts +10 -8
- package/src/settings/modules.ts +149 -0
- package/src/settings/registry.ts +397 -0
- package/src/spawn-command.ts +5 -1
- package/src/team-template-config.ts +2 -2
- package/src/token-db.ts +20 -0
- package/src/workspace-pr-completion.ts +70 -0
- package/public/assets/MessageBubble-BrEKymoV.js +0 -2
- package/public/assets/MessageBubble-BrEKymoV.js.map +0 -1
- package/public/assets/agents-Cbegv0Hm.js +0 -2
- package/public/assets/branch-state-badge-CRA50r81.js +0 -2
- package/public/assets/card-BU6PvVHv.js +0 -2
- package/public/assets/chat-BqIaFYGJ.js +0 -2
- package/public/assets/display-CketawEF.js +0 -3
- package/public/assets/dist-D41tzJYg.js +0 -2
- package/public/assets/dist-DUoZnlB2.js +0 -2
- package/public/assets/dist-De2oguBm.js +0 -2
- package/public/assets/index-DaNOQJci.css +0 -2
- package/public/assets/index-br_Y6Cvt.js +0 -21
- package/public/assets/lucide-react-H_-8G79A.js +0 -9
- package/public/assets/lucide-react-H_-8G79A.js.map +0 -1
- package/public/assets/memory-CUUAzJDc.js +0 -2
- package/public/assets/settings-BgInn_f3.js +0 -10
- package/public/assets/settings-BgInn_f3.js.map +0 -1
- package/public/assets/store-DHO_a8BD.js +0 -9
- package/public/assets/store-DHO_a8BD.js.map +0 -1
- package/public/assets/tasks-B-4oCPuv.js +0 -2
- package/public/assets/teams-sCQAs_QA.js +0 -2
package/src/security.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
5
5
|
import { getDb } from "./db";
|
|
6
6
|
import { isPathWithinBase } from "./utils";
|
|
7
7
|
import type { ComponentToken, TokenConstraints } from "./types";
|
|
8
|
+
import { hasScope, normalizeScope } from "agent-relay-sdk/types";
|
|
8
9
|
|
|
9
10
|
const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1", "localhost"]);
|
|
10
11
|
|
|
@@ -99,30 +100,7 @@ type AuthorizationCheck = {
|
|
|
99
100
|
resource?: AuthorizationResource;
|
|
100
101
|
};
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
"admin:*": "system:admin",
|
|
104
|
-
"system:write": "system:admin",
|
|
105
|
-
"agents:read": "agent:read",
|
|
106
|
-
"agents:write": "agent:write",
|
|
107
|
-
"messages:read": "message:read",
|
|
108
|
-
"messages:write": "message:send",
|
|
109
|
-
"tasks:read": "task:read",
|
|
110
|
-
"tasks:write": "task:write",
|
|
111
|
-
"commands:read": "command:read",
|
|
112
|
-
"commands:write": "command:write",
|
|
113
|
-
"channels:read": "channel:read",
|
|
114
|
-
"channels:write": "channel:write",
|
|
115
|
-
"integrations:read": "integration:read",
|
|
116
|
-
"integrations:write": "integration:write",
|
|
117
|
-
"connectors:read": "integration:read",
|
|
118
|
-
"connectors:write": "integration:write",
|
|
119
|
-
"events:create": "integration:write",
|
|
120
|
-
"tasks:create": "task:write",
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export function normalizeScope(scope: string): string {
|
|
124
|
-
return SCOPE_ALIASES[scope] ?? scope;
|
|
125
|
-
}
|
|
103
|
+
export { normalizeScope };
|
|
126
104
|
|
|
127
105
|
export function getIntegrationAuth(req: Request): IntegrationAuth | null {
|
|
128
106
|
const token = extractToken(req);
|
|
@@ -170,6 +148,8 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
170
148
|
if (pathname === "/api/orchestrators/bootstrap") return "token:write";
|
|
171
149
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/logs\//)) return "logs:read";
|
|
172
150
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/terminal\//)) return "terminal:attach";
|
|
151
|
+
const settingScope = requiredSettingsScopeFor(method, pathname);
|
|
152
|
+
if (settingScope !== undefined) return settingScope;
|
|
173
153
|
if (pathname.startsWith("/api/artifacts")) {
|
|
174
154
|
if (method === "GET" || method === "HEAD") return "artifact:read";
|
|
175
155
|
if (method === "DELETE") return "artifact:admin";
|
|
@@ -195,11 +175,13 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
195
175
|
if (pathname.startsWith("/api/inbox")) return method === "GET" ? "message:read" : "message:send";
|
|
196
176
|
if (pathname.startsWith("/api/messages")) return method === "GET" ? "message:read" : "message:send";
|
|
197
177
|
if (pathname.startsWith("/api/commands")) return method === "GET" ? "command:read" : "command:write";
|
|
178
|
+
if (pathname.startsWith("/api/merges")) return method === "GET" ? "command:read" : "command:write";
|
|
198
179
|
if (pathname.startsWith("/api/workspaces")) return method === "GET" ? "agent:read" : "agent:write";
|
|
199
180
|
if (pathname.startsWith("/api/agent-profiles")) return method === "GET" ? "agent:read" : "agent:write";
|
|
200
181
|
if (pathname.startsWith("/api/recipes")) return method === "GET" ? "recipes:read" : "recipes:write";
|
|
201
182
|
if (pathname === "/api/runtime-tokens/renew") return "agent:write";
|
|
202
183
|
if (pathname.startsWith("/api/runtime-tokens")) return "token:write";
|
|
184
|
+
if (pathname === "/api/tokens/me") return null;
|
|
203
185
|
if (pathname.startsWith("/api/tokens") || pathname.startsWith("/api/token-profiles")) return method === "GET" ? "token:read" : "token:write";
|
|
204
186
|
if (pathname.startsWith("/api/maintenance")) return "system:admin";
|
|
205
187
|
if (pathname.startsWith("/api/tasks")) return method === "GET" ? "task:read" : "task:write";
|
|
@@ -250,11 +232,15 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
250
232
|
if (pathname === "/api/mcp") return "mcp:use";
|
|
251
233
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/logs\//)) return "logs:read";
|
|
252
234
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/terminal\//)) return "terminal:attach";
|
|
235
|
+
const settingScope = requiredSettingsScopeFor(method, pathname);
|
|
236
|
+
if (settingScope !== undefined) return settingScope ?? "system:admin";
|
|
253
237
|
if (pathname.startsWith("/api/commands")) {
|
|
254
238
|
if (method === "GET") return "command:read";
|
|
255
239
|
return "command:write";
|
|
256
240
|
}
|
|
241
|
+
if (pathname.startsWith("/api/merges")) return method === "GET" ? "command:read" : "command:write";
|
|
257
242
|
if (pathname.startsWith("/api/workspaces")) return method === "GET" ? "agent:read" : "agent:write";
|
|
243
|
+
if (pathname === "/api/tokens/me") return null;
|
|
258
244
|
if (pathname.startsWith("/api/tokens") || pathname.startsWith("/api/token-profiles")) return method === "GET" ? "token:read" : "token:write";
|
|
259
245
|
if (pathname.startsWith("/api/maintenance")) return "system:admin";
|
|
260
246
|
if (pathname === "/api/recipes/start") return "recipe:start";
|
|
@@ -296,6 +282,32 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
296
282
|
return "system:admin";
|
|
297
283
|
}
|
|
298
284
|
|
|
285
|
+
const CONFIG_SETTINGS_NAMESPACES = new Set(["steward", "insights", "notifications", "workspace", "landing", "provider"]);
|
|
286
|
+
|
|
287
|
+
function requiredSettingsScopeFor(method: string, pathname: string): string | null | undefined {
|
|
288
|
+
if (pathname === "/api/settings") return method === "GET" ? "settings:read" : null;
|
|
289
|
+
const settingsMatch = pathname.match(/^\/api\/settings\/([^/]+)(?:\/[^/]+)?$/);
|
|
290
|
+
if (settingsMatch) return settingsScope(method, decodeURIComponent(settingsMatch[1]!));
|
|
291
|
+
if (pathname === "/api/steward-config") return settingsScope(method, "steward");
|
|
292
|
+
if (pathname === "/api/workspace-config") return settingsScope(method, "workspace");
|
|
293
|
+
if (pathname === "/api/insights/config") return settingsScope(method, "insights");
|
|
294
|
+
const configMatch = pathname.match(/^\/api\/config\/([^/]+)(?:\/[^/]+)?(?:\/history)?$/);
|
|
295
|
+
if (configMatch) {
|
|
296
|
+
const namespace = decodeURIComponent(configMatch[1]!);
|
|
297
|
+
if (CONFIG_SETTINGS_NAMESPACES.has(namespace)) return settingsScope(method, namespace);
|
|
298
|
+
}
|
|
299
|
+
if (pathname.match(/^\/api\/providers\/[^/]+\/models\/[^/]+$/) && ["PUT", "PATCH", "DELETE"].includes(method)) {
|
|
300
|
+
return "settings:write:provider";
|
|
301
|
+
}
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function settingsScope(method: string, namespace: string): string | null {
|
|
306
|
+
if (method === "GET" || method === "HEAD") return `settings:read:${namespace}`;
|
|
307
|
+
if (["PUT", "PATCH", "POST", "DELETE"].includes(method)) return `settings:write:${namespace}`;
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
299
311
|
export function signComponentToken(payload: Omit<ComponentToken, "iat"> & { iat?: number }): string {
|
|
300
312
|
const header = { alg: "HS256", typ: "JWT" };
|
|
301
313
|
const body: ComponentToken = {
|
|
@@ -402,19 +414,6 @@ function isComponentToken(value: unknown): value is ComponentToken {
|
|
|
402
414
|
(record.jti === undefined || typeof record.jti === "string");
|
|
403
415
|
}
|
|
404
416
|
|
|
405
|
-
function hasScope(scopes: string[], requiredScope: string): boolean {
|
|
406
|
-
const required = normalizeScope(requiredScope);
|
|
407
|
-
if (scopes.includes("*")) return true;
|
|
408
|
-
for (const rawScope of scopes) {
|
|
409
|
-
const scope = normalizeScope(rawScope);
|
|
410
|
-
if (scope === "system:admin") return true;
|
|
411
|
-
if (scope === required) return true;
|
|
412
|
-
const [category] = required.split(":");
|
|
413
|
-
if (category && scope === `${category}:*`) return true;
|
|
414
|
-
}
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
417
|
/** Authoritative parent lineage from a registering token's signed constraints — agent-initiated
|
|
419
418
|
* spawn (`spawnedBy`) or the delegating-component path (`parentAgents`). One home, used by both the
|
|
420
419
|
* HTTP `postAgent` route and the bus register path so `spawned_by` is set however a child registers
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
acquireMergePause as acquireMergePauseRow,
|
|
3
|
+
getMergePause,
|
|
4
|
+
listMergePauses,
|
|
5
|
+
releaseMergePause as releaseMergePauseRow,
|
|
6
|
+
renewMergePause as renewMergePauseRow,
|
|
7
|
+
type MergePauseRecord,
|
|
8
|
+
} from "../db";
|
|
9
|
+
|
|
10
|
+
export type { MergePauseRecord };
|
|
11
|
+
|
|
12
|
+
export function listAutoMergePauses(): MergePauseRecord[] {
|
|
13
|
+
return listMergePauses();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getAutoMergePause(id: string): MergePauseRecord | null {
|
|
17
|
+
return getMergePause(id);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function pauseAutoMerge(input: {
|
|
21
|
+
repoRoot: string;
|
|
22
|
+
baseRef?: string;
|
|
23
|
+
holder: string;
|
|
24
|
+
reason?: string;
|
|
25
|
+
ttlMs?: number;
|
|
26
|
+
now?: number;
|
|
27
|
+
}): { ok: true; pause: MergePauseRecord } | { ok: false; pause: MergePauseRecord } {
|
|
28
|
+
return acquireMergePauseRow(input);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function renewAutoMergePause(id: string, ttlMs?: number): MergePauseRecord | null {
|
|
32
|
+
return renewMergePauseRow(id, ttlMs);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resumeAutoMerge(input: { id?: string; repoRoot?: string; baseRef?: string }): boolean {
|
|
36
|
+
return releaseMergePauseRow(input);
|
|
37
|
+
}
|
|
@@ -208,19 +208,21 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
208
208
|
// resolves it. Absent (graceful) when the cwd is inside no registered root.
|
|
209
209
|
const callerProject = resolveProjectForCwd(resolvedCwd);
|
|
210
210
|
|
|
211
|
-
// #409
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
211
|
+
// #409 + #463 — one composition home for the prompt material handed to providers.
|
|
212
|
+
// Precedence is: project preamble, profile system, profile append entries, then
|
|
213
|
+
// transient caller/fork context. Spawns without profile/project material stay
|
|
214
|
+
// byte-identical to the caller-provided systemPromptAppend.
|
|
215
|
+
const appendBlocks: string[] = [];
|
|
216
216
|
if (input.projectInstructions && callerProject) {
|
|
217
217
|
const block = composeProjectInstructions(callerProject, {
|
|
218
218
|
...(input.projectInstructions.policy ? { policy: input.projectInstructions.policy } : {}),
|
|
219
219
|
});
|
|
220
|
-
if (block)
|
|
221
|
-
systemPromptAppend = systemPromptAppend ? `${block}\n\n${systemPromptAppend}` : block;
|
|
222
|
-
}
|
|
220
|
+
if (block.trim()) appendBlocks.push(block);
|
|
223
221
|
}
|
|
222
|
+
if (agentProfile?.instructions.system?.trim()) appendBlocks.push(agentProfile.instructions.system);
|
|
223
|
+
appendBlocks.push(...(agentProfile?.instructions.append.filter((entry) => entry.trim()) ?? []));
|
|
224
|
+
if (input.systemPromptAppend?.trim()) appendBlocks.push(input.systemPromptAppend);
|
|
225
|
+
const systemPromptAppend = appendBlocks.length ? appendBlocks.join("\n\n") : input.systemPromptAppend;
|
|
224
226
|
|
|
225
227
|
// Child runner token. `agentInitiated` (set whenever an agent is the caller) forces a
|
|
226
228
|
// non-spawn-capable child (no grandchildren); `spawnedBy` stamps authoritative lineage the
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SPAWN_PROVIDERS,
|
|
3
|
+
errMessage,
|
|
4
|
+
isRecord,
|
|
5
|
+
type ConnectorManifest,
|
|
6
|
+
type JSONSchema,
|
|
7
|
+
type SettingDescriptor,
|
|
8
|
+
type SettingDisplay,
|
|
9
|
+
type SettingScope,
|
|
10
|
+
type SettingTier,
|
|
11
|
+
} from "agent-relay-sdk";
|
|
12
|
+
import { ValidationError } from "../db";
|
|
13
|
+
import { cleanString } from "../validation";
|
|
14
|
+
import type { ProviderConfig } from "../../runner/src/adapter";
|
|
15
|
+
import { defaultProviderConfig } from "../../runner/src/config";
|
|
16
|
+
import { SETTING_ADMIN_SCOPE, getSettingDescriptor, registerProviderConfigSetting, registerSetting, validateSettingValue } from "./registry";
|
|
17
|
+
|
|
18
|
+
export const CONNECTOR_CONFIG_NAMESPACE = "connector-config";
|
|
19
|
+
export const CHANNEL_TYPE_CONFIG_NAMESPACE = "channel-type";
|
|
20
|
+
|
|
21
|
+
export function registerConnectorConfigSetting(
|
|
22
|
+
manifest: Pick<ConnectorManifest, "id" | "displayName" | "description" | "configSchema">,
|
|
23
|
+
opts: { defaults?: unknown } = {},
|
|
24
|
+
): SettingDescriptor | undefined {
|
|
25
|
+
if (!manifest.configSchema) return undefined;
|
|
26
|
+
const schema = schemaFromRecord(manifest.configSchema, `connector ${manifest.id} configSchema`);
|
|
27
|
+
return registerModuleSetting({
|
|
28
|
+
namespace: CONNECTOR_CONFIG_NAMESPACE,
|
|
29
|
+
key: manifest.id,
|
|
30
|
+
schema,
|
|
31
|
+
defaults: opts.defaults ?? schemaDefault(schema) ?? {},
|
|
32
|
+
scope: { read: "integration:read", write: SETTING_ADMIN_SCOPE },
|
|
33
|
+
tier: "server-runtime",
|
|
34
|
+
display: {
|
|
35
|
+
group: "Connectors",
|
|
36
|
+
label: manifest.displayName || manifest.id,
|
|
37
|
+
description: manifest.description ?? `Configuration for connector ${manifest.id}.`,
|
|
38
|
+
order: 100,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function validateConnectorConfig(manifest: ConnectorManifest, config: unknown): void {
|
|
44
|
+
const descriptor = getSettingDescriptor(CONNECTOR_CONFIG_NAMESPACE, manifest.id) ?? registerConnectorConfigSetting(manifest);
|
|
45
|
+
if (!descriptor) return;
|
|
46
|
+
const validation = validateSettingValue(descriptor.namespace, descriptor.key, config);
|
|
47
|
+
if (!validation.valid) throw new ValidationError(validation.errors.join("; "));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function registerChannelTypeSetting(input: {
|
|
51
|
+
type: string;
|
|
52
|
+
schema: JSONSchema;
|
|
53
|
+
defaults: unknown;
|
|
54
|
+
scope?: Partial<SettingScope>;
|
|
55
|
+
tier?: SettingTier;
|
|
56
|
+
display?: Partial<SettingDisplay>;
|
|
57
|
+
}): SettingDescriptor {
|
|
58
|
+
const type = cleanString(input.type, "channel type", { required: true, max: 80 })!;
|
|
59
|
+
return registerModuleSetting({
|
|
60
|
+
namespace: CHANNEL_TYPE_CONFIG_NAMESPACE,
|
|
61
|
+
key: type,
|
|
62
|
+
schema: input.schema,
|
|
63
|
+
defaults: input.defaults,
|
|
64
|
+
scope: { read: "integration:read", write: SETTING_ADMIN_SCOPE, ...input.scope },
|
|
65
|
+
tier: input.tier ?? "server-runtime",
|
|
66
|
+
display: {
|
|
67
|
+
group: "Channels",
|
|
68
|
+
label: `${type} channel`,
|
|
69
|
+
description: `Configuration for ${type} channel integrations.`,
|
|
70
|
+
order: 120,
|
|
71
|
+
...input.display,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ProviderSettingsDeclaration {
|
|
77
|
+
provider: string;
|
|
78
|
+
defaults: ProviderConfig;
|
|
79
|
+
display?: Partial<SettingDisplay>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const providerSettingsDeclarations = new Map<string, ProviderSettingsDeclaration>();
|
|
83
|
+
let builtInProvidersSeeded = false;
|
|
84
|
+
|
|
85
|
+
export function registerProviderSettingsDeclaration(input: {
|
|
86
|
+
provider: string;
|
|
87
|
+
defaults?: ProviderConfig;
|
|
88
|
+
display?: Partial<SettingDisplay>;
|
|
89
|
+
}): ProviderSettingsDeclaration {
|
|
90
|
+
const provider = cleanString(input.provider, "provider", { required: true, max: 80 })!;
|
|
91
|
+
const declaration: ProviderSettingsDeclaration = {
|
|
92
|
+
provider,
|
|
93
|
+
defaults: input.defaults ?? defaultProviderConfig(provider),
|
|
94
|
+
display: input.display,
|
|
95
|
+
};
|
|
96
|
+
providerSettingsDeclarations.set(provider, declaration);
|
|
97
|
+
return declaration;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function listProviderSettingsDeclarations(): ProviderSettingsDeclaration[] {
|
|
101
|
+
ensureBuiltInProviderSettingsDeclarations();
|
|
102
|
+
return [...providerSettingsDeclarations.values()];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getProviderSettingsDeclaration(provider: string): ProviderSettingsDeclaration | undefined {
|
|
106
|
+
ensureBuiltInProviderSettingsDeclarations();
|
|
107
|
+
return providerSettingsDeclarations.get(provider);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function providerConfigDefaults(provider: string): ProviderConfig {
|
|
111
|
+
return getProviderSettingsDeclaration(provider)?.defaults ?? defaultProviderConfig(provider);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function registerDeclaredProviderConfigSetting(input: { host: string; provider: string }): SettingDescriptor {
|
|
115
|
+
const declaration = getProviderSettingsDeclaration(input.provider);
|
|
116
|
+
if (!declaration) throw new ValidationError(`provider must be one of: ${listProviderSettingsDeclarations().map((d) => d.provider).join(", ")}`);
|
|
117
|
+
return registerProviderConfigSetting({
|
|
118
|
+
host: input.host,
|
|
119
|
+
provider: declaration.provider,
|
|
120
|
+
defaults: declaration.defaults,
|
|
121
|
+
display: declaration.display,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function ensureBuiltInProviderSettingsDeclarations(): void {
|
|
126
|
+
if (builtInProvidersSeeded) return;
|
|
127
|
+
builtInProvidersSeeded = true;
|
|
128
|
+
for (const provider of SPAWN_PROVIDERS) {
|
|
129
|
+
registerProviderSettingsDeclaration({ provider, defaults: defaultProviderConfig(provider) });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function registerModuleSetting(input: Parameters<typeof registerSetting>[0]): SettingDescriptor {
|
|
134
|
+
try {
|
|
135
|
+
return registerSetting(input);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error instanceof ValidationError) throw error;
|
|
138
|
+
throw new ValidationError(errMessage(error));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function schemaFromRecord(value: Record<string, unknown>, field: string): JSONSchema {
|
|
143
|
+
if (!isRecord(value)) throw new ValidationError(`${field} must be an object`);
|
|
144
|
+
return value as JSONSchema;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function schemaDefault(schema: JSONSchema): unknown {
|
|
148
|
+
return Object.prototype.hasOwnProperty.call(schema, "default") ? schema.default : undefined;
|
|
149
|
+
}
|