@triflux/remote 10.50.2 → 10.52.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.
Files changed (134) hide show
  1. package/cto/brief.mjs +0 -20
  2. package/cto/collect.mjs +1 -200
  3. package/cto/current.schema.json +2 -23
  4. package/cto/hygiene.mjs +2 -61
  5. package/cto/lake-root.mjs +0 -56
  6. package/cto/status.mjs +5 -187
  7. package/hub/index.mjs +0 -17
  8. package/hub/team/agent-map.json +0 -1
  9. package/hub/team/agents-row-attach.py +120 -0
  10. package/hub/team/agents-row.mjs +200 -0
  11. package/hub/team/ansi.mjs +1 -35
  12. package/hub/team/backend.mjs +9 -145
  13. package/hub/team/claude-daemon-control.mjs +1 -2
  14. package/hub/team/claude-session-projection.mjs +5 -73
  15. package/hub/team/cli/commands/attach.mjs +1 -17
  16. package/hub/team/cli/commands/control.mjs +12 -38
  17. package/hub/team/cli/commands/debug.mjs +2 -39
  18. package/hub/team/cli/commands/focus.mjs +5 -31
  19. package/hub/team/cli/commands/interrupt.mjs +6 -25
  20. package/hub/team/cli/commands/kill.mjs +1 -30
  21. package/hub/team/cli/commands/list.mjs +0 -16
  22. package/hub/team/cli/commands/send.mjs +6 -26
  23. package/hub/team/cli/commands/start/index.mjs +42 -133
  24. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  25. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  26. package/hub/team/cli/commands/start/start-mux.mjs +2 -7
  27. package/hub/team/cli/commands/status.mjs +2 -57
  28. package/hub/team/cli/commands/stop.mjs +14 -19
  29. package/hub/team/cli/help.mjs +4 -5
  30. package/hub/team/cli/manifest.mjs +5 -1
  31. package/hub/team/cli/render.mjs +0 -12
  32. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  33. package/hub/team/cli/services/state-store.mjs +5 -20
  34. package/hub/team/handoff.mjs +3 -34
  35. package/hub/team/headless.mjs +108 -1296
  36. package/hub/team/orchestrator.mjs +4 -30
  37. package/hub/team/pane.mjs +6 -28
  38. package/hub/team/psmux.mjs +3 -34
  39. package/hub/team/remote-session.mjs +1 -2
  40. package/hub/team/session-context.mjs +1 -1
  41. package/hub/team/session.mjs +7 -91
  42. package/hub/team/tui-core.mjs +1 -1
  43. package/hub/team/tui-viewer.mjs +11 -58
  44. package/hub/team/tui.mjs +8 -306
  45. package/hub/team/uds-orchestrator.mjs +2 -580
  46. package/hub/team/wt-manager.mjs +2 -2
  47. package/hub/workers/factory.mjs +6 -84
  48. package/hub/workers/interface.mjs +1 -7
  49. package/hub/workers/lib/jsonrpc-core.mjs +11 -0
  50. package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
  51. package/package.json +3 -8
  52. package/scripts/lib/agent-route-policy.mjs +143 -62
  53. package/scripts/lib/cli-claude.mjs +0 -14
  54. package/scripts/lib/env-probe.mjs +2 -131
  55. package/scripts/lib/gemini-profiles.mjs +2 -69
  56. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  57. package/scripts/lib/machine-profile.mjs +3 -17
  58. package/scripts/lib/mcp-filter.mjs +109 -53
  59. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  60. package/scripts/lib/mcp-health.mjs +2 -2
  61. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  62. package/scripts/lib/psmux-info.mjs +11 -59
  63. package/scripts/lib/skill-template.mjs +0 -245
  64. package/hub/hub-lifecycle.mjs +0 -248
  65. package/hub/pipe.mjs +0 -825
  66. package/hub/promote-penalties.mjs +0 -138
  67. package/hub/public/dashboard.html +0 -416
  68. package/hub/schema.sql +0 -209
  69. package/hub/server.mjs +0 -3048
  70. package/hub/store-adapter.mjs +0 -182
  71. package/hub/store.mjs +0 -1736
  72. package/hub/team/build-worker-prompt.mjs +0 -92
  73. package/hub/team/check-mcp-hub.mjs +0 -49
  74. package/hub/team/claude-native-bridge.mjs +0 -987
  75. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  76. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  77. package/hub/team/cli/services/hub-client.mjs +0 -264
  78. package/hub/team/cli/services/native-control.mjs +0 -145
  79. package/hub/team/codex-review.mjs +0 -520
  80. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  81. package/hub/team/conductor-registry.mjs +0 -121
  82. package/hub/team/conductor.mjs +0 -1529
  83. package/hub/team/consensus-meta.mjs +0 -176
  84. package/hub/team/cto-auto-collect.mjs +0 -115
  85. package/hub/team/cto-pull.mjs +0 -31
  86. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  87. package/hub/team/dashboard.mjs +0 -322
  88. package/hub/team/event-log.mjs +0 -84
  89. package/hub/team/execution-mode.mjs +0 -324
  90. package/hub/team/extract-completion-payload.mjs +0 -110
  91. package/hub/team/headless-bridge-session.mjs +0 -17
  92. package/hub/team/health-probe.mjs +0 -435
  93. package/hub/team/interactive-native-launcher.mjs +0 -119
  94. package/hub/team/interactive-native-worker.mjs +0 -80
  95. package/hub/team/interactive-tui-transport.mjs +0 -479
  96. package/hub/team/launcher-template.mjs +0 -144
  97. package/hub/team/lead-control.mjs +0 -191
  98. package/hub/team/mcp-selector.mjs +0 -145
  99. package/hub/team/native-supervisor.mjs +0 -460
  100. package/hub/team/native.mjs +0 -727
  101. package/hub/team/recovery-store.mjs +0 -149
  102. package/hub/team/remote-probe.mjs +0 -292
  103. package/hub/team/remote-watcher.mjs +0 -506
  104. package/hub/team/runtime-strategy.mjs +0 -200
  105. package/hub/team/schemas/worker-completion.json +0 -39
  106. package/hub/team/sentinel-capture.mjs +0 -127
  107. package/hub/team/session-sync.mjs +0 -316
  108. package/hub/team/swarm-cli.mjs +0 -390
  109. package/hub/team/swarm-hypervisor.mjs +0 -2206
  110. package/hub/team/swarm-intent.mjs +0 -216
  111. package/hub/team/swarm-locks.mjs +0 -335
  112. package/hub/team/swarm-planner.mjs +0 -371
  113. package/hub/team/swarm-preflight.mjs +0 -366
  114. package/hub/team/swarm-reconciler.mjs +0 -242
  115. package/hub/team/synapse-cli.mjs +0 -458
  116. package/hub/team/synapse-http.mjs +0 -311
  117. package/hub/team/synapse-registry.mjs +0 -618
  118. package/hub/team/tui-remote-adapter.mjs +0 -377
  119. package/hub/team/tui-synapse.mjs +0 -223
  120. package/hub/team/worker-completion-validator.mjs +0 -79
  121. package/hub/team/worker-sandbox.mjs +0 -125
  122. package/hub/team/worktree-lifecycle.mjs +0 -639
  123. package/hub/team/wt-templates.json +0 -43
  124. package/hub/tools.mjs +0 -900
  125. package/hub/workers/antigravity-route-worker.mjs +0 -179
  126. package/hub/workers/codex-app-server-worker.mjs +0 -1067
  127. package/hub/workers/delegator-mcp.mjs +0 -1098
  128. package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
  129. package/scripts/lib/claudemd-scanner.mjs +0 -223
  130. package/scripts/lib/context.mjs +0 -67
  131. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  132. package/scripts/lib/handoff.mjs +0 -187
  133. package/scripts/lib/logger.mjs +0 -122
  134. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,39 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "WorkerCompletion",
