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
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// Settings descriptor registry (epic #380, #382) — the single server-side source
|
|
2
|
+
// of truth for which settings exist, their JSON Schema, defaults, required scope,
|
|
3
|
+
// tier, and display. This is the *declaration* layer over the existing `config`
|
|
4
|
+
// table (src/config-store.ts owns storage + history + the `config.changed` SSE) —
|
|
5
|
+
// it does NOT touch storage. The API (#383), scope enforcement (#385), module
|
|
6
|
+
// wiring (#386), and the Settings UI (#384) all build on this contract.
|
|
7
|
+
//
|
|
8
|
+
// DECISION LOCKED (#380 thread): validation is via `ajv`, descriptors carry full
|
|
9
|
+
// JSON Schema. Each descriptor's schema is ajv-compiled at registration, which
|
|
10
|
+
// validates it against the JSON Schema meta-schema and throws on a malformed
|
|
11
|
+
// schema — fail fast, never seed a broken descriptor.
|
|
12
|
+
//
|
|
13
|
+
// The `SettingDescriptor`/`JSONSchema` types live in `sdk/src/types.ts` (imported
|
|
14
|
+
// here, never re-declared — DRY rule). Keep this module under the ratchet ceiling;
|
|
15
|
+
// add per-surface seed helpers, don't inline a god-object.
|
|
16
|
+
|
|
17
|
+
import Ajv from "ajv";
|
|
18
|
+
import type { ValidateFunction } from "ajv";
|
|
19
|
+
import {
|
|
20
|
+
APPROVAL_MODES,
|
|
21
|
+
errMessage,
|
|
22
|
+
SPAWN_PROVIDERS,
|
|
23
|
+
VALID_EFFORTS,
|
|
24
|
+
type JSONSchema,
|
|
25
|
+
type SettingDescriptor,
|
|
26
|
+
type SettingScope,
|
|
27
|
+
} from "agent-relay-sdk";
|
|
28
|
+
|
|
29
|
+
/** Fail-closed default scope — a descriptor that omits scope requires admin to
|
|
30
|
+
* read and write (#385 owns the scope grammar; this layer only declares it). */
|
|
31
|
+
export const SETTING_ADMIN_SCOPE = "system:admin";
|
|
32
|
+
|
|
33
|
+
// One shared compiler. `strict: false` relaxes ajv's opinionated strict-mode
|
|
34
|
+
// warnings (unknown keywords/formats) so any reasonable JSON Schema compiles,
|
|
35
|
+
// while meta-schema validation (default on) still rejects a structurally broken
|
|
36
|
+
// schema. `allErrors` so value-validation reports every failure, not just the first.
|
|
37
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Registration input: a descriptor with an optional, possibly-partial `scope`.
|
|
41
|
+
* The registry resolves any omitted read/write scope to the fail-closed admin
|
|
42
|
+
* default and returns a fully-populated `SettingDescriptor`.
|
|
43
|
+
*/
|
|
44
|
+
export type SettingRegistration<T = unknown> = Omit<SettingDescriptor<T>, "scope"> & {
|
|
45
|
+
scope?: Partial<SettingScope>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
interface RegisteredSetting {
|
|
49
|
+
descriptor: SettingDescriptor;
|
|
50
|
+
validate: ValidateFunction;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const registry = new Map<string, RegisteredSetting>();
|
|
54
|
+
|
|
55
|
+
function descriptorId(namespace: string, key: string): string {
|
|
56
|
+
return `${namespace}:${key}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveScope(scope: Partial<SettingScope> | undefined): SettingScope {
|
|
60
|
+
return {
|
|
61
|
+
read: scope?.read ?? SETTING_ADMIN_SCOPE,
|
|
62
|
+
write: scope?.write ?? SETTING_ADMIN_SCOPE,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register (or replace) a setting descriptor. Compiles `schema` with ajv — a
|
|
68
|
+
* malformed JSON Schema throws here, at registration/seed time. The mechanism
|
|
69
|
+
* #386 modules call to contribute their own settings.
|
|
70
|
+
*/
|
|
71
|
+
export function registerSetting<T>(reg: SettingRegistration<T>): SettingDescriptor<T> {
|
|
72
|
+
if (!reg.namespace) throw new Error("setting descriptor requires a non-empty namespace");
|
|
73
|
+
if (!reg.key) throw new Error("setting descriptor requires a non-empty key");
|
|
74
|
+
const id = descriptorId(reg.namespace, reg.key);
|
|
75
|
+
|
|
76
|
+
let validate: ValidateFunction;
|
|
77
|
+
try {
|
|
78
|
+
validate = ajv.compile(reg.schema as object);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw new Error(`invalid JSON Schema for setting "${id}": ${errMessage(error)}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const descriptor: SettingDescriptor<T> = {
|
|
84
|
+
namespace: reg.namespace,
|
|
85
|
+
key: reg.key,
|
|
86
|
+
schema: reg.schema,
|
|
87
|
+
defaults: reg.defaults,
|
|
88
|
+
scope: resolveScope(reg.scope),
|
|
89
|
+
tier: reg.tier,
|
|
90
|
+
display: reg.display,
|
|
91
|
+
};
|
|
92
|
+
registry.set(id, { descriptor: descriptor as SettingDescriptor, validate });
|
|
93
|
+
return descriptor;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Look up a single descriptor by its (namespace, key) address. */
|
|
97
|
+
export function getSettingDescriptor(namespace: string, key: string): SettingDescriptor | undefined {
|
|
98
|
+
return registry.get(descriptorId(namespace, key))?.descriptor;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** All registered descriptors (built-ins + module contributions), seeded on first access. */
|
|
102
|
+
export function listSettingDescriptors(): SettingDescriptor[] {
|
|
103
|
+
ensureSeeded();
|
|
104
|
+
return [...registry.values()].map((entry) => entry.descriptor);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Distinct namespaces that have at least one registered descriptor. */
|
|
108
|
+
export function listSettingNamespaces(): string[] {
|
|
109
|
+
return [...new Set(listSettingDescriptors().map((d) => d.namespace))];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Validate a candidate value against a registered descriptor's compiled schema.
|
|
114
|
+
* The single validation seam — #383 calls this on writes so the schema is the one
|
|
115
|
+
* source of truth. Returns `valid:false` with a reason when no descriptor exists.
|
|
116
|
+
*/
|
|
117
|
+
export function validateSettingValue(
|
|
118
|
+
namespace: string,
|
|
119
|
+
key: string,
|
|
120
|
+
value: unknown,
|
|
121
|
+
): { valid: boolean; errors: string[] } {
|
|
122
|
+
ensureSeeded();
|
|
123
|
+
const entry = registry.get(descriptorId(namespace, key));
|
|
124
|
+
if (!entry) return { valid: false, errors: [`no descriptor registered for "${descriptorId(namespace, key)}"`] };
|
|
125
|
+
const valid = entry.validate(value) === true;
|
|
126
|
+
if (valid) return { valid: true, errors: [] };
|
|
127
|
+
const errors = (entry.validate.errors ?? []).map((e) => `${e.instancePath || "/"} ${e.message ?? "invalid"}`.trim());
|
|
128
|
+
return { valid: false, errors };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
132
|
+
// Built-in seeds — descriptors for today's `config`-table namespaces. Schemas
|
|
133
|
+
// mirror the validators in src/config-store.ts; defaults mirror its *_DEFAULTS.
|
|
134
|
+
// All current built-ins are `server-runtime` (config-table backed, runtime
|
|
135
|
+
// mutable). Scope is left default (admin) here; #385 refines read/write tokens.
|
|
136
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
const STEWARD_SCHEMA: JSONSchema = {
|
|
139
|
+
type: "object",
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
required: ["enabled", "provider", "permissionMode", "keepaliveSeconds"],
|
|
142
|
+
properties: {
|
|
143
|
+
enabled: { type: "boolean" },
|
|
144
|
+
provider: { type: "string", enum: [...SPAWN_PROVIDERS] },
|
|
145
|
+
model: { type: "string", maxLength: 120 },
|
|
146
|
+
effort: { type: "string", enum: [...VALID_EFFORTS] },
|
|
147
|
+
permissionMode: { type: "string", enum: [...APPROVAL_MODES] },
|
|
148
|
+
keepaliveSeconds: { type: "integer", minimum: 0, maximum: 2_592_000 },
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const INSIGHTS_SCHEMA: JSONSchema = {
|
|
153
|
+
type: "object",
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
required: ["enabled", "contextRatio", "introspection"],
|
|
156
|
+
properties: {
|
|
157
|
+
enabled: { type: "boolean" },
|
|
158
|
+
contextRatio: {
|
|
159
|
+
type: "object",
|
|
160
|
+
additionalProperties: false,
|
|
161
|
+
required: ["enabled"],
|
|
162
|
+
properties: { enabled: { type: "boolean" } },
|
|
163
|
+
},
|
|
164
|
+
introspection: {
|
|
165
|
+
type: "object",
|
|
166
|
+
additionalProperties: false,
|
|
167
|
+
required: ["enabled", "minTurns", "minContextRemaining"],
|
|
168
|
+
properties: {
|
|
169
|
+
enabled: { type: "boolean" },
|
|
170
|
+
minTurns: { type: "integer", minimum: 0, maximum: 1000 },
|
|
171
|
+
minContextRemaining: { type: "number", minimum: 0, maximum: 1 },
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const CONTEXT_THRESHOLD_SCHEMA: JSONSchema = {
|
|
178
|
+
type: "object",
|
|
179
|
+
additionalProperties: false,
|
|
180
|
+
required: ["enabled", "advisoryUtilization", "hardBackstopUtilization", "bandSize"],
|
|
181
|
+
properties: {
|
|
182
|
+
enabled: { type: "boolean" },
|
|
183
|
+
advisoryUtilization: { type: "number", minimum: 0, maximum: 1 },
|
|
184
|
+
hardBackstopUtilization: { type: "number", minimum: 0, maximum: 1 },
|
|
185
|
+
bandSize: { type: "number", minimum: 0, maximum: 1 },
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const NOTIFICATIONS_SCHEMA: JSONSchema = {
|
|
190
|
+
type: "object",
|
|
191
|
+
additionalProperties: false,
|
|
192
|
+
required: ["enabled", "branchLanded", "agentReady", "agentExited", "agentSpawnFailed", "contextThreshold"],
|
|
193
|
+
properties: {
|
|
194
|
+
enabled: { type: "boolean" },
|
|
195
|
+
branchLanded: { type: "boolean" },
|
|
196
|
+
agentReady: { type: "boolean" },
|
|
197
|
+
agentExited: { type: "boolean" },
|
|
198
|
+
agentSpawnFailed: { type: "boolean" },
|
|
199
|
+
contextThreshold: CONTEXT_THRESHOLD_SCHEMA,
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const WORKSPACE_SCHEMA: JSONSchema = {
|
|
204
|
+
type: "object",
|
|
205
|
+
additionalProperties: false,
|
|
206
|
+
required: ["symlinkPaths"],
|
|
207
|
+
properties: {
|
|
208
|
+
symlinkPaths: {
|
|
209
|
+
type: "array",
|
|
210
|
+
maxItems: 100,
|
|
211
|
+
items: { type: "string", maxLength: 120 },
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const LANDING_SCHEMA: JSONSchema = {
|
|
217
|
+
type: "object",
|
|
218
|
+
additionalProperties: false,
|
|
219
|
+
required: ["strategy"],
|
|
220
|
+
properties: {
|
|
221
|
+
strategy: { type: "string", enum: ["direct", "pr"] },
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Provider chat/reasoning capture toggles (source: runner/src/adapter.ts ProviderConfig).
|
|
226
|
+
// NOTE (#383 coordination): provider config storage is being centralized in #383;
|
|
227
|
+
// these are seeded instance-global under namespace "provider". Per-provider
|
|
228
|
+
// (claude vs codex) host-addressing is #383's call — keep these minimal until then.
|
|
229
|
+
const PROVIDER_CHAT_CAPTURE_SCHEMA: JSONSchema = {
|
|
230
|
+
type: "string",
|
|
231
|
+
enum: ["final", "full"],
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const PROVIDER_REASONING_CAPTURE_SCHEMA: JSONSchema = {
|
|
235
|
+
type: "boolean",
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export const PROVIDER_CONFIG_NAMESPACE = "provider-config";
|
|
239
|
+
export const PROVIDER_CONFIG_SCHEMA: JSONSchema = {
|
|
240
|
+
type: "object",
|
|
241
|
+
additionalProperties: false,
|
|
242
|
+
required: [
|
|
243
|
+
"command",
|
|
244
|
+
"defaultArgs",
|
|
245
|
+
"env",
|
|
246
|
+
"pluginDirs",
|
|
247
|
+
"defaultCapabilities",
|
|
248
|
+
"defaultApprovalMode",
|
|
249
|
+
"defaultTags",
|
|
250
|
+
"chatCaptureMode",
|
|
251
|
+
"reasoningCapture",
|
|
252
|
+
"headless",
|
|
253
|
+
],
|
|
254
|
+
properties: {
|
|
255
|
+
command: { type: "string", minLength: 1, maxLength: 500 },
|
|
256
|
+
defaultArgs: { type: "array", maxItems: 50, items: { type: "string", maxLength: 80 } },
|
|
257
|
+
env: { type: "object", additionalProperties: { type: "string" } },
|
|
258
|
+
pluginDirs: { type: "array", maxItems: 50, items: { type: "string", maxLength: 80 } },
|
|
259
|
+
defaultCapabilities: { type: "array", maxItems: 50, items: { type: "string", maxLength: 80 } },
|
|
260
|
+
defaultApprovalMode: { type: "string", maxLength: 80 },
|
|
261
|
+
defaultTags: { type: "array", maxItems: 50, items: { type: "string", maxLength: 80 } },
|
|
262
|
+
chatCaptureMode: { type: "string", enum: ["final", "full"] },
|
|
263
|
+
reasoningCapture: { type: "boolean" },
|
|
264
|
+
headless: {
|
|
265
|
+
type: "object",
|
|
266
|
+
additionalProperties: false,
|
|
267
|
+
required: ["tmuxPrefix", "shutdownTimeoutMs"],
|
|
268
|
+
properties: {
|
|
269
|
+
tmuxPrefix: { type: "string", minLength: 1, maxLength: 120 },
|
|
270
|
+
shutdownTimeoutMs: { type: "integer", minimum: 1 },
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
let seeded = false;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Register the built-in descriptors. Idempotent: safe to call repeatedly (and is
|
|
280
|
+
* called lazily by the read accessors). #386 adds module-contributed descriptors
|
|
281
|
+
* on top via `registerSetting`.
|
|
282
|
+
*/
|
|
283
|
+
export function seedBuiltInSettings(): void {
|
|
284
|
+
if (seeded) return;
|
|
285
|
+
seeded = true;
|
|
286
|
+
|
|
287
|
+
registerSetting({
|
|
288
|
+
namespace: "steward",
|
|
289
|
+
key: "default",
|
|
290
|
+
schema: STEWARD_SCHEMA,
|
|
291
|
+
defaults: { enabled: false, provider: "claude", permissionMode: "open", keepaliveSeconds: 300 },
|
|
292
|
+
scope: { read: "settings:read:steward", write: "settings:write:steward" },
|
|
293
|
+
tier: "server-runtime",
|
|
294
|
+
display: { group: "Agents", label: "Steward", description: "Auto-provisioned steward that reconciles merges and commands.", order: 10 },
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
registerSetting({
|
|
298
|
+
namespace: "insights",
|
|
299
|
+
key: "default",
|
|
300
|
+
schema: INSIGHTS_SCHEMA,
|
|
301
|
+
defaults: {
|
|
302
|
+
enabled: true,
|
|
303
|
+
contextRatio: { enabled: true },
|
|
304
|
+
introspection: { enabled: true, minTurns: 4, minContextRemaining: 0.15 },
|
|
305
|
+
},
|
|
306
|
+
scope: { read: "settings:read:insights", write: "settings:write:insights" },
|
|
307
|
+
tier: "server-runtime",
|
|
308
|
+
display: { group: "Insights", label: "Insights", description: "Continuous self-improvement instrumentation toggles.", order: 20 },
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
registerSetting({
|
|
312
|
+
namespace: "notifications",
|
|
313
|
+
key: "default",
|
|
314
|
+
schema: NOTIFICATIONS_SCHEMA,
|
|
315
|
+
defaults: {
|
|
316
|
+
enabled: true,
|
|
317
|
+
branchLanded: true,
|
|
318
|
+
agentReady: true,
|
|
319
|
+
agentExited: true,
|
|
320
|
+
agentSpawnFailed: true,
|
|
321
|
+
contextThreshold: { enabled: true, advisoryUtilization: 0.7, hardBackstopUtilization: 0.9, bandSize: 0.1 },
|
|
322
|
+
},
|
|
323
|
+
scope: { read: "settings:read:notifications", write: "settings:write:notifications" },
|
|
324
|
+
tier: "server-runtime",
|
|
325
|
+
display: { group: "Notifications", label: "Lifecycle notifications", description: "Relay-driven lifecycle push notifications (#239).", order: 30 },
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
registerSetting({
|
|
329
|
+
namespace: "workspace",
|
|
330
|
+
key: "default",
|
|
331
|
+
schema: WORKSPACE_SCHEMA,
|
|
332
|
+
defaults: { symlinkPaths: ["AGENTS.md", ".claude-rig"] },
|
|
333
|
+
scope: { read: "settings:read:workspace", write: "settings:write:workspace" },
|
|
334
|
+
tier: "server-runtime",
|
|
335
|
+
display: { group: "Workspace", label: "Workspace provisioning", description: "Untracked paths symlinked into isolated worktrees.", order: 40 },
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
registerSetting({
|
|
339
|
+
namespace: "landing",
|
|
340
|
+
key: "default",
|
|
341
|
+
schema: LANDING_SCHEMA,
|
|
342
|
+
defaults: { strategy: "direct" },
|
|
343
|
+
scope: { read: "settings:read:landing", write: "settings:write:landing" },
|
|
344
|
+
tier: "server-runtime",
|
|
345
|
+
display: { group: "Workspace", label: "Landing strategy", description: "Default land strategy for workspace branches.", order: 41 },
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
registerSetting({
|
|
349
|
+
namespace: "provider",
|
|
350
|
+
key: "chatCaptureMode",
|
|
351
|
+
schema: PROVIDER_CHAT_CAPTURE_SCHEMA,
|
|
352
|
+
defaults: "full",
|
|
353
|
+
scope: { read: "settings:read:provider", write: "settings:write:provider" },
|
|
354
|
+
tier: "server-runtime",
|
|
355
|
+
display: { group: "Providers", label: "Chat capture mode", description: "How much provider output is captured into chat.", order: 50 },
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
registerSetting({
|
|
359
|
+
namespace: "provider",
|
|
360
|
+
key: "reasoningCapture",
|
|
361
|
+
schema: PROVIDER_REASONING_CAPTURE_SCHEMA,
|
|
362
|
+
defaults: true,
|
|
363
|
+
scope: { read: "settings:read:provider", write: "settings:write:provider" },
|
|
364
|
+
tier: "server-runtime",
|
|
365
|
+
display: { group: "Providers", label: "Reasoning capture", description: "Stream reasoning/tool steps into chat.", order: 51 },
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function registerProviderConfigSetting(input: {
|
|
370
|
+
host: string;
|
|
371
|
+
provider: string;
|
|
372
|
+
defaults: unknown;
|
|
373
|
+
display?: Partial<SettingDescriptor["display"]>;
|
|
374
|
+
}): SettingDescriptor {
|
|
375
|
+
return registerSetting({
|
|
376
|
+
namespace: PROVIDER_CONFIG_NAMESPACE,
|
|
377
|
+
key: `${input.host}/${input.provider}`,
|
|
378
|
+
schema: PROVIDER_CONFIG_SCHEMA,
|
|
379
|
+
defaults: input.defaults,
|
|
380
|
+
scope: { read: "agent:read", write: SETTING_ADMIN_SCOPE },
|
|
381
|
+
tier: "server-runtime",
|
|
382
|
+
display: {
|
|
383
|
+
group: "Providers",
|
|
384
|
+
label: `${input.host} ${input.provider}`,
|
|
385
|
+
description: `Provider runtime configuration for ${input.provider} on ${input.host}.`,
|
|
386
|
+
order: 60,
|
|
387
|
+
...input.display,
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function ensureSeeded(): void {
|
|
393
|
+
if (!seeded) seedBuiltInSettings();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Seed on import so any consumer of the registry sees the built-ins.
|
|
397
|
+
seedBuiltInSettings();
|
package/src/spawn-command.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { errMessage } from "agent-relay-sdk";
|
|
|
4
4
|
import { resolveProviderSelection, type ProviderEffort } from "agent-relay-sdk/provider-catalog";
|
|
5
5
|
import { workspaceSpawnParams } from "./config-store";
|
|
6
6
|
import { ValidationError } from "./db";
|
|
7
|
+
import { effectiveProviderCatalogList } from "./provider-catalog-store";
|
|
7
8
|
|
|
8
9
|
/** Resolved provider/model/effort triple — the output shape of every model-resolution wrapper. */
|
|
9
10
|
export interface SpawnModelParams {
|
|
@@ -48,7 +49,10 @@ export function resolveSpawnModelParams(
|
|
|
48
49
|
): SpawnModelParams {
|
|
49
50
|
if (!model && !effort && opts.skipDefaultWhenEmpty) return {};
|
|
50
51
|
try {
|
|
51
|
-
const selection = resolveProviderSelection(
|
|
52
|
+
const selection = resolveProviderSelection(
|
|
53
|
+
{ provider: provider as SpawnProvider, model, effort: effort as ProviderEffort | undefined },
|
|
54
|
+
effectiveProviderCatalogList(),
|
|
55
|
+
);
|
|
52
56
|
return {
|
|
53
57
|
...(selection.modelAlias ? { model: selection.modelAlias } : {}),
|
|
54
58
|
...(selection.providerModel ? { providerModel: selection.providerModel } : {}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRecord, SPAWN_PROVIDERS
|
|
1
|
+
import { isRecord, SPAWN_PROVIDERS } from "agent-relay-sdk";
|
|
2
2
|
import { ValidationError } from "./db";
|
|
3
3
|
import { cleanEnum, cleanString, cleanStringArray } from "./validation";
|
|
4
4
|
import type { PlanOwnerRef, SpawnProvider, TeamTemplate, TeamTemplateCompositionEntry, TeamTemplatePlanSkeleton } from "./types";
|
|
@@ -18,7 +18,7 @@ function cleanPositiveCount(value: unknown, field: string): number {
|
|
|
18
18
|
|
|
19
19
|
function optionalEffort(value: unknown, field: string): TeamTemplateCompositionEntry["effort"] {
|
|
20
20
|
if (value === undefined || value === null) return undefined;
|
|
21
|
-
return
|
|
21
|
+
return cleanString(value, field, { max: 120 }) as TeamTemplateCompositionEntry["effort"];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function optionalStringProp<T extends string>(out: object, key: T, value: unknown, max: number): void {
|
package/src/token-db.ts
CHANGED
|
@@ -41,6 +41,26 @@ const BUILT_IN_PROFILES: Array<Omit<TokenProfile, "createdAt" | "updatedAt">> =
|
|
|
41
41
|
builtIn: true,
|
|
42
42
|
createdBy: "system",
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
id: "settings-reader",
|
|
46
|
+
name: "Settings Reader",
|
|
47
|
+
description: "Dashboard access for viewing schema-driven settings without mutation rights.",
|
|
48
|
+
role: "dashboard",
|
|
49
|
+
scope: ["settings:read"],
|
|
50
|
+
ttlSeconds: 7 * 24 * 60 * 60,
|
|
51
|
+
builtIn: true,
|
|
52
|
+
createdBy: "system",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "settings-operator",
|
|
56
|
+
name: "Settings Operator",
|
|
57
|
+
description: "Dashboard access for viewing settings and managing provider runtime catalog settings.",
|
|
58
|
+
role: "dashboard",
|
|
59
|
+
scope: ["settings:read", "settings:write:provider"],
|
|
60
|
+
ttlSeconds: 7 * 24 * 60 * 60,
|
|
61
|
+
builtIn: true,
|
|
62
|
+
createdBy: "system",
|
|
63
|
+
},
|
|
44
64
|
{
|
|
45
65
|
id: "orchestrator-host",
|
|
46
66
|
name: "Orchestrator Host",
|
|
@@ -64,6 +64,7 @@ const ACTIVE_COMMAND_STATUSES = new Set(["pending", "accepted", "running"]);
|
|
|
64
64
|
const PASSING_CONCLUSIONS = new Set(["SUCCESS", "NEUTRAL", "SKIPPED"]);
|
|
65
65
|
const PASSING_STATES = new Set(["SUCCESS"]);
|
|
66
66
|
const READY_MERGE_STATES = new Set(["CLEAN", "HAS_HOOKS"]);
|
|
67
|
+
const MAX_PR_BRANCH_REFRESH_ATTEMPTS = 2;
|
|
67
68
|
|
|
68
69
|
function relayPrMergeAlreadyHandled(workspace: WorkspaceRecord, preview: WorkspaceMergePreview): boolean {
|
|
69
70
|
const commandId = text(workspace.metadata.relayMergeCommandId);
|
|
@@ -119,6 +120,7 @@ function prReadyForRelayMerge(workspace: WorkspaceRecord, preview: WorkspaceMerg
|
|
|
119
120
|
export function preparePrCompletionScan(workspace: WorkspaceRecord, preview: WorkspaceMergePreview, orchestratorAgentId: string): {
|
|
120
121
|
armCommand?: Command;
|
|
121
122
|
notifiedOwner?: string;
|
|
123
|
+
refreshCommand?: Command;
|
|
122
124
|
mergeCommand?: Command;
|
|
123
125
|
waitReason?: string;
|
|
124
126
|
stop?: boolean;
|
|
@@ -128,6 +130,9 @@ export function preparePrCompletionScan(workspace: WorkspaceRecord, preview: Wor
|
|
|
128
130
|
if (action.armCommand) return { armCommand: action.armCommand, stop: true };
|
|
129
131
|
if (action.notifiedOwner) return { notifiedOwner: action.notifiedOwner, stop: true };
|
|
130
132
|
}
|
|
133
|
+
const refresh = prRefreshForRelayMerge(workspace, preview);
|
|
134
|
+
if (refresh.ok) return { refreshCommand: dispatchRelayPrRefreshCommand(workspace, preview, orchestratorAgentId, refresh.policy, refresh.refreshKey, refresh.attempts), stop: true };
|
|
135
|
+
if (refresh.blocked) return { waitReason: refresh.reason, stop: true };
|
|
131
136
|
const relayPr = prReadyForRelayMerge(workspace, preview);
|
|
132
137
|
if (relayPr.ok) return { mergeCommand: dispatchRelayPrMergeCommand(workspace, preview, orchestratorAgentId, relayPr.policy), stop: true };
|
|
133
138
|
return {
|
|
@@ -136,6 +141,49 @@ export function preparePrCompletionScan(workspace: WorkspaceRecord, preview: Wor
|
|
|
136
141
|
};
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
function prRefreshForRelayMerge(workspace: WorkspaceRecord, preview: WorkspaceMergePreview):
|
|
145
|
+
| { ok: true; policy: WorkspaceAutoMergePolicy; refreshKey: string; attempts: number }
|
|
146
|
+
| { ok: false; blocked?: boolean; reason?: string } {
|
|
147
|
+
if (preview.strategy !== "pr") return { ok: false };
|
|
148
|
+
if (preview.prMerged === true || preview.prState === "merged") return { ok: false };
|
|
149
|
+
if (!preview.prNumber && !preview.prUrl) return { ok: false };
|
|
150
|
+
if (nativeAutoMergeArmed(workspace)) return { ok: false };
|
|
151
|
+
if (relayPrMergeAlreadyHandled(workspace, preview)) return { ok: false };
|
|
152
|
+
|
|
153
|
+
const policy = prAutoMergePolicy(workspace, preview);
|
|
154
|
+
if (policy === "manual") return { ok: false };
|
|
155
|
+
if (policy === "on-approval" && preview.reviewDecision !== "APPROVED") return { ok: false };
|
|
156
|
+
if (preview.conflict === true) return { ok: false };
|
|
157
|
+
if ((preview.behind ?? 0) <= 0) return { ok: false };
|
|
158
|
+
|
|
159
|
+
const mergeable = text(preview.mergeable)?.toUpperCase();
|
|
160
|
+
if (mergeable && mergeable !== "MERGEABLE") return { ok: false };
|
|
161
|
+
|
|
162
|
+
const active = activePrRefreshCommand(workspace, preview);
|
|
163
|
+
if (active) return { ok: false, blocked: true, reason: "branch-refresh-already-dispatched" };
|
|
164
|
+
|
|
165
|
+
const refreshKey = prRefreshKey(preview);
|
|
166
|
+
const attempts = workspace.metadata.prRefreshKey === refreshKey && typeof workspace.metadata.prRefreshAttempts === "number" ? workspace.metadata.prRefreshAttempts : 0;
|
|
167
|
+
if (attempts >= MAX_PR_BRANCH_REFRESH_ATTEMPTS) return { ok: false, blocked: true, reason: "branch-refresh-retry-exhausted" };
|
|
168
|
+
return { ok: true, policy, refreshKey, attempts: attempts + 1 };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function activePrRefreshCommand(workspace: WorkspaceRecord, preview: WorkspaceMergePreview): boolean {
|
|
172
|
+
const commandId = text(workspace.metadata.prRefreshCommandId);
|
|
173
|
+
if (!commandId) return false;
|
|
174
|
+
const command = getCommand(commandId);
|
|
175
|
+
if (!command || !ACTIVE_COMMAND_STATUSES.has(command.status)) return false;
|
|
176
|
+
if (!isRecord(command.params)) return true;
|
|
177
|
+
const recordedPr = typeof command.params.prNumber === "number" ? command.params.prNumber : undefined;
|
|
178
|
+
const currentPr = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
179
|
+
return !(typeof recordedPr === "number" && typeof currentPr === "number" && recordedPr !== currentPr);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function prRefreshKey(preview: WorkspaceMergePreview): string {
|
|
183
|
+
const pr = preview.prNumber ? `pr:${preview.prNumber}` : `url:${text(preview.prUrl) ?? "unknown"}`;
|
|
184
|
+
return [pr, `base:${text(preview.baseRef) ?? "base"}`, `behind:${preview.behind ?? 0}`].join("|");
|
|
185
|
+
}
|
|
186
|
+
|
|
139
187
|
function dispatchRelayPrMergeCommand(workspace: WorkspaceRecord, preview: WorkspaceMergePreview, orchestratorAgentId: string, policy: WorkspaceAutoMergePolicy): Command {
|
|
140
188
|
const now = Date.now();
|
|
141
189
|
const prNumber = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
@@ -158,6 +206,28 @@ function dispatchRelayPrMergeCommand(workspace: WorkspaceRecord, preview: Worksp
|
|
|
158
206
|
return command;
|
|
159
207
|
}
|
|
160
208
|
|
|
209
|
+
function dispatchRelayPrRefreshCommand(workspace: WorkspaceRecord, preview: WorkspaceMergePreview, orchestratorAgentId: string, policy: WorkspaceAutoMergePolicy, refreshKey: string, attempts: number): Command {
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
const prNumber = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
212
|
+
const prUrl = preview.prUrl ?? text(workspace.metadata.prUrl);
|
|
213
|
+
const command = createCommand({
|
|
214
|
+
type: "workspace.pr-refresh",
|
|
215
|
+
source: "system",
|
|
216
|
+
target: orchestratorAgentId,
|
|
217
|
+
correlationId: workspace.id,
|
|
218
|
+
params: {
|
|
219
|
+
action: "pr-refresh", workspaceId: workspace.id, repoRoot: workspace.repoRoot, worktreePath: workspace.worktreePath,
|
|
220
|
+
branch: workspace.branch, baseRef: workspace.baseRef, prNumber, prUrl, policy, refreshKey, attempts,
|
|
221
|
+
requestedBy: "relay-pr-completion", requestedAt: now,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
patchWorkspaceMetadata(workspace.id, {
|
|
225
|
+
prRefreshCommandId: command.id, prRefreshDispatchedAt: now, prRefreshPolicy: policy, prRefreshPrNumber: prNumber,
|
|
226
|
+
prRefreshKey: refreshKey, prRefreshAttempts: attempts, prRefreshError: undefined, prRefreshFailedAt: undefined,
|
|
227
|
+
});
|
|
228
|
+
return command;
|
|
229
|
+
}
|
|
230
|
+
|
|
161
231
|
function statusChecksGreen(rollup: unknown): boolean {
|
|
162
232
|
if (!Array.isArray(rollup) || rollup.length === 0) return false;
|
|
163
233
|
return rollup.every(statusNodeGreen);
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{$t as t,En as n,Fn as r,L as i,Ln as a,Nn as o,Nt as s,P as c,Qt as l,St as u,Vt as d,Yt as f,an as p,b as m,c as h,cn as g,g as _,gn as v,i as y,in as b,ln as x,on as S,ot as C,p as w,qn as T,r as E,rt as D,un as O,xt as k}from"./lucide-react-H_-8G79A.js";import{n as A,p as j,t as M}from"./store-DHO_a8BD.js";import{D as N,I as P,M as F,W as I,it as L,j as R,w as z,z as ee}from"./display-CketawEF.js";import{a as te,i as ne,o as re,t as B,x as ie}from"./index-br_Y6Cvt.js";var V=e(T(),1),H={online:`came online`,offline:`went offline`,shutdown:`shut down`,restarted:`restarted`,"compaction-requested":`compaction requested`,compacting:`compaction started`,compacted:`context compacted`,"compaction-stalled":`compaction stalled — may need manual intervention`,"clear-requested":`clear requested`,"clearing-context":`clear started`,"context-cleared":`context cleared`,"clear-stalled":`context clear stalled — may need manual intervention`,"shutting-down":`shutting down`},ae=new Set(Object.keys(H)),oe=3e3;function U(e){return new Date(e.occurredAt??e.createdAt).getTime()}function se(e){if(e.kind!==`session`)return null;let t=e.payload?.session;return!t||t.type!==`narration`&&t.type!==`reasoning`&&t.type!==`tool`?null:{id:e.id,kind:t.type,label:typeof t.label==`string`?t.label:void 0,text:e.body,ts:U(e),turnId:typeof t.turnId==`string`?t.turnId:void 0}}function ce(e){if(e.kind!==`session`)return null;let t=e.payload?.session;if(t?.type!==`notice`)return null;let n=typeof t.label==`string`&&t.label?t.label:`compacted`;return{id:`notice-${e.id}`,status:n,timestamp:U(e)}}function le(e){return e.kind===`system`&&e.from===`system`}var W=new Set([`agent.ready`,`agent.exited`,`agent.spawn_failed`]);function G(e){let t=new Date(e);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`}function ue(e){let t=e.filter(e=>ae.has(e.status)).slice().sort((e,t)=>e.timestamp-t.timestamp),n=[];for(let e of t)n.some(t=>t.id===e.id)||n.some(t=>t.status===e.status&&Math.abs(t.timestamp-e.timestamp)<=oe)||n.push(e);return n}function de(e,t,n,r=[],i){let a=[];for(let e of r){let t=e.entries.reduce((e,t)=>Math.min(e,I(t.originalCreatedAt)),1/0),n=Number.isFinite(t)?t-1:e.importedAt;a.push({ts:n,entry:{type:`import-boundary`,history:e,timestamp:n}});for(let t of e.entries)a.push({ts:I(t.originalCreatedAt),entry:{type:`imported-message`,history:e,entry:t}})}if(n){let e=new Date(n).getTime();Number.isFinite(e)&&a.push({ts:e,entry:{type:`created`,timestamp:e}})}if(i?.spawnedBy){let e=n?new Date(n).getTime():0,t=Number.isFinite(e)?e:0;a.push({ts:t,entry:{type:`agent-action`,direction:`spawned-by`,agentId:i.spawnedBy.agentId,provider:i.spawnedBy.provider,status:`ready`,timestamp:t}})}let o=new Map;for(let t of e){if(t.kind!==`session`)continue;let e=t.payload?.session;if(e?.type===`response`&&typeof e.turnId==`string`){let n=o.get(e.turnId)??[];n.push(t.body.trim()),o.set(e.turnId,n)}}let s=new Map,c=[];for(let t of e){if(le(t)){let e=typeof t.payload?.kind==`string`?t.payload.kind:``;if(W.has(e)){let n=t.payload,r=typeof n.agentId==`string`?n.agentId:``,i=U(t);if(!r){a.push({ts:i,entry:{type:`notice`,msg:t}});continue}let o=e===`agent.ready`?`ready`:e===`agent.exited`?`exited`:`failed`,c=s.get(r);if(c)c.entry.status=o,typeof n.reason==`string`&&(c.entry.reason=n.reason),c.entry.timestamp=i,c.ts=i;else{let e={ts:i,entry:{type:`agent-action`,direction:`spawned-child`,agentId:r,provider:typeof n.provider==`string`?n.provider:void 0,status:o,reason:typeof n.reason==`string`?n.reason:void 0,timestamp:i}};s.set(r,e),a.push(e)}continue}a.push({ts:U(t),entry:{type:`notice`,msg:t}});continue}let e=ce(t);if(e){c.push(e);continue}let n=se(t);if(n){if(n.kind===`narration`&&n.turnId&&o.get(n.turnId)?.some(e=>ee(e,n.text)))continue;a.push({ts:n.ts,entry:{type:`activity`,steps:[n],timestamp:n.ts}});continue}a.push({ts:U(t),entry:{type:`message`,msg:t}})}for(let e of ue([...t,...c]))a.push({ts:e.timestamp,entry:{type:`status`,event:e}});let l={created:0,date:1,"import-boundary":2,"imported-message":3,message:4,activity:4,notice:4,"agent-action":4,status:5};a.sort((e,t)=>{let n=e.ts-t.ts;return Math.abs(n)<3e3&&l[e.entry.type]-l[t.entry.type]||n});let u=[],d=``;for(let{ts:e,entry:t}of a){if(t.type===`created`){u.push(t);continue}let n=G(e);if(n!==d&&(u.push({type:`date`,date:n,timestamp:e}),d=n),t.type===`activity`){let e=u[u.length-1];if(e&&e.type===`activity`&&fe(e,t)){e.steps.push(...t.steps);continue}}u.push(t)}return u}function fe(e,t){let n=e.steps[e.steps.length-1]?.turnId,r=t.steps[0]?.turnId;return n&&r?n===r:!n&&!r}function pe(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`}function me(e,t){if(t.startsWith(`/`))return t;let n=`${e.replace(/\/+$/,``)}/${t}`.split(`/`),r=[];for(let e of n)!e||e===`.`||(e===`..`?r.pop():r.push(e));return`/`+r.join(`/`)}function he(e){let t=e.replace(/\/+$/,``),n=t.lastIndexOf(`/`);return n>0?t.slice(0,n):`/`}function ge(e,t){return e.filter(e=>e.status===`online`&&P(t,e.baseDir)).sort((e,t)=>R(t.baseDir).length-R(e.baseDir).length)[0]||e.find(e=>e.status===`online`)}function _e(){return typeof window<`u`&&window.matchMedia(`(min-width: 1280px)`).matches}function K(){return typeof window<`u`&&!!window.getSelection()?.toString()}function ve(e){let t=e.payload?.attachments;return Array.isArray(t)?t.filter(e=>!!(e&&typeof e==`object`&&typeof e.artifactId==`string`)):[]}var ye=[`👍`,`❤️`,`✅`,`👀`];function be(e){let t=new Map;for(let n of e.reactions||[]){let e=F(n.emoji),r=t.get(e)??{emoji:e,count:0,mine:!1,actors:[]};r.count+=1,r.mine=r.mine||n.actorId===`user`,r.actors.push(n.actorId),t.set(e,r)}return[...t.values()].sort((e,t)=>t.count-e.count||e.emoji.localeCompare(t.emoji))}function q(e){return z(e)}function xe(e,t){let n=e.readBy||[],r=e.resolvedToAgent||t||e.to,i=r?n.includes(r):!1,a=n.some(e=>e!==L);if(i||a){let e=i?r:n.find(e=>e!==L);return{label:`read`,title:e?`Read by ${e}`:`Read by agent`,tone:`ok`,icon:O}}if(e.deliveryStatus===`delivered`)return{label:`delivered`,title:`Delivered to agent inbox`,tone:`ok`,icon:O};if(e.deliveryStatus===`queued`)return{label:`queued`,title:`Queued until the target agent is available`,tone:`warn`,icon:f};if(e.deliveryStatus===`failed`||e.deliveryStatus===`dead`){let t=e.deliveryPoisonReason||e.deliveryLastError||e.deliveryStatus;return{label:e.deliveryStatus,title:t,tone:`danger`,icon:b}}return{label:`sent`,title:`Accepted by Relay`,tone:`muted`,icon:x}}function Se(e){let t=I(e.createdAt);return(e.reactions||[]).reduce((e,t)=>Math.max(e,I(t.updatedAt||t.createdAt)),t)}function Ce(e){return e?e.messages.reduce((e,t)=>Math.max(e,Se(t)),0):0}function J(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}function we(e){for(let t of e)t.previewUrl&&URL.revokeObjectURL(t.previewUrl)}function Te(e,t,n,r){if(e?.teamId)return{teamId:e.teamId};let i=[...t];n&&!i.some(e=>e.id===n.id)&&i.push(n);let a=i.filter(e=>e.threadId).reverse(),o=a.find(e=>!!r[A(e.threadId,e.channel)])??a[0];return o?.threadId?{threadId:o.threadId,...o.channel?{channel:o.channel}:{}}:null}var Y=r(),Ee={online:`text-emerald-400`,offline:`text-zinc-400`,shutdown:`text-zinc-400`,restarted:`text-blue-400`,"compaction-requested":`text-zinc-400`,compacting:`text-amber-400`,compacted:`text-amber-400`,"compaction-stalled":`text-red-400`,"clear-requested":`text-zinc-400`,"clearing-context":`text-amber-400`,"context-cleared":`text-amber-400`,"clear-stalled":`text-red-400`,"shutting-down":`text-zinc-400`};function De(e){let t=new Date,[n,r,i]=e.split(`-`).map(Number),a=new Date(n,r-1,i);if(e===G(t.getTime()))return`Today`;let o=new Date(t);return o.setDate(o.getDate()-1),e===G(o.getTime())?`Yesterday`:a.toLocaleDateString(void 0,{weekday:`short`,month:`short`,day:`numeric`,year:a.getFullYear()===t.getFullYear()?void 0:`numeric`})}function Oe({event:e}){return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,Y.jsx)(l,{className:o(`w-2 h-2 fill-current`,Ee[e.status]||`text-zinc-400`)}),(0,Y.jsx)(`span`,{children:H[e.status]||e.status}),(0,Y.jsx)(`span`,{className:`text-muted-foreground/60`,children:J(e.timestamp)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var ke={"branch.landed":k,"context.warning":w},Ae={"branch.landed":`branch landed`,"context.warning":`context warning`};function je({msg:e}){let t=typeof e.payload?.kind==`string`?e.payload.kind:``,n=ke[t]??c,r=Ae[t]??(t||`system signal`);return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground`,children:[(0,Y.jsx)(n,{className:`h-3 w-3`}),(0,Y.jsx)(`span`,{children:r}),(0,Y.jsx)(`span`,{className:`text-muted-foreground/60`,children:J(e.occurredAt??e.createdAt)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}var Me={"spawned-child":{ready:`spawned`,exited:`spawned · exited`,failed:`spawn failed`},"spawned-by":{ready:`spawned by`,exited:`spawned by`,failed:`spawned by`}},Ne={"spawned-child-ready":i,"spawned-child-exited":D,"spawned-child-failed":b,"spawned-by-ready":n,"spawned-by-exited":n,"spawned-by-failed":n},Pe={"spawned-child-ready":`text-emerald-400`,"spawned-child-exited":`text-zinc-400`,"spawned-child-failed":`text-red-400`,"spawned-by-ready":`text-blue-400`,"spawned-by-exited":`text-blue-400`,"spawned-by-failed":`text-blue-400`};function Fe({entry:e}){let t=M(e=>e.agentsById),n=M(e=>e.openInboxThread),r=M(e=>e.switchView),i=t[e.agentId]||null,a=i?.label??e.agentId,s=`${e.direction}-${e.status}`,l=Me[e.direction]?.[e.status]??e.direction,u=Ne[s]??c,d=Pe[s]??`text-muted-foreground`,f=e.status===`failed`;function p(){n(e.agentId),r(`chat`)}return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:o(`flex items-center gap-1.5 rounded-full border bg-muted/30 px-2.5 py-1 text-xs`,f?`border-red-500/30`:`border-border`),children:[(0,Y.jsx)(u,{className:o(`h-3 w-3 shrink-0`,d)}),(0,Y.jsx)(`span`,{className:`text-muted-foreground`,children:l}),(0,Y.jsxs)(`button`,{type:`button`,className:`inline-flex items-center gap-1 rounded hover:text-foreground text-muted-foreground transition-colors hover:bg-muted/50 px-1`,onClick:p,title:`Open chat for ${a}`,children:[(0,Y.jsx)(ie,{agent:i,className:`w-4 h-4 text-[10px]`}),(0,Y.jsx)(`span`,{className:`max-w-[160px] truncate`,children:a})]}),e.reason&&f&&(0,Y.jsxs)(`span`,{className:`text-red-400/80 max-w-[120px] truncate`,title:e.reason,children:[`— `,e.reason]}),(0,Y.jsx)(`span`,{className:`text-muted-foreground/60 shrink-0`,children:J(e.timestamp)})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Ie({blockedLabel:e,onInterrupt:n}){return e?(0,Y.jsx)(`div`,{className:`flex justify-start mb-3`,children:(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-red-500/30 px-4 py-2.5`,children:[(0,Y.jsx)(b,{className:`w-3.5 h-3.5 text-red-400`}),(0,Y.jsxs)(`span`,{className:`text-xs text-red-300`,children:[`blocked: `,e]})]})}):(0,Y.jsx)(`div`,{className:`flex justify-start mb-3`,children:(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 rounded-2xl rounded-bl-sm bg-card ring-1 ring-foreground/10 px-4 py-2.5`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,Y.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_infinite]`}),(0,Y.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.2s_infinite]`}),(0,Y.jsx)(`span`,{className:`w-1.5 h-1.5 rounded-full bg-yellow-400 animate-[pulse_1.4s_ease-in-out_0.4s_infinite]`})]}),(0,Y.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`working`}),n&&(0,Y.jsxs)(`button`,{onClick:n,title:`Interrupt`,className:`ml-1 flex items-center gap-1 rounded-full px-1.5 py-0.5 text-xs text-muted-foreground hover:text-red-300 hover:bg-red-500/10 transition-colors`,children:[(0,Y.jsx)(t,{className:`w-3.5 h-3.5`}),`Stop`]})]})})}var X=`agent-relay:chat-hide-tools`,Z=(()=>{let e=new Set,t=(()=>{try{return window.localStorage.getItem(X)===`on`}catch{return!1}})(),n=()=>e.forEach(e=>e());return typeof window<`u`&&window.addEventListener(`storage`,e=>{e.key===X&&(t=e.newValue===`on`,n())}),{subscribe(t){return e.add(t),()=>e.delete(t)},get:()=>t,toggle(){t=!t;try{window.localStorage.setItem(X,t?`on`:`off`)}catch{}n()}}})();function Le(){return[(0,V.useSyncExternalStore)(Z.subscribe,Z.get,()=>!1),Z.toggle]}function Re({steps:e,showReasoning:t}){let[n,r]=Le(),i=t?e:e.filter(e=>e.kind!==`reasoning`);if(!i.length)return null;let a=i.filter(e=>e.kind===`tool`).length;return(0,Y.jsx)(`div`,{className:`flex justify-start mb-2`,children:(0,Y.jsxs)(`div`,{className:`max-w-[85%] md:max-w-[75%] min-w-0 space-y-1.5`,children:[i.map(e=>e.kind===`narration`?(0,Y.jsx)(B,{text:e.text,className:`text-sm leading-relaxed break-words text-foreground/90 min-w-0`},e.id):e.kind===`reasoning`?(0,Y.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground/80`,children:[(0,Y.jsx)(v,{className:`w-3.5 h-3.5 mt-0.5 shrink-0 opacity-70`}),(0,Y.jsx)(B,{text:e.text,className:`italic break-words min-w-0`})]},e.id):n?null:(0,Y.jsxs)(`div`,{className:`flex items-start gap-1.5 text-xs leading-relaxed text-muted-foreground`,children:[(0,Y.jsx)(_,{className:`w-3 h-3 mt-0.5 shrink-0 opacity-70`}),(0,Y.jsxs)(`span`,{className:`min-w-0 break-words`,children:[(0,Y.jsx)(`span`,{className:`font-medium`,children:e.label||`tool`}),e.text?(0,Y.jsxs)(`span`,{className:`opacity-70`,children:[` — `,e.text]}):null]})]},e.id)),a>0&&(0,Y.jsxs)(`button`,{onClick:r,className:`flex items-center gap-1 text-[11px] text-muted-foreground/50 hover:text-muted-foreground transition-colors text-left`,children:[(0,Y.jsx)(S,{className:o(`w-3 h-3 shrink-0 transition-transform`,!n&&`rotate-90`)}),(0,Y.jsx)(`span`,{children:n?`show ${a} tool step${a===1?``:`s`}`:`hide tool steps`})]})]})})}function Q({date:e}){return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-3 py-3 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground`,children:De(e)}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function ze({timestamp:e}){let t=new Date(e).toLocaleString(void 0,{month:`short`,day:`numeric`,year:`numeric`,hour:`2-digit`,minute:`2-digit`});return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`span`,{className:`text-xs text-muted-foreground/60`,children:[`created `,t]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Be({history:e}){let t=e.sourceAgentLabel||e.sourcePeerId,n=e.entries.length;return(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-2 my-1`,children:[(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`}),(0,Y.jsxs)(`div`,{className:`flex items-center gap-1.5 rounded-full border border-dashed border-border bg-muted/30 px-2 py-1 text-[11px] text-muted-foreground`,children:[(0,Y.jsx)(u,{className:`h-3 w-3`}),(0,Y.jsxs)(`span`,{children:[`forked history from `,t]}),(0,Y.jsxs)(`span`,{className:`text-muted-foreground/60`,children:[n,` `,n===1?`entry`:`entries`]})]}),(0,Y.jsx)(`div`,{className:`h-px flex-1 bg-border`})]})}function Ve({entry:e,sourceLabel:t}){let n=e.message,r=N(n),i=e.originalFrom===L;return(0,Y.jsx)(`div`,{className:o(`flex mb-3 opacity-80`,i?`justify-end`:`justify-start`),children:(0,Y.jsx)(`div`,{className:`max-w-[85%] md:max-w-[75%]`,children:(0,Y.jsxs)(`div`,{className:o(`rounded-2xl border border-dashed px-3.5 py-2 text-sm select-text`,i?`border-primary/25 bg-primary/10 text-foreground rounded-br-sm`:`border-border bg-card/70 rounded-bl-sm`),children:[(0,Y.jsxs)(`div`,{className:o(`mb-1 text-[10px] uppercase tracking-normal`,i?`text-primary/70`:`text-muted-foreground`),children:[`forked history · `,i?`you`:t||e.originalFrom]}),(0,Y.jsx)(B,{text:r,rawBody:n.body,className:`leading-relaxed`}),(0,Y.jsxs)(`p`,{className:o(`mt-1 flex items-center justify-end gap-1.5 text-xs`,i?`text-primary/60`:`text-muted-foreground`),children:[(0,Y.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.originalMessageId]}),(0,Y.jsx)(`span`,{children:J(e.originalCreatedAt)})]})]})})})}function He({scrollRef:e,timeline:t}){let[n,r]=(0,V.useState)(!0),[i,a]=(0,V.useState)(null),[s,c]=(0,V.useState)(null),[l,u]=(0,V.useState)(!0),d=(0,V.useMemo)(()=>{for(let e=t.length-1;e>=0;e--){let n=t[e];if(!(!n||n.type!==`message`)&&n.msg.from===`user`)return{id:n.msg.id,body:N(n.msg)}}return null},[t]);if((0,V.useEffect)(()=>{let t=e.current;if(!d||!t){r(!0);return}let n=t.querySelector(`[data-msg-id="${d.id}"]`);if(!n){r(!0);return}let i=new IntersectionObserver(([e])=>{if(e&&(r(e.isIntersecting),!e.isIntersecting)){let n=e.rootBounds?.top??t.getBoundingClientRect().top;u(e.boundingClientRect.top<n)}},{root:t,threshold:0});i.observe(n);let a=()=>{let e=n.getBoundingClientRect(),r=t.getBoundingClientRect();u(e.top<r.top)};return t.addEventListener(`scroll`,a,{passive:!0}),()=>{i.disconnect(),t.removeEventListener(`scroll`,a)}},[d?.id,e]),n||!d||!d.body.trim())return null;let f=i?.id===d.id?i.value:!1,m=s?.id===d.id?s.value:!1;function h(){let t=e.current;!t||!d||t.querySelector(`[data-msg-id="${d.id}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`})}let _=l?`↑`:`↓`;return m?(0,Y.jsx)(`div`,{className:`sticky top-0 z-10 -mx-3 md:-mx-4 -mt-3 md:-mt-4 bg-background`,children:(0,Y.jsxs)(`button`,{type:`button`,className:`w-full flex items-center justify-center gap-1.5 py-1 text-[11px] text-primary/60 hover:text-primary border-b border-primary/10 transition-colors`,onClick:()=>c({id:d.id,value:!1}),children:[(0,Y.jsx)(`span`,{children:`Your prompt`}),(0,Y.jsx)(g,{className:`w-3 h-3`})]})}):(0,Y.jsxs)(`div`,{className:`sticky top-0 z-10 -mx-3 md:-mx-4 -mt-3 md:-mt-4 px-2 md:px-3 pt-2 pb-1.5 bg-background border-b border-primary/10`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-2 text-[11px] text-muted-foreground mb-0.5`,children:[(0,Y.jsx)(`span`,{className:`font-medium text-primary/70`,children:`Your prompt`}),(0,Y.jsxs)(`button`,{type:`button`,className:`ml-auto hover:text-foreground transition-colors`,onClick:h,children:[_,` scroll to original`]}),(0,Y.jsx)(`button`,{type:`button`,className:`hover:text-foreground transition-colors`,onClick:()=>c({id:d.id,value:!0}),title:`Minimize`,children:(0,Y.jsx)(p,{className:`w-3 h-3`})})]}),(0,Y.jsx)(`p`,{className:o(`text-sm text-foreground leading-snug cursor-pointer`,!f&&`line-clamp-2`),onClick:()=>a({id:d.id,value:!f}),children:d.body})]})}function Ue(){return(0,V.useSyncExternalStore)(e=>j.subscribe(e),()=>j.getPlayingKey(),()=>null)}function We({attachment:e,outbound:t}){let n=e.kind===`image`,r=e.title||e.artifactId,[i,c]=(0,V.useState)(null),[l,u]=(0,V.useState)(null),[f,p]=(0,V.useState)(!1),[m,h]=(0,V.useState)(!1);(0,V.useEffect)(()=>{let t=!1,n=null;async function r(){p(!0),u(null),c(null);try{let r=await a(`/artifacts/${encodeURIComponent(e.artifactId)}/content`);if(t)return;n=URL.createObjectURL(r),c(n)}catch(e){t||u(e.message)}finally{t||p(!1)}}return r(),()=>{t=!0,n&&URL.revokeObjectURL(n)}},[e.artifactId]);function g(){if(!i)return;let e=document.createElement(`a`);e.href=i,e.download=r,document.body.appendChild(e),e.click(),e.remove()}return n?(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(`button`,{type:`button`,onClick:()=>i&&h(!0),disabled:!i,className:`group/img mt-2 block overflow-hidden rounded-md border border-foreground/10 bg-black/10 text-left disabled:cursor-default relative`,children:i?(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(`img`,{src:i,alt:r,loading:`lazy`,className:`block max-h-72 max-w-full object-contain`}),(0,Y.jsx)(`span`,{className:`absolute inset-0 flex items-center justify-center bg-black/0 group-hover/img:bg-black/30 transition-colors`,children:(0,Y.jsx)(E,{className:`w-6 h-6 text-white opacity-0 group-hover/img:opacity-100 transition-opacity drop-shadow-md`})})]}):(0,Y.jsxs)(`span`,{className:o(`flex min-h-20 items-center gap-2 px-3 py-2 text-xs`,l?`text-red-400`:`text-muted-foreground`),children:[f?(0,Y.jsx)(C,{className:`h-4 w-4 animate-spin`}):(0,Y.jsx)(b,{className:`h-4 w-4`}),l||`Loading image`]})}),i&&(0,Y.jsx)(ne,{open:m,onOpenChange:h,children:(0,Y.jsxs)(te,{className:`max-w-[95vw] max-h-[95vh] w-auto p-0 bg-black/95 border-zinc-800 gap-0 overflow-hidden`,showCloseButton:!1,children:[(0,Y.jsx)(re,{className:`sr-only`,children:r}),(0,Y.jsxs)(`div`,{className:`relative flex items-center justify-center min-h-[200px]`,children:[(0,Y.jsx)(`img`,{src:i,alt:r,className:`max-w-[93vw] max-h-[90vh] object-contain`}),(0,Y.jsxs)(`div`,{className:`absolute top-2 right-2 flex gap-1`,children:[(0,Y.jsx)(`button`,{type:`button`,onClick:g,className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Download`,children:(0,Y.jsx)(d,{className:`w-4 h-4`})}),(0,Y.jsx)(`button`,{type:`button`,onClick:()=>h(!1),className:`rounded-md bg-black/60 hover:bg-black/80 p-2 text-white transition-colors`,title:`Close`,children:(0,Y.jsx)(y,{className:`w-4 h-4`})})]})]})]})})]}):(0,Y.jsxs)(`button`,{type:`button`,onClick:g,disabled:!i,className:o(`mt-2 flex w-full items-center gap-2 rounded-md border px-2.5 py-2 text-left text-xs transition-colors disabled:cursor-default disabled:opacity-70`,t?`border-primary-foreground/20 bg-primary-foreground/10 hover:bg-primary-foreground/15`:`border-border bg-background hover:bg-muted`),children:[f?(0,Y.jsx)(C,{className:`h-4 w-4 shrink-0 animate-spin`}):l?(0,Y.jsx)(b,{className:`h-4 w-4 shrink-0 text-red-400`}):(0,Y.jsx)(s,{className:`h-4 w-4 shrink-0`}),(0,Y.jsx)(`span`,{className:o(`min-w-0 flex-1 truncate`,l&&`text-red-400`),children:l||r}),(0,Y.jsx)(d,{className:`h-3.5 w-3.5 shrink-0`})]})}function $({open:e,onToggle:t,onReact:n}){return(0,Y.jsxs)(`div`,{className:`relative shrink-0`,children:[(0,Y.jsx)(`button`,{type:`button`,title:`Add reaction`,onClick:t,className:o(`inline-flex h-5 w-6 items-center justify-center rounded-full border border-border bg-popover text-muted-foreground shadow-sm transition hover:bg-muted hover:text-foreground`,e?`opacity-100`:`opacity-0 pointer-events-none md:group-hover/msg:opacity-100 md:group-hover/msg:pointer-events-auto`),children:(0,Y.jsx)(m,{className:`h-3 w-3`})}),e&&(0,Y.jsx)(`div`,{className:`absolute bottom-full left-0 z-30 mb-1 flex gap-0.5 rounded-full border border-border bg-popover p-0.5 shadow-md`,children:ye.map(e=>(0,Y.jsx)(`button`,{type:`button`,title:`React ${e}`,onClick:()=>n(e),className:`inline-flex h-6 w-7 items-center justify-center rounded-full text-xs transition-colors hover:bg-muted`,children:e},e))})]})}var Ge=(0,V.memo)(function({msg:e,peer:n,onOpenReferencedPath:r,onPreviewReferencedPath:i,onPreviewReferencedPathEnd:a}){let s=e.from!==`user`&&e.to!==`user`,c=s?e.from===n:e.from===L,l=s?c?`→ ${e.to}`:`from ${e.from}`:null,u=M(e=>e.reactToMessage),d=M(e=>e.voiceTtsEnabled),f=Ue(),p=M(e=>{let t=e.agentsById[n]?.meta?.cwd;return typeof t==`string`?t:``}),[m,g]=(0,V.useState)(!1),_=(0,V.useRef)(null),v=(0,V.useRef)(null),y=(0,V.useRef)(!1),b=N(e),x=J(e.occurredAt??e.createdAt),S=ve(e),C=be(e),w=c?xe(e,n):null,T=w?.icon,E=f===String(e.id),D=d&&!c&&!s&&!q(e)&&b.trim().length>0;function O(t){t.stopPropagation(),E?j.bargeIn():j.speak(e.body,String(e.id))}(0,V.useEffect)(()=>{if(!m)return;function e(e){K()||_.current&&!_.current.contains(e.target)&&g(!1)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[m]);function k(e){return me(p||`/`,e)}function A(e,t){let n=k(e);r?.(n,t)}function P(e,t,n){let r=k(e);i?.(r,t,n)}function F(t){u(e,t),g(!1)}function I(e){e.button===0&&(v.current={x:e.clientX,y:e.clientY},y.current=!1)}function R(e){let t=v.current;t&&(Math.abs(e.clientX-t.x)>3||Math.abs(e.clientY-t.y)>3)&&(y.current=!0)}function z(t){if(!(q(e)||c||s)&&!t.target.closest(`a,button,input,textarea,select,[role="button"]`)){if(y.current||K()){y.current=!1;return}g(e=>!e)}}return(0,Y.jsx)(`div`,{"data-msg-id":e.id,className:o(`group/msg flex mb-3`,c?`justify-end`:`justify-start`),children:(0,Y.jsxs)(`div`,{ref:_,className:`relative max-w-[85%] md:max-w-[75%]`,children:[D&&(0,Y.jsx)(`button`,{type:`button`,title:E?`Stop playback`:`Play aloud`,onClick:O,className:o(`absolute -top-2 -right-2 z-20 inline-flex h-6 w-6 items-center justify-center rounded-full border bg-popover shadow-sm transition`,E?`border-primary/50 text-primary opacity-100`:`border-border text-muted-foreground opacity-70 hover:bg-muted hover:text-foreground hover:opacity-100 [@media(hover:hover)]:md:opacity-0 [@media(hover:hover)]:md:group-hover/msg:opacity-100`),children:E?(0,Y.jsx)(t,{className:`h-3.5 w-3.5 animate-pulse`}):(0,Y.jsx)(h,{className:`h-3.5 w-3.5`})}),(0,Y.jsxs)(`div`,{className:o(`rounded-2xl px-3.5 py-2 text-sm select-text`,!c&&!q(e)&&!s&&`cursor-pointer`,c?`bg-primary text-primary-foreground rounded-br-sm`:`bg-card ring-1 ring-foreground/10 rounded-bl-sm`),onPointerDown:I,onPointerMove:R,onClick:z,children:[l&&(0,Y.jsx)(`p`,{className:o(`text-xs font-medium mb-1 opacity-60`,c?`text-primary-foreground`:`text-muted-foreground`),children:l}),e.subject&&(0,Y.jsx)(`p`,{className:o(`text-xs font-semibold mb-1`,c?`text-primary-foreground/70`:`text-muted-foreground`),children:e.subject}),(0,Y.jsx)(B,{text:b,rawBody:e.body,className:`leading-relaxed`,onOpenPath:A,onPreviewPath:P,onPreviewPathEnd:a,resolvePathTitle:(e,t)=>`${k(e)}${t?`:${t}`:``}`}),S.map(t=>(0,Y.jsx)(We,{attachment:t,outbound:c},`${e.id}-${t.artifactId}-${t.role||``}`)),(0,Y.jsxs)(`p`,{className:o(`text-xs mt-1 flex items-center gap-1.5`,c?`justify-end text-primary-foreground/60`:`justify-end text-muted-foreground`),children:[(0,Y.jsxs)(`span`,{className:`opacity-40`,children:[`#`,e.id]}),(0,Y.jsx)(`span`,{children:x}),w&&T&&(0,Y.jsxs)(`span`,{className:o(`inline-flex items-center gap-1`,w.tone===`ok`&&`text-emerald-300`,w.tone===`warn`&&`text-amber-200`,w.tone===`danger`&&`text-red-200`),title:w.title,children:[(0,Y.jsx)(T,{className:`h-3 w-3`}),w.label]})]})]}),!q(e)&&C.length>0&&(0,Y.jsxs)(`div`,{className:o(`flex flex-wrap items-center gap-0.5 -mt-1 relative z-10`,c?`justify-end pr-2`:`justify-start pl-2`),children:[!c&&(0,Y.jsx)($,{open:m,onToggle:()=>g(e=>!e),onReact:F}),C.map(e=>(0,Y.jsxs)(`button`,{type:`button`,title:e.actors.join(`, `),onClick:()=>F(e.emoji),className:o(`inline-flex h-5 items-center gap-0.5 rounded-full border px-1.5 text-[11px] shadow-sm transition-colors`,e.mine?`border-primary/40 bg-primary/10 text-primary`:`border-border bg-background hover:bg-muted text-foreground`),children:[(0,Y.jsx)(`span`,{children:e.emoji}),e.count>1&&(0,Y.jsx)(`span`,{children:e.count})]},e.emoji))]}),!q(e)&&!c&&C.length===0&&(0,Y.jsx)(`div`,{className:`absolute -bottom-2.5 left-1 z-20`,children:(0,Y.jsx)($,{open:m,onToggle:()=>g(e=>!e),onReact:F})})]})})});export{_e as _,ze as a,Ve as c,He as d,pe as f,we as g,Te as h,Ie as i,je as l,he as m,Re as n,Q as o,ge as p,Fe as r,Be as s,Ge as t,Oe as u,Ce as v,de as y};
|
|
2
|
-
//# sourceMappingURL=MessageBubble-BrEKymoV.js.map
|