@aexol/spectral 0.9.210 → 0.9.212
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/dist/agent/agents.d.ts.map +1 -1
- package/dist/agent/agents.js +40 -0
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +24 -4
- package/dist/extensions/browser/tools/console.d.ts.map +1 -1
- package/dist/extensions/browser/tools/console.js +22 -2
- package/dist/extensions/browser/tools/evaluate.d.ts.map +1 -1
- package/dist/extensions/browser/tools/evaluate.js +28 -4
- package/dist/extensions/browser/tools/network.d.ts.map +1 -1
- package/dist/extensions/browser/tools/network.js +22 -2
- package/dist/extensions/seo/tools/fetch.d.ts.map +1 -1
- package/dist/extensions/seo/tools/fetch.js +59 -8
- package/dist/extensions/web/tools/fetch.d.ts.map +1 -1
- package/dist/extensions/web/tools/fetch.js +28 -15
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +27 -0
- package/dist/generated/zeus/index.d.ts +122 -0
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/memory/tool-output-compressor.d.ts.map +1 -1
- package/dist/memory/tool-output-compressor.js +53 -2
- package/dist/relay/client.d.ts +11 -0
- package/dist/relay/client.d.ts.map +1 -1
- package/dist/relay/client.js +24 -6
- package/dist/relay/history-projection.d.ts.map +1 -1
- package/dist/relay/history-projection.js +7 -0
- package/dist/sdk/coding-agent/core/tools/offload.d.ts +102 -0
- package/dist/sdk/coding-agent/core/tools/offload.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/tools/offload.js +207 -0
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +21 -0
- package/dist/server/offload-gc.d.ts +21 -0
- package/dist/server/offload-gc.d.ts.map +1 -0
- package/dist/server/offload-gc.js +111 -0
- package/dist/server/session-stream.d.ts +2 -0
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +166 -9
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/agent/agents.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AACrD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAC/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/agent/agents.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AACrD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAC/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AA6ED,eAAO,MAAM,aAAa,EAAE,WAAW,EAkDtC,CAAC;AA0DF,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3E;AACD;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,yEAAyE;IACzE,YAAY,EAAE,IAAI,CAAC;IACnB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG;IACF,YAAY,EAAE,KAAK,CAAC;IACpB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA6EnC;AAkBD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,oBAAoB,CAmBtB;AACD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,UAAmB,EAC1B,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,WAAW,GAAG,IAAI,CAepB;AACD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACvC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,WAAW,GAAG,IAAI,CAgBpB;AACD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,YAAY,EAAE,IAAI,CAChB,WAAW,EACX,MAAM,GAAG,aAAa,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,CAC7D,GAAG,IAAI,GACP,MAAM,GAAG,SAAS,CAQpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,CACT,WAAW,EACX,MAAM,GAAG,aAAa,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,CAC7D,GAAG,IAAI,GACP,MAAM,CAgBR"}
|
package/dist/agent/agents.js
CHANGED
|
@@ -29,6 +29,44 @@ const GHOST_SYSTEM_PROMPT = [
|
|
|
29
29
|
"3. Integrate: combine subagent reports into one coherent outcome. Resolve gaps or conflicts by sending follow-up `subagent` tasks — never by doing the work yourself.",
|
|
30
30
|
"4. Verify: after every implementation phase, delegate a `reviewer` pass before declaring the work done.",
|
|
31
31
|
"",
|
|
32
|
+
"## Wave orchestration",
|
|
33
|
+
"",
|
|
34
|
+
"For non-trivial work, organize delegation into waves. A wave is one `subagent`",
|
|
35
|
+
"parallel call (a set of independent tasks) plus a barrier: you wait for all",
|
|
36
|
+
"reports, verify each against its done-criteria, write a short distillate",
|
|
37
|
+
"(~10-15 lines: decisions, key file paths, what was rejected and why,",
|
|
38
|
+
"distillate kept in session context — no PLAN.md file), then",
|
|
39
|
+
"decide continue / re-plan / stop before launching the next wave.",
|
|
40
|
+
"",
|
|
41
|
+
"Routing — waves vs single wave vs sequence:",
|
|
42
|
+
"- Independent tasks with no dependencies → one wave (single `subagent` parallel call).",
|
|
43
|
+
"- Tasks depending on other tasks' results → separate, later waves (topological",
|
|
44
|
+
" order by dependsOn). Never put dependent tasks in the same wave.",
|
|
45
|
+
"- A strict 1:1 dependency inside one logical step → `chain` with {previous},",
|
|
46
|
+
" not a one-task wave.",
|
|
47
|
+
"- More than ~8 parallel tasks, or fully independent fire-and-forget research →",
|
|
48
|
+
" split into smaller waves; `session_fanout` stays fire-and-forget instead of",
|
|
49
|
+
" `subagent` parallel (read worker reports yourself, results are not returned).",
|
|
50
|
+
"",
|
|
51
|
+
"Typical shapes:",
|
|
52
|
+
"- research-wave → implement-wave → review-wave (standard feature/fix flow).",
|
|
53
|
+
"- research-wave (wide, 3-6 researchers) → implement-wave (1-3 executors on",
|
|
54
|
+
" disjoint scopes) → review-wave (reviewer + fix executor if needed).",
|
|
55
|
+
"- Single research-wave → direct answer (read-only questions need no more waves).",
|
|
56
|
+
"",
|
|
57
|
+
"Per-task prompt shape: goal (one action) + scope (dirs/files/commands, explicit",
|
|
58
|
+
"exclusions) + facts the task needs (paths, line numbers from prior distillates)",
|
|
59
|
+
"+ expected report shape (done-criteria: Status/Dowody/Luki text block, no JSON).",
|
|
60
|
+
"Keep it under ~40 lines. Between waves pass only distillates and selected facts —",
|
|
61
|
+
"never full transcripts.",
|
|
62
|
+
"",
|
|
63
|
+
"Barrier checklist: all reports back → each verified against done-criteria →",
|
|
64
|
+
"contradictions resolved (follow-up task if needed) → distillate written",
|
|
65
|
+
"(kept in session context, compressed to 3-5 lines when older than 2 waves) →",
|
|
66
|
+
"continue (next wave from ready tasks) / re-plan (wave failed: rewrite wave list,",
|
|
67
|
+
"keep old distillates) / stop (goal met or provably unachievable — report why).",
|
|
68
|
+
"There is no wave-level timeout — only the per-subagent timeout applies.",
|
|
69
|
+
"",
|
|
32
70
|
"## Anti-patterns — never do these",
|
|
33
71
|
"",
|
|
34
72
|
"- Do not explore the codebase yourself (no broad reads or searches) — that is `researcher`'s job.",
|
|
@@ -36,6 +74,8 @@ const GHOST_SYSTEM_PROMPT = [
|
|
|
36
74
|
"- Do not skip the `reviewer` pass after implementation.",
|
|
37
75
|
"- Do not treat delegation as optional: a session with zero `subagent` calls on a non-trivial task is a failed session.",
|
|
38
76
|
"- Do not claim completion without subagent evidence (files changed, commands run, test results).",
|
|
77
|
+
"- Do not put dependent tasks in the same wave, and do not start the next wave",
|
|
78
|
+
" before the barrier (distillate + stop/continue decision) is done.",
|
|
39
79
|
].join("\n");
|
|
40
80
|
export const SYSTEM_AGENTS = [
|
|
41
81
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAwBH,OAAO,EAAE,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AAYlE,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAwBH,OAAO,EAAE,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AAYlE,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAarE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAazD;AAED,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA6B9D;AAED,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,oBAAoB,CAAC;IAC9B,4CAA4C;IAC5C,KAAK,EAAE,WAAW,CAAC;IACnB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0jBlF;AA+DD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAc5E"}
|
package/dist/commands/serve.js
CHANGED
|
@@ -48,6 +48,7 @@ import { gracefulShutdown } from "../server/shutdown.js";
|
|
|
48
48
|
import { getLiveMcpState } from "../mcp/state-getter.js";
|
|
49
49
|
import { preflightSqlite, SessionStore } from "../server/storage.js";
|
|
50
50
|
import { DevProcessRegistry } from "../server/dev-process-registry.js";
|
|
51
|
+
import { sweepOffloads } from "../server/offload-gc.js";
|
|
51
52
|
/** Default backend base URL when neither env nor override is set. */
|
|
52
53
|
const DEFAULT_BACKEND_URL = "https://api.aexol.ai";
|
|
53
54
|
import { getVersion } from "../version.js";
|
|
@@ -133,9 +134,21 @@ export async function runServe(opts = {}) {
|
|
|
133
134
|
process.stderr.write(`✗ ${preflight.error}\n`);
|
|
134
135
|
process.exit(1);
|
|
135
136
|
}
|
|
137
|
+
// Pre-flight: sweep stale large-result offload files (fire-and-forget).
|
|
136
138
|
const store = new SessionStore(dbPath);
|
|
137
139
|
const devProcessRegistry = new DevProcessRegistry(getAgentDir());
|
|
138
140
|
devProcessRegistry.reconcile();
|
|
141
|
+
// GC for stale large-result offload files. Deferred via setImmediate so it
|
|
142
|
+
// never blocks server startup; sweepOffloads() never throws, but the guard
|
|
143
|
+
// stays so a failed sweep can never take down serving.
|
|
144
|
+
setImmediate(() => {
|
|
145
|
+
try {
|
|
146
|
+
sweepOffloads();
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Best-effort.
|
|
150
|
+
}
|
|
151
|
+
});
|
|
139
152
|
// Mutable holder for the meta-event publisher. Bound below once we
|
|
140
153
|
// have both `relay` (the send target) and `registration.record.machineId`
|
|
141
154
|
// (the canonical machineId the backend uses to fan out). The manager
|
|
@@ -427,11 +440,18 @@ export async function runServe(opts = {}) {
|
|
|
427
440
|
r.on("reconnect-scheduled", ({ delayMs, attempt }) => {
|
|
428
441
|
process.stdout.write(`Reconnect attempt ${attempt} in ${delayMs}ms\n`);
|
|
429
442
|
});
|
|
430
|
-
r.on("error", (err) => {
|
|
431
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
432
|
-
process.stderr.write(`Relay error: ${msg}\n`);
|
|
433
|
-
});
|
|
434
443
|
}
|
|
444
|
+
// The `error` listener must be registered regardless of `silent`:
|
|
445
|
+
// without it, a transport hiccup in silent mode would surface as an
|
|
446
|
+
// unhandled 'error' event and kill the whole serve process. (RelayClient
|
|
447
|
+
// itself also guards this now, but the explicit listener keeps the
|
|
448
|
+
// stderr output contract for non-silent runs.)
|
|
449
|
+
r.on("error", (err) => {
|
|
450
|
+
if (silent)
|
|
451
|
+
return;
|
|
452
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
453
|
+
process.stderr.write(`Relay error: ${msg}\n`);
|
|
454
|
+
});
|
|
435
455
|
// When the relay reports an auth failure (HTTP 401/403 on WS upgrade),
|
|
436
456
|
// the machine JWT is invalid. Attempt re-registration with the original
|
|
437
457
|
// credentials to obtain a fresh JWT, then restart the relay. If
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/tools/console.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/tools/console.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIvD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CA2EhF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Console messages tool: browser_console_messages.
|
|
3
3
|
*/
|
|
4
|
+
import { offloadLargeResult } from "../../../sdk/coding-agent/core/tools/offload.js";
|
|
4
5
|
export function registerConsoleTools(ext, bs) {
|
|
5
6
|
const consoleTool = {
|
|
6
7
|
name: "browser_console_messages",
|
|
@@ -30,12 +31,31 @@ export function registerConsoleTools(ext, bs) {
|
|
|
30
31
|
}
|
|
31
32
|
const lines = messages.map((m) => `[${m.type.padEnd(7)}] ${m.text.slice(0, 300)}`);
|
|
32
33
|
const text = lines.join("\n");
|
|
34
|
+
// Small console dumps pass through untouched; large ones become
|
|
35
|
+
// preview + offload footer (fallback keeps the legacy cap).
|
|
36
|
+
let displayText = text;
|
|
37
|
+
let fullOutputPath;
|
|
38
|
+
let offloadTruncation;
|
|
39
|
+
try {
|
|
40
|
+
const offloaded = offloadLargeResult(text, "browser_console_messages", "shared");
|
|
41
|
+
displayText = offloaded.preview;
|
|
42
|
+
if (offloaded.fullOutputPath && offloaded.truncation) {
|
|
43
|
+
fullOutputPath = offloaded.fullOutputPath;
|
|
44
|
+
offloadTruncation = offloaded.truncation;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
displayText = text.length > 20_000 ? text.slice(0, 20_000) + "\n\n[truncated]" : text;
|
|
49
|
+
}
|
|
33
50
|
return {
|
|
34
51
|
content: [{
|
|
35
52
|
type: "text",
|
|
36
|
-
text:
|
|
53
|
+
text: displayText,
|
|
37
54
|
}],
|
|
38
|
-
details: {
|
|
55
|
+
details: {
|
|
56
|
+
...(fullOutputPath ? { fullOutputPath } : {}),
|
|
57
|
+
...(offloadTruncation ? { offloadTruncation } : {}),
|
|
58
|
+
},
|
|
39
59
|
};
|
|
40
60
|
}
|
|
41
61
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/tools/evaluate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/tools/evaluate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAuBvD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAkHjF"}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Evaluate tools: browser_evaluate, browser_run_code_unsafe.
|
|
3
3
|
*/
|
|
4
|
+
import { offloadLargeResult, } from "../../../sdk/coding-agent/core/tools/offload.js";
|
|
5
|
+
function offloadBrowserText(text, toolName) {
|
|
6
|
+
try {
|
|
7
|
+
const offloaded = offloadLargeResult(text, toolName, "shared");
|
|
8
|
+
if (!offloaded.fullOutputPath || !offloaded.truncation)
|
|
9
|
+
return { text };
|
|
10
|
+
return {
|
|
11
|
+
text: offloaded.preview,
|
|
12
|
+
fullOutputPath: offloaded.fullOutputPath,
|
|
13
|
+
offloadTruncation: offloaded.truncation,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return { text: text.length > 50000 ? text.slice(0, 50000) + "\n\n[truncated]" : text };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
4
20
|
export function registerEvaluateTools(ext, bs) {
|
|
5
21
|
const evaluateTool = {
|
|
6
22
|
name: "browser_evaluate",
|
|
@@ -34,12 +50,16 @@ export function registerEvaluateTools(ext, bs) {
|
|
|
34
50
|
const text = typeof result === "string"
|
|
35
51
|
? result
|
|
36
52
|
: JSON.stringify(result, null, 2);
|
|
53
|
+
const offloaded = offloadBrowserText(text, "browser_evaluate");
|
|
37
54
|
return {
|
|
38
55
|
content: [{
|
|
39
56
|
type: "text",
|
|
40
|
-
text:
|
|
57
|
+
text: offloaded.text,
|
|
41
58
|
}],
|
|
42
|
-
details: {
|
|
59
|
+
details: {
|
|
60
|
+
...(offloaded.fullOutputPath ? { fullOutputPath: offloaded.fullOutputPath } : {}),
|
|
61
|
+
...(offloaded.offloadTruncation ? { offloadTruncation: offloaded.offloadTruncation } : {}),
|
|
62
|
+
},
|
|
43
63
|
};
|
|
44
64
|
}
|
|
45
65
|
catch (err) {
|
|
@@ -79,12 +99,16 @@ export function registerEvaluateTools(ext, bs) {
|
|
|
79
99
|
const text = typeof result === "string"
|
|
80
100
|
? result
|
|
81
101
|
: JSON.stringify(result, null, 2);
|
|
102
|
+
const offloaded = offloadBrowserText(text, "browser_run_code_unsafe");
|
|
82
103
|
return {
|
|
83
104
|
content: [{
|
|
84
105
|
type: "text",
|
|
85
|
-
text:
|
|
106
|
+
text: offloaded.text,
|
|
86
107
|
}],
|
|
87
|
-
details: {
|
|
108
|
+
details: {
|
|
109
|
+
...(offloaded.fullOutputPath ? { fullOutputPath: offloaded.fullOutputPath } : {}),
|
|
110
|
+
...(offloaded.offloadTruncation ? { offloadTruncation: offloaded.offloadTruncation } : {}),
|
|
111
|
+
},
|
|
88
112
|
};
|
|
89
113
|
}
|
|
90
114
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/tools/network.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/tools/network.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAwBvD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CA4HhF"}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Network tools: browser_network_requests, browser_network_request.
|
|
3
3
|
*/
|
|
4
|
+
import { offloadLargeResult, } from "../../../sdk/coding-agent/core/tools/offload.js";
|
|
5
|
+
function offloadBrowserText(text, toolName, legacyLimit) {
|
|
6
|
+
try {
|
|
7
|
+
const offloaded = offloadLargeResult(text, toolName, "shared");
|
|
8
|
+
if (!offloaded.fullOutputPath || !offloaded.truncation)
|
|
9
|
+
return { text };
|
|
10
|
+
return {
|
|
11
|
+
text: offloaded.preview,
|
|
12
|
+
fullOutputPath: offloaded.fullOutputPath,
|
|
13
|
+
offloadTruncation: offloaded.truncation,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return { text: text.length > legacyLimit ? text.slice(0, legacyLimit) + "\n\n[truncated]" : text };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
4
20
|
export function registerNetworkTools(ext, bs) {
|
|
5
21
|
const networkRequestsTool = {
|
|
6
22
|
name: "browser_network_requests",
|
|
@@ -90,12 +106,16 @@ export function registerNetworkTools(ext, bs) {
|
|
|
90
106
|
}
|
|
91
107
|
}
|
|
92
108
|
const text = lines.join("\n");
|
|
109
|
+
const offloaded = offloadBrowserText(text, "browser_network_request", 50_000);
|
|
93
110
|
return {
|
|
94
111
|
content: [{
|
|
95
112
|
type: "text",
|
|
96
|
-
text:
|
|
113
|
+
text: offloaded.text,
|
|
97
114
|
}],
|
|
98
|
-
details: {
|
|
115
|
+
details: {
|
|
116
|
+
...(offloaded.fullOutputPath ? { fullOutputPath: offloaded.fullOutputPath } : {}),
|
|
117
|
+
...(offloaded.offloadTruncation ? { offloadTruncation: offloaded.offloadTruncation } : {}),
|
|
118
|
+
},
|
|
99
119
|
};
|
|
100
120
|
}
|
|
101
121
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/extensions/seo/tools/fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/extensions/seo/tools/fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAyC9C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAqK7E"}
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import { fetchPage } from "../utils/fetcher.js";
|
|
2
2
|
import { renderPage, isRendererAvailable } from "../utils/renderer.js";
|
|
3
|
-
import {
|
|
3
|
+
import { countLines, offloadLargeResult } from "../../../sdk/coding-agent/core/tools/offload.js";
|
|
4
|
+
import { errorResult, textResult } from "./helpers.js";
|
|
5
|
+
/**
|
|
6
|
+
* Offload one large text field via the shared offload module (shared
|
|
7
|
+
* thresholds, head+tail preview + footer, owner-only file permissions).
|
|
8
|
+
* `shown` is always counted from the actually displayed preview text.
|
|
9
|
+
*/
|
|
10
|
+
function tryOffloadSeoContent(content, toolName, sessionId) {
|
|
11
|
+
if (!content)
|
|
12
|
+
return { text: "" };
|
|
13
|
+
let offloaded;
|
|
14
|
+
try {
|
|
15
|
+
offloaded = offloadLargeResult(content, toolName, sessionId);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { text: content };
|
|
19
|
+
}
|
|
20
|
+
if (!offloaded.fullOutputPath || !offloaded.truncation) {
|
|
21
|
+
return { text: content };
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
text: offloaded.preview,
|
|
25
|
+
fullOutputPath: offloaded.fullOutputPath,
|
|
26
|
+
offloadTruncation: offloaded.truncation,
|
|
27
|
+
shownLines: countLines(offloaded.preview),
|
|
28
|
+
totalLines: countLines(content),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
4
31
|
export function registerFetchTools(ext, config) {
|
|
5
32
|
const fetchTool = {
|
|
6
33
|
name: "seo_fetch",
|
|
@@ -24,7 +51,7 @@ export function registerFetchTools(ext, config) {
|
|
|
24
51
|
},
|
|
25
52
|
required: ["url"],
|
|
26
53
|
},
|
|
27
|
-
async execute(_toolCallId, params) {
|
|
54
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
28
55
|
const { url, userAgent, timeout, followRedirects } = params;
|
|
29
56
|
try {
|
|
30
57
|
const result = await fetchPage(url, config, {
|
|
@@ -35,18 +62,26 @@ export function registerFetchTools(ext, config) {
|
|
|
35
62
|
if (result.error) {
|
|
36
63
|
return errorResult(`Fetch failed: ${result.error}`, { result });
|
|
37
64
|
}
|
|
65
|
+
const sessionId = ctx
|
|
66
|
+
?.sessionManager?.getSessionId?.() ?? "shared";
|
|
67
|
+
const offloaded = tryOffloadSeoContent(result.content, "seo_fetch", sessionId);
|
|
38
68
|
const summary = `✓ Fetched: ${result.finalUrl}\n` +
|
|
39
69
|
`Status: ${result.statusCode} ${result.statusText}\n` +
|
|
40
70
|
`Redirects: ${result.redirectChain.length}\n` +
|
|
41
71
|
`Content-Length: ${result.contentLengthBytes} bytes\n` +
|
|
42
|
-
`Headers: ${Object.keys(result.headers).join(", ")}
|
|
72
|
+
`Headers: ${Object.keys(result.headers).join(", ")}` +
|
|
73
|
+
(offloaded.fullOutputPath ? `\nFull output: ${offloaded.fullOutputPath}` : "");
|
|
43
74
|
return textResult(summary, {
|
|
44
75
|
url: result.url,
|
|
45
76
|
finalUrl: result.finalUrl,
|
|
46
77
|
statusCode: result.statusCode,
|
|
47
|
-
content:
|
|
78
|
+
content: offloaded.text,
|
|
48
79
|
headers: result.headers,
|
|
49
80
|
redirectChain: result.redirectChain,
|
|
81
|
+
...(offloaded.fullOutputPath ? { fullOutputPath: offloaded.fullOutputPath } : {}),
|
|
82
|
+
...(offloaded.offloadTruncation ? { offloadTruncation: offloaded.offloadTruncation } : {}),
|
|
83
|
+
...(offloaded.shownLines !== undefined ? { shownLines: offloaded.shownLines } : {}),
|
|
84
|
+
...(offloaded.totalLines !== undefined ? { totalLines: offloaded.totalLines } : {}),
|
|
50
85
|
});
|
|
51
86
|
}
|
|
52
87
|
catch (err) {
|
|
@@ -83,7 +118,7 @@ export function registerFetchTools(ext, config) {
|
|
|
83
118
|
},
|
|
84
119
|
required: ["url"],
|
|
85
120
|
},
|
|
86
|
-
async execute(_toolCallId, params) {
|
|
121
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
87
122
|
const { url, viewport, captureA11y, captureConsole, timeout } = params;
|
|
88
123
|
const available = await isRendererAvailable();
|
|
89
124
|
if (!available) {
|
|
@@ -96,21 +131,37 @@ export function registerFetchTools(ext, config) {
|
|
|
96
131
|
captureConsole,
|
|
97
132
|
timeout,
|
|
98
133
|
});
|
|
134
|
+
const sessionId = ctx
|
|
135
|
+
?.sessionManager?.getSessionId?.() ?? "shared";
|
|
136
|
+
const contentOffload = tryOffloadSeoContent(result.content, "seo_render", sessionId);
|
|
137
|
+
const rawOffload = tryOffloadSeoContent(result.rawContent, "seo_render", sessionId);
|
|
138
|
+
const offloadNote = contentOffload.fullOutputPath || rawOffload.fullOutputPath
|
|
139
|
+
? `\nFull output: ${[contentOffload.fullOutputPath, rawOffload.fullOutputPath].filter(Boolean).join(", ")}`
|
|
140
|
+
: "";
|
|
99
141
|
const summary = `✓ Rendered: ${result.finalUrl}\n` +
|
|
100
142
|
`Status: ${result.statusCode}\n` +
|
|
101
143
|
`SPA: ${result.isSpa}\n` +
|
|
102
144
|
`Render time: ${result.renderMs}ms\n` +
|
|
103
|
-
`Console errors: ${result.consoleErrors.length}
|
|
145
|
+
`Console errors: ${result.consoleErrors.length}` + offloadNote;
|
|
146
|
+
// Keep BOTH offload paths — content and rawContent are independent
|
|
147
|
+
// fields and each may need re-reading.
|
|
148
|
+
const fullOutputPaths = [contentOffload.fullOutputPath, rawOffload.fullOutputPath].filter((p) => Boolean(p));
|
|
104
149
|
return textResult(summary, {
|
|
105
150
|
url: result.url,
|
|
106
151
|
finalUrl: result.finalUrl,
|
|
107
152
|
statusCode: result.statusCode,
|
|
108
|
-
content:
|
|
109
|
-
rawContent:
|
|
153
|
+
content: contentOffload.text,
|
|
154
|
+
rawContent: rawOffload.text,
|
|
110
155
|
isSpa: result.isSpa,
|
|
111
156
|
consoleErrors: result.consoleErrors,
|
|
112
157
|
renderMs: result.renderMs,
|
|
113
158
|
accessibilityTree: result.accessibilityTree,
|
|
159
|
+
...(fullOutputPaths.length > 0
|
|
160
|
+
? { fullOutputPath: fullOutputPaths[0], fullOutputPaths }
|
|
161
|
+
: {}),
|
|
162
|
+
...(contentOffload.offloadTruncation
|
|
163
|
+
? { offloadTruncation: contentOffload.offloadTruncation }
|
|
164
|
+
: {}),
|
|
114
165
|
});
|
|
115
166
|
}
|
|
116
167
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/extensions/web/tools/fetch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmB,YAAY,EAAkB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../../src/extensions/web/tools/fetch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmB,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AAgCxG,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAwE5D"}
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* web_fetch tool definition.
|
|
3
3
|
*/
|
|
4
4
|
import { Type } from "typebox";
|
|
5
|
-
import {
|
|
5
|
+
import { isAlreadyOffloaded, offloadLargeResult, } from "../../../sdk/coding-agent/core/tools/offload.js";
|
|
6
6
|
import { fetchWebPage, WebFetchError } from "../utils/fetcher.js";
|
|
7
7
|
import { convertContent } from "../utils/parser.js";
|
|
8
|
-
const MAX_OUTPUT_LINES = 4000;
|
|
9
|
-
const MAX_OUTPUT_BYTES = 200 * 1024; // 200 KB
|
|
10
8
|
const webFetchSchema = Type.Object({
|
|
11
9
|
url: Type.String({
|
|
12
10
|
description: "The URL to fetch. Must begin with http:// or https://.",
|
|
@@ -32,7 +30,7 @@ export function registerWebFetchTool(ext) {
|
|
|
32
30
|
"Supports HTTP/HTTPS only and rejects binary content.",
|
|
33
31
|
promptSnippet: "`web_fetch { url, format?, timeout? }` — fetch a web page as markdown/text/HTML/JSON",
|
|
34
32
|
parameters: webFetchSchema,
|
|
35
|
-
async execute(_toolCallId, params, signal, _onUpdate,
|
|
33
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
36
34
|
const { url, format = "markdown", timeout = 30 } = params;
|
|
37
35
|
try {
|
|
38
36
|
const fetchResult = await fetchWebPage(url, {
|
|
@@ -40,19 +38,33 @@ export function registerWebFetchTool(ext) {
|
|
|
40
38
|
signal,
|
|
41
39
|
});
|
|
42
40
|
const converted = await convertContent(fetchResult.content, format);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
// Single truncation system: offloadLargeResult decides by its own
|
|
42
|
+
// byte/line thresholds and builds the head+tail preview + footer.
|
|
43
|
+
// The full content is passed directly — no pre-truncation here.
|
|
44
|
+
let fullOutputPath;
|
|
45
|
+
let output = converted;
|
|
46
|
+
let offloadTruncation;
|
|
47
|
+
if (!isAlreadyOffloaded(converted)) {
|
|
48
|
+
try {
|
|
49
|
+
const sessionId = ctx?.sessionManager?.getSessionId?.() ?? "shared";
|
|
50
|
+
const offloaded = offloadLargeResult(converted, "web_fetch", sessionId);
|
|
51
|
+
output = offloaded.preview;
|
|
52
|
+
if (offloaded.fullOutputPath) {
|
|
53
|
+
fullOutputPath = offloaded.fullOutputPath;
|
|
54
|
+
offloadTruncation = offloaded.truncation ?? undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Best-effort — serve the full content when offload fails.
|
|
59
|
+
output = converted;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const truncated = fullOutputPath !== undefined;
|
|
51
63
|
return {
|
|
52
64
|
content: [
|
|
53
65
|
{
|
|
54
66
|
type: "text",
|
|
55
|
-
text: `✓ Fetched ${fetchResult.finalUrl}\nContent-Type: ${fetchResult.contentType || "unknown"}\nFormat: ${format}\nSize: ${fetchResult.contentLengthBytes} bytes
|
|
67
|
+
text: `✓ Fetched ${fetchResult.finalUrl}\nContent-Type: ${fetchResult.contentType || "unknown"}\nFormat: ${format}\nSize: ${fetchResult.contentLengthBytes} bytes\n\n${output}`,
|
|
56
68
|
},
|
|
57
69
|
],
|
|
58
70
|
details: {
|
|
@@ -61,8 +73,9 @@ export function registerWebFetchTool(ext) {
|
|
|
61
73
|
contentType: fetchResult.contentType,
|
|
62
74
|
format,
|
|
63
75
|
output,
|
|
64
|
-
truncated
|
|
65
|
-
|
|
76
|
+
truncated,
|
|
77
|
+
...(fullOutputPath ? { fullOutputPath } : {}),
|
|
78
|
+
...(offloadTruncation ? { offloadTruncation } : {}),
|
|
66
79
|
},
|
|
67
80
|
};
|
|
68
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../../src/generated/zeus/const.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"const.d.ts","sourceRoot":"","sources":["../../../src/generated/zeus/const.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAC,GAAG,CAyyB5C,CAAA;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAC,GAAG,CA4nC1C,CAAA;AAED,eAAO,MAAM,GAAG;;;;CAIf,CAAA"}
|
|
@@ -194,6 +194,8 @@ export const AllTypesProps = {
|
|
|
194
194
|
adminBillingPlanCount: {},
|
|
195
195
|
adminCreditPacks: {},
|
|
196
196
|
adminCreditPackCount: {},
|
|
197
|
+
adminTeamPayments: {},
|
|
198
|
+
adminTeamPaymentCount: {},
|
|
197
199
|
adminLeads: {
|
|
198
200
|
status: "LeadStatus",
|
|
199
201
|
leadType: "LeadType",
|
|
@@ -677,6 +679,14 @@ export const ReturnTypes = {
|
|
|
677
679
|
usage: "TokenUsage",
|
|
678
680
|
taskCount: "Int"
|
|
679
681
|
},
|
|
682
|
+
DailyUsageByUserEntry: {
|
|
683
|
+
date: "DateTime",
|
|
684
|
+
userId: "ID",
|
|
685
|
+
userEmail: "String",
|
|
686
|
+
userName: "String",
|
|
687
|
+
usage: "TokenUsage",
|
|
688
|
+
taskCount: "Int"
|
|
689
|
+
},
|
|
680
690
|
ProjectBreakdownEntry: {
|
|
681
691
|
project: "Project",
|
|
682
692
|
usage: "TokenUsage",
|
|
@@ -1113,6 +1123,8 @@ export const ReturnTypes = {
|
|
|
1113
1123
|
adminBillingPlanCount: "Int",
|
|
1114
1124
|
adminCreditPacks: "AdminCreditPack",
|
|
1115
1125
|
adminCreditPackCount: "Int",
|
|
1126
|
+
adminTeamPayments: "AdminTeamPayment",
|
|
1127
|
+
adminTeamPaymentCount: "Int",
|
|
1116
1128
|
adminLeads: "Lead",
|
|
1117
1129
|
adminLeadCount: "Int",
|
|
1118
1130
|
adminLead: "Lead",
|
|
@@ -1513,8 +1525,23 @@ export const ReturnTypes = {
|
|
|
1513
1525
|
costUsd: "Float",
|
|
1514
1526
|
dailyBreakdown: "DailyUsageEntry",
|
|
1515
1527
|
dailyBreakdownByModel: "DailyUsageByModelEntry",
|
|
1528
|
+
dailyBreakdownByUser: "DailyUsageByUserEntry",
|
|
1516
1529
|
createdAt: "DateTime"
|
|
1517
1530
|
},
|
|
1531
|
+
AdminTeamPayment: {
|
|
1532
|
+
id: "ID",
|
|
1533
|
+
description: "String",
|
|
1534
|
+
amountPaid: "Float",
|
|
1535
|
+
currency: "String",
|
|
1536
|
+
credits: "Float",
|
|
1537
|
+
status: "String",
|
|
1538
|
+
stripeCheckoutSessionId: "String",
|
|
1539
|
+
stripePaymentIntentId: "String",
|
|
1540
|
+
stripeInvoiceId: "String",
|
|
1541
|
+
createdAt: "DateTime",
|
|
1542
|
+
completedAt: "DateTime",
|
|
1543
|
+
expiresAt: "DateTime"
|
|
1544
|
+
},
|
|
1518
1545
|
AdminDashboardStats: {
|
|
1519
1546
|
totalTeams: "Int",
|
|
1520
1547
|
totalUsers: "Int",
|