4
- "description": "Self-reported worker completion payload emitted by a swarm shard worker.",
5
- "type": "object",
6
- "required": ["status"],
7
- "properties": {
8
- "status": {
9
- "type": "string",
10
- "enum": ["ok", "skipped", "failed"]
11
- },
12
- "shard": { "type": "string" },
13
- "reason": { "type": "string" },
14
- "commits_made": {
15
- "type": "array",
16
- "items": {
17
- "type": "object",
18
- "required": ["sha"],
19
- "properties": {
20
- "sha": { "type": "string", "minLength": 7 },
21
- "message": { "type": "string" }
22
- },
23
- "additionalProperties": true
24
- }
25
- }
26
- },
27
- "additionalProperties": true,
28
- "allOf": [
29
- {
30
- "if": { "properties": { "status": { "const": "ok" } } },
31
- "then": {
32
- "required": ["commits_made"],
33
- "properties": {
34
- "commits_made": { "minItems": 1 }
35
- }
36
- }
37
- }
38
- ]
39
- }
@@ -1,127 +0,0 @@
1
- // hub/team/sentinel-capture.mjs — stdout sentinel-framed payload capture (#125)
2
- //
3
- // Workers emit completion payloads delimited by `<<<TFX_COMPLETION_BEGIN>>>`
4
- // and `<<<TFX_COMPLETION_END>>>` (see swarm-hypervisor.mjs buildWorkerPrompt
5
- // appendix). conductor.mjs feeds every stdout chunk through `push()`. After the
6
- // child exits, `snapshot()` returns the captured slice (BEGIN..END inclusive)
7
- // for `extractCompletionPayload`.
8
- //
9
- // Why this lives outside the 16 KiB stdoutTail buffer:
10
- // payloads with large `commits_made` arrays can exceed 16 KiB and a sliding
11
- // tail would head-truncate the BEGIN marker, leading to silent fallback to
12
- // brace-scan and an incorrect inner-object extraction (#115 Codex Finding 2).
13
- //
14
- // Pure module — no I/O, no timers — so it can be unit-tested directly.
15
-
16
- export const SENTINEL_BEGIN = "<<<TFX_COMPLETION_BEGIN>>>";
17
- export const SENTINEL_END = "<<<TFX_COMPLETION_END>>>";
18
-
19
- const DEFAULT_MAX_BYTES = 1 << 20; // 1 MiB safety cap on captured region
20
-
21
- // Per the worker prompt appendix, both markers must appear alone on their own
22
- // line. Substring matches inside log lines / debug echoes must NOT trigger
23
- // capture or terminate it. We treat `\n`, `\r`, and string boundaries (start
24
- // or end of buffer) as line edges so Windows `\r\n` works without special-casing.
25
- function isLineEdgeChar(ch) {
26
- return ch === "\n" || ch === "\r";
27
- }
28
-
29
- function findStandaloneMarker(text, marker, fromIdx = 0) {
30
- let from = fromIdx;
31
- while (from <= text.length - marker.length) {
32
- const idx = text.indexOf(marker, from);
33
- if (idx === -1) return -1;
34
- const before = idx === 0 || isLineEdgeChar(text[idx - 1]);
35
- const afterIdx = idx + marker.length;
36
- const after = afterIdx === text.length || isLineEdgeChar(text[afterIdx]);
37
- if (before && after) return idx;
38
- from = idx + 1;
39
- }
40
- return -1;
41
- }
42
-
43
- /**
44
- * Streaming sentinel capture state machine.
45
- *
46
- * @param {object} [opts]
47
- * @param {number} [opts.maxBytes=1048576] safety cap on the captured region.
48
- * If the capture grows past this and head-truncation removes the BEGIN
49
- * marker, `isOverflow()` becomes true so the conductor can deterministically
50
- * reject instead of falling back to brace-scan on a runaway payload
51
- * (Codex R1 MEDIUM finding).
52
- * @returns {{push: (text: string) => void, snapshot: () => string,
53
- * isActive: () => boolean, isComplete: () => boolean,
54
- * isOverflow: () => boolean}}
55
- */
56
- export function createSentinelCapture({ maxBytes = DEFAULT_MAX_BYTES } = {}) {
57
- let capturing = false;
58
- let capture = "";
59
- let overlap = "";
60
- let done = false;
61
- let overflowed = false;
62
-
63
- // Boundary overlap covers the marker length plus one extra byte so we can
64
- // verify the line-edge character that precedes BEGIN across chunk seams.
65
- const overlapWindow = SENTINEL_BEGIN.length;
66
-
67
- function maybeFlagOverflow() {
68
- if (findStandaloneMarker(capture, SENTINEL_BEGIN) === -1) {
69
- overflowed = true;
70
- done = true;
71
- return true;
72
- }
73
- return false;
74
- }
75
-
76
- function push(text) {
77
- if (done || typeof text !== "string" || text.length === 0) return;
78
-
79
- if (!capturing) {
80
- const search = overlap + text;
81
- const idx = findStandaloneMarker(search, SENTINEL_BEGIN);
82
- if (idx !== -1) {
83
- capturing = true;
84
- capture = search.slice(idx);
85
- overlap = "";
86
- if (capture.length > maxBytes) {
87
- capture = capture.slice(-maxBytes);
88
- if (maybeFlagOverflow()) return;
89
- }
90
- } else {
91
- overlap = search.slice(-overlapWindow);
92
- return;
93
- }
94
- } else {
95
- capture += text;
96
- if (capture.length > maxBytes) {
97
- capture = capture.slice(-maxBytes);
98
- if (maybeFlagOverflow()) return;
99
- }
100
- }
101
-
102
- // Stop capturing once the standalone-line END marker is seen. Trim to the
103
- // first BEGIN..END pair so a worker that re-emitted (retry, debug log)
104
- // doesn't bloat the snapshot.
105
- const endIdx = findStandaloneMarker(capture, SENTINEL_END);
106
- if (endIdx !== -1) {
107
- done = true;
108
- capture = capture.slice(0, endIdx + SENTINEL_END.length);
109
- }
110
- }
111
-
112
- return {
113
- push,
114
- snapshot() {
115
- return capture;
116
- },
117
- isActive() {
118
- return capturing && !done;
119
- },
120
- isComplete() {
121
- return done && !overflowed;
122
- },
123
- isOverflow() {
124
- return overflowed;
125
- },
126
- };
127
- }
@@ -1,316 +0,0 @@
1
- import { readState } from "@triflux/core/hub/state.mjs";
2
-
3
- const DEFAULT_TIMEOUT_MS = 2500;
4
- const DEFAULT_HEADLESS_POLL_MS = 1000;
5
-
6
- function resolveFetch(fetchImpl) {
7
- if (typeof fetchImpl === "function") return fetchImpl;
8
- if (typeof globalThis.fetch === "function")
9
- return globalThis.fetch.bind(globalThis);
10
- return null;
11
- }
12
-
13
- function normalizeHubBaseUrl(hubUrl) {
14
- return String(hubUrl || "")
15
- .replace(/\/+$/, "")
16
- .replace(/\/mcp$/, "");
17
- }
18
-
19
- function safeAbortSignal(timeoutMs) {
20
- if (typeof AbortSignal?.timeout !== "function") return undefined;
21
- return AbortSignal.timeout(timeoutMs);
22
- }
23
-
24
- function isPidAlive(pid) {
25
- const resolvedPid = Number(pid);
26
- if (!Number.isFinite(resolvedPid) || resolvedPid <= 0) return false;
27
- try {
28
- process.kill(resolvedPid, 0);
29
- return true;
30
- } catch {
31
- return false;
32
- }
33
- }
34
-
35
- function resolveHeadlessHubUrl(hubUrl) {
36
- const normalized = normalizeHubBaseUrl(hubUrl);
37
- if (normalized) return normalized;
38
-
39
- const stateUrl = normalizeHubBaseUrl(readState()?.url);
40
- if (stateUrl) return stateUrl;
41
-
42
- const envHubUrl = normalizeHubBaseUrl(process.env.TFX_HUB_URL);
43
- if (envHubUrl) return envHubUrl;
44
-
45
- const envPort = Number(process.env.TFX_HUB_PORT || "27888");
46
- const port = Number.isFinite(envPort) && envPort > 0 ? envPort : 27888;
47
- return `http://127.0.0.1:${port}`;
48
- }
49
-
50
- function hasLiveHubState() {
51
- const state = readState();
52
- return !!normalizeHubBaseUrl(state?.url) && isPidAlive(state?.pid);
53
- }
54
-
55
- function toHeadlessSessionAgentId(sessionName) {
56
- const normalizedSessionName = String(sessionName || "").trim();
57
- return normalizedSessionName ? `session:${normalizedSessionName}` : "";
58
- }
59
-
60
- async function dispatchHeadlessCommand(command, callbacks) {
61
- const callbackMap = {
62
- pause: callbacks.onPause,
63
- resume: callbacks.onResume,
64
- abort: callbacks.onAbort,
65
- reassign: callbacks.onReassign,
66
- };
67
- const handler = callbackMap[command?.command];
68
- if (typeof handler === "function") {
69
- await handler(command);
70
- }
71
- }
72
-
73
- async function safeJson(res) {
74
- try {
75
- return await res.json();
76
- } catch {
77
- return {};
78
- }
79
- }
80
-
81
- function normalizeLeadCommandMessage(message) {
82
- const payload =
83
- message?.payload && typeof message.payload === "object"
84
- ? message.payload
85
- : {};
86
- const command = String(payload.command || "")
87
- .trim()
88
- .toLowerCase();
89
- if (!command) return null;
90
- return {
91
- messageId: message.id || null,
92
- topic: message.topic || "lead.control",
93
- fromAgent: message.from_agent || payload.issued_by || "lead",
94
- command,
95
- reason: payload.reason || "",
96
- payload,
97
- traceId: message.trace_id || null,
98
- correlationId: message.correlation_id || null,
99
- createdAtMs: message.created_at_ms || null,
100
- };
101
- }
102
-
103
- export async function subscribeToLeadCommands({
104
- hubUrl,
105
- agentId,
106
- maxMessages = 10,
107
- autoAck = true,
108
- timeoutMs = DEFAULT_TIMEOUT_MS,
109
- topics = ["lead.control"],
110
- onCommand = null,
111
- fetchImpl,
112
- } = {}) {
113
- const requestFetch = resolveFetch(fetchImpl);
114
- if (!requestFetch) {
115
- return { ok: false, error: "FETCH_UNAVAILABLE", commands: [] };
116
- }
117
-
118
- const hubBase = normalizeHubBaseUrl(hubUrl);
119
- if (!hubBase) {
120
- return { ok: false, error: "HUB_URL_REQUIRED", commands: [] };
121
- }
122
-
123
- const normalizedAgentId = String(agentId || "").trim();
124
- if (!normalizedAgentId) {
125
- return { ok: false, error: "AGENT_ID_REQUIRED", commands: [] };
126
- }
127
-
128
- try {
129
- const res = await requestFetch(`${hubBase}/bridge/context`, {
130
- method: "POST",
131
- headers: { "Content-Type": "application/json" },
132
- body: JSON.stringify({
133
- agent_id: normalizedAgentId,
134
- topics: Array.isArray(topics) ? topics : ["lead.control"],
135
- max_messages: maxMessages,
136
- auto_ack: autoAck,
137
- }),
138
- signal: safeAbortSignal(timeoutMs),
139
- });
140
-
141
- const body = await safeJson(res);
142
- const messages = Array.isArray(body?.data?.messages)
143
- ? body.data.messages
144
- : [];
145
- const commands = messages
146
- .filter((message) => message?.topic === "lead.control")
147
- .map(normalizeLeadCommandMessage)
148
- .filter(Boolean);
149
-
150
- if (typeof onCommand === "function") {
151
- for (const command of commands) {
152
- await onCommand(command);
153
- }
154
- }
155
-
156
- return {
157
- ok: res.ok && body?.ok !== false,
158
- status: res.status,
159
- commands,
160
- messageCount: messages.length,
161
- body,
162
- };
163
- } catch (error) {
164
- return {
165
- ok: false,
166
- error: "LEAD_COMMAND_SUBSCRIBE_FAILED",
167
- message: error?.message || "lead command subscribe failed",
168
- commands: [],
169
- };
170
- }
171
- }
172
-
173
- export function createHeadlessControlSubscriber(
174
- sessionName,
175
- {
176
- onPause,
177
- onResume,
178
- onAbort,
179
- onReassign,
180
- hubUrl,
181
- pollIntervalMs = DEFAULT_HEADLESS_POLL_MS,
182
- maxMessages = 10,
183
- autoAck = true,
184
- timeoutMs = DEFAULT_TIMEOUT_MS,
185
- fetchImpl,
186
- } = {},
187
- ) {
188
- const sessionAgentId = toHeadlessSessionAgentId(sessionName);
189
- if (!sessionAgentId) {
190
- return { stop() {} };
191
- }
192
-
193
- const explicitHubUrl =
194
- normalizeHubBaseUrl(hubUrl) || normalizeHubBaseUrl(process.env.TFX_HUB_URL);
195
- if (!explicitHubUrl && !hasLiveHubState()) {
196
- return { stop() {} };
197
- }
198
-
199
- const resolvedHubUrl = explicitHubUrl || resolveHeadlessHubUrl(hubUrl);
200
- const callbacks = { onPause, onResume, onAbort, onReassign };
201
- const intervalMs = Math.max(
202
- 50,
203
- Number(pollIntervalMs) || DEFAULT_HEADLESS_POLL_MS,
204
- );
205
-
206
- let stopped = false;
207
- let timer = null;
208
- let pending = false;
209
-
210
- const stop = () => {
211
- stopped = true;
212
- if (timer) clearTimeout(timer);
213
- timer = null;
214
- };
215
-
216
- const scheduleNext = () => {
217
- if (stopped) return;
218
- timer = setTimeout(tick, intervalMs);
219
- if (typeof timer?.unref === "function") timer.unref();
220
- };
221
-
222
- const tick = async () => {
223
- if (stopped || pending) {
224
- scheduleNext();
225
- return;
226
- }
227
-
228
- pending = true;
229
- try {
230
- const result = await subscribeToLeadCommands({
231
- hubUrl: resolvedHubUrl,
232
- agentId: sessionAgentId,
233
- maxMessages,
234
- autoAck,
235
- timeoutMs,
236
- fetchImpl,
237
- onCommand: async (command) => {
238
- await dispatchHeadlessCommand(command, callbacks);
239
- },
240
- });
241
-
242
- if (!result?.ok && result?.error === "LEAD_COMMAND_SUBSCRIBE_FAILED") {
243
- stop();
244
- return;
245
- }
246
- } finally {
247
- pending = false;
248
- scheduleNext();
249
- }
250
- };
251
-
252
- void tick();
253
-
254
- return { stop };
255
- }
256
-
257
- export async function getTeamStatus({
258
- hubUrl,
259
- scope = "hub",
260
- agentId,
261
- includeMetrics = true,
262
- method = "GET",
263
- timeoutMs = DEFAULT_TIMEOUT_MS,
264
- fetchImpl,
265
- } = {}) {
266
- const requestFetch = resolveFetch(fetchImpl);
267
- if (!requestFetch) {
268
- return { ok: false, error: "FETCH_UNAVAILABLE" };
269
- }
270
-
271
- const hubBase = normalizeHubBaseUrl(hubUrl);
272
- if (!hubBase) {
273
- return { ok: false, error: "HUB_URL_REQUIRED" };
274
- }
275
-
276
- const normalizedMethod =
277
- String(method || "GET").toUpperCase() === "POST" ? "POST" : "GET";
278
- const statusScope = String(scope || "hub").trim() || "hub";
279
-
280
- let endpoint = `${hubBase}/bridge/status`;
281
- const options = {
282
- method: normalizedMethod,
283
- headers: { "Content-Type": "application/json" },
284
- signal: safeAbortSignal(timeoutMs),
285
- };
286
-
287
- if (normalizedMethod === "GET") {
288
- const params = new URLSearchParams({ scope: statusScope });
289
- if (agentId) params.set("agent_id", String(agentId));
290
- if (!includeMetrics) params.set("include_metrics", "0");
291
- endpoint = `${endpoint}?${params.toString()}`;
292
- } else {
293
- options.body = JSON.stringify({
294
- scope: statusScope,
295
- agent_id: agentId || undefined,
296
- include_metrics: includeMetrics,
297
- });
298
- }
299
-
300
- try {
301
- const res = await requestFetch(endpoint, options);
302
- const body = await safeJson(res);
303
- return {
304
- ok: res.ok && body?.ok !== false,
305
- status: res.status,
306
- body,
307
- data: body?.data || null,
308
- };
309
- } catch (error) {
310
- return {
311
- ok: false,
312
- error: "TEAM_STATUS_FETCH_FAILED",
313
- message: error?.message || "team status fetch failed",
314
- };
315
- }
316
- }