@useorgx/openclaw-plugin 0.4.8 → 0.7.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/README.md +35 -0
- package/dashboard/dist/assets/BJgZIVUQ.js +53 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js +1 -0
- package/dashboard/dist/assets/BXWDRGm-.js.br +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js.gz +0 -0
- package/dashboard/dist/assets/BgOYB78t.js +4 -0
- package/dashboard/dist/assets/BgOYB78t.js.br +0 -0
- package/dashboard/dist/assets/BgOYB78t.js.gz +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
- package/dashboard/dist/assets/CE38zU4U.js +1 -0
- package/dashboard/dist/assets/CE38zU4U.js.br +0 -0
- package/dashboard/dist/assets/CE38zU4U.js.gz +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js +1 -0
- package/dashboard/dist/assets/CFGKRAzG.js.br +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js.gz +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js +1 -0
- package/dashboard/dist/assets/CGGR2GZh.js.br +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js.gz +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js +1 -0
- package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js +8 -0
- package/dashboard/dist/assets/CPFiTmlw.js.br +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js.gz +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js +1 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.br +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.gz +0 -0
- package/dashboard/dist/assets/{CpJsfbXo.js → CxQ08qFN.js} +2 -2
- package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
- package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js +213 -0
- package/dashboard/dist/assets/D-bf6hEI.js.br +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js.gz +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js +2 -0
- package/dashboard/dist/assets/DG6y9wJI.js.br +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js.gz +0 -0
- package/dashboard/dist/assets/DNxKz-GV.js +1 -0
- package/dashboard/dist/assets/DNxKz-GV.js.br +0 -0
- package/dashboard/dist/assets/DNxKz-GV.js.gz +0 -0
- package/dashboard/dist/assets/DW_rKUic.js +11 -0
- package/dashboard/dist/assets/DW_rKUic.js.br +0 -0
- package/dashboard/dist/assets/DW_rKUic.js.gz +0 -0
- package/dashboard/dist/assets/DbNoijHm.js +1 -0
- package/dashboard/dist/assets/DbNoijHm.js.br +0 -0
- package/dashboard/dist/assets/DbNoijHm.js.gz +0 -0
- package/dashboard/dist/assets/DjcdE6jC.js +2 -0
- package/dashboard/dist/assets/DjcdE6jC.js.br +0 -0
- package/dashboard/dist/assets/DjcdE6jC.js.gz +0 -0
- package/dashboard/dist/assets/FZYuCDnt.js +1 -0
- package/dashboard/dist/assets/FZYuCDnt.js.br +0 -0
- package/dashboard/dist/assets/FZYuCDnt.js.gz +0 -0
- package/dashboard/dist/assets/PAUiij_z.js +1 -0
- package/dashboard/dist/assets/PAUiij_z.js.br +0 -0
- package/dashboard/dist/assets/PAUiij_z.js.gz +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js +8 -0
- package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
- package/dashboard/dist/assets/h5biQs2I.css +1 -0
- package/dashboard/dist/assets/h5biQs2I.css.br +0 -0
- package/dashboard/dist/assets/h5biQs2I.css.gz +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js +1 -0
- package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
- package/dashboard/dist/assets/nByHNHoW.js +1 -0
- package/dashboard/dist/assets/nByHNHoW.js.br +0 -0
- package/dashboard/dist/assets/nByHNHoW.js.gz +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css +1 -0
- package/dashboard/dist/assets/qm8xLgv-.css.br +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css.gz +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js +1 -0
- package/dashboard/dist/assets/tS9mbYZi.js.br +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js.gz +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.br +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openai-mark.svg.br +0 -0
- package/dashboard/dist/brand/openai-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.br +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.gz +0 -0
- package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
- package/dashboard/dist/index.html +7 -5
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/activity-actor-fields.js +26 -4
- package/dist/activity-store.js +38 -26
- package/dist/agent-context-store.js +84 -42
- package/dist/agent-run-store.js +49 -28
- package/dist/agent-suite.d.ts +9 -0
- package/dist/agent-suite.js +150 -17
- package/dist/artifacts/artifact-domain-schemas.d.ts +66 -0
- package/dist/artifacts/artifact-domain-schemas.js +357 -0
- package/dist/artifacts/register-artifact.d.ts +4 -3
- package/dist/artifacts/register-artifact.js +170 -57
- package/dist/auth/flows.d.ts +47 -0
- package/dist/auth/flows.js +169 -0
- package/dist/auth-store.js +6 -26
- package/dist/byok-store.js +5 -19
- package/dist/chat-store.d.ts +157 -0
- package/dist/chat-store.js +586 -0
- package/dist/cli/orgx.d.ts +66 -0
- package/dist/cli/orgx.js +102 -0
- package/dist/config/refresh.d.ts +32 -0
- package/dist/config/refresh.js +55 -0
- package/dist/config/resolution.d.ts +37 -0
- package/dist/config/resolution.js +178 -0
- package/dist/contracts/client.d.ts +43 -3
- package/dist/contracts/client.js +159 -30
- package/dist/contracts/retro-schema.d.ts +81 -0
- package/dist/contracts/retro-schema.js +80 -0
- package/dist/contracts/shared-types.d.ts +306 -0
- package/dist/contracts/shared-types.js +179 -0
- package/dist/contracts/skill-pack-schema.d.ts +192 -0
- package/dist/contracts/skill-pack-schema.js +180 -0
- package/dist/contracts/types.d.ts +224 -132
- package/dist/contracts/types.js +5 -0
- package/dist/entities/auto-assignment.d.ts +36 -0
- package/dist/entities/auto-assignment.js +141 -0
- package/dist/entity-comment-store.js +5 -25
- package/dist/event-sanitization.d.ts +11 -0
- package/dist/event-sanitization.js +113 -0
- package/dist/fs-utils.js +13 -1
- package/dist/gateway-watchdog.d.ts +5 -0
- package/dist/gateway-watchdog.js +50 -0
- package/dist/hash-utils.d.ts +2 -0
- package/dist/hash-utils.js +12 -0
- package/dist/hooks/post-reporting-event.mjs +1 -5
- package/dist/http/helpers/activity-headline.d.ts +10 -0
- package/dist/http/helpers/activity-headline.js +73 -0
- package/dist/http/helpers/artifact-fallback.d.ts +13 -0
- package/dist/http/helpers/artifact-fallback.js +148 -0
- package/dist/http/helpers/auto-continue-engine.d.ts +486 -0
- package/dist/http/helpers/auto-continue-engine.js +3563 -0
- package/dist/http/helpers/autopilot-operations.d.ts +176 -0
- package/dist/http/helpers/autopilot-operations.js +554 -0
- package/dist/http/helpers/autopilot-runtime.d.ts +43 -0
- package/dist/http/helpers/autopilot-runtime.js +607 -0
- package/dist/http/helpers/autopilot-slice-utils.d.ts +56 -0
- package/dist/http/helpers/autopilot-slice-utils.js +899 -0
- package/dist/http/helpers/decision-mapper.d.ts +52 -0
- package/dist/http/helpers/decision-mapper.js +260 -0
- package/dist/http/helpers/dispatch-lifecycle.d.ts +119 -0
- package/dist/http/helpers/dispatch-lifecycle.js +809 -0
- package/dist/http/helpers/hash-utils.d.ts +1 -0
- package/dist/http/helpers/hash-utils.js +1 -0
- package/dist/http/helpers/kickoff-context.d.ts +12 -0
- package/dist/http/helpers/kickoff-context.js +228 -0
- package/dist/http/helpers/llm-client.d.ts +47 -0
- package/dist/http/helpers/llm-client.js +256 -0
- package/dist/http/helpers/mission-control.d.ts +193 -0
- package/dist/http/helpers/mission-control.js +1383 -0
- package/dist/http/helpers/openclaw-cli.d.ts +37 -0
- package/dist/http/helpers/openclaw-cli.js +283 -0
- package/dist/http/helpers/runtime-sse.d.ts +20 -0
- package/dist/http/helpers/runtime-sse.js +110 -0
- package/dist/http/helpers/sentinel-catalog.d.ts +23 -0
- package/dist/http/helpers/sentinel-catalog.js +193 -0
- package/dist/http/helpers/session-classification.d.ts +9 -0
- package/dist/http/helpers/session-classification.js +564 -0
- package/dist/http/helpers/slice-experience-v2.d.ts +137 -0
- package/dist/http/helpers/slice-experience-v2.js +677 -0
- package/dist/http/helpers/slice-run-projections.d.ts +72 -0
- package/dist/http/helpers/slice-run-projections.js +860 -0
- package/dist/http/helpers/triage-mapper.d.ts +43 -0
- package/dist/http/helpers/triage-mapper.js +549 -0
- package/dist/http/helpers/value-utils.d.ts +6 -0
- package/dist/http/helpers/value-utils.js +72 -0
- package/dist/http/helpers/workspace-scope.d.ts +15 -0
- package/dist/http/helpers/workspace-scope.js +170 -0
- package/dist/http/index.d.ts +88 -0
- package/dist/http/index.js +3610 -0
- package/dist/http/router.d.ts +23 -0
- package/dist/http/router.js +23 -0
- package/dist/http/routes/agent-control.d.ts +79 -0
- package/dist/http/routes/agent-control.js +684 -0
- package/dist/http/routes/agent-suite.d.ts +38 -0
- package/dist/http/routes/agent-suite.js +397 -0
- package/dist/http/routes/agents-catalog.d.ts +40 -0
- package/dist/http/routes/agents-catalog.js +128 -0
- package/dist/http/routes/billing.d.ts +23 -0
- package/dist/http/routes/billing.js +55 -0
- package/dist/http/routes/chat.d.ts +19 -0
- package/dist/http/routes/chat.js +522 -0
- package/dist/http/routes/debug.d.ts +14 -0
- package/dist/http/routes/debug.js +21 -0
- package/dist/http/routes/decision-actions.d.ts +20 -0
- package/dist/http/routes/decision-actions.js +103 -0
- package/dist/http/routes/delegation.d.ts +19 -0
- package/dist/http/routes/delegation.js +32 -0
- package/dist/http/routes/dispatch-gateway-envelope.d.ts +25 -0
- package/dist/http/routes/dispatch-gateway-envelope.js +26 -0
- package/dist/http/routes/entities.d.ts +63 -0
- package/dist/http/routes/entities.js +440 -0
- package/dist/http/routes/entity-dynamic.d.ts +25 -0
- package/dist/http/routes/entity-dynamic.js +191 -0
- package/dist/http/routes/health.d.ts +22 -0
- package/dist/http/routes/health.js +49 -0
- package/dist/http/routes/live-legacy.d.ts +115 -0
- package/dist/http/routes/live-legacy.js +112 -0
- package/dist/http/routes/live-misc.d.ts +81 -0
- package/dist/http/routes/live-misc.js +426 -0
- package/dist/http/routes/live-snapshot.d.ts +136 -0
- package/dist/http/routes/live-snapshot.js +916 -0
- package/dist/http/routes/live-terminal.d.ts +11 -0
- package/dist/http/routes/live-terminal.js +261 -0
- package/dist/http/routes/live-triage.d.ts +61 -0
- package/dist/http/routes/live-triage.js +248 -0
- package/dist/http/routes/mission-control-actions.d.ts +131 -0
- package/dist/http/routes/mission-control-actions.js +1791 -0
- package/dist/http/routes/mission-control-read.d.ts +73 -0
- package/dist/http/routes/mission-control-read.js +1640 -0
- package/dist/http/routes/onboarding.d.ts +34 -0
- package/dist/http/routes/onboarding.js +101 -0
- package/dist/http/routes/realtime-orchestrator.d.ts +10 -0
- package/dist/http/routes/realtime-orchestrator.js +74 -0
- package/dist/http/routes/run-control.d.ts +27 -0
- package/dist/http/routes/run-control.js +96 -0
- package/dist/http/routes/runtime-hooks.d.ts +69 -0
- package/dist/http/routes/runtime-hooks.js +437 -0
- package/dist/http/routes/sentinels-catalog.d.ts +7 -0
- package/dist/http/routes/sentinels-catalog.js +24 -0
- package/dist/http/routes/settings-byok.d.ts +23 -0
- package/dist/http/routes/settings-byok.js +163 -0
- package/dist/http/routes/summary.d.ts +18 -0
- package/dist/http/routes/summary.js +49 -0
- package/dist/http/routes/usage.d.ts +24 -0
- package/dist/http/routes/usage.js +362 -0
- package/dist/http/routes/work-artifacts.d.ts +9 -0
- package/dist/http/routes/work-artifacts.js +55 -0
- package/dist/http/shared-state.d.ts +16 -0
- package/dist/http/shared-state.js +1 -0
- package/dist/http-handler.d.ts +1 -88
- package/dist/http-handler.js +1 -10605
- package/dist/index.js +287 -2284
- package/dist/json-utils.d.ts +1 -0
- package/dist/json-utils.js +8 -0
- package/dist/local-openclaw.js +29 -6
- package/dist/mcp-client-setup.js +3 -3
- package/dist/mcp-http-handler.js +33 -59
- package/dist/next-up-queue-store.d.ts +16 -1
- package/dist/next-up-queue-store.js +93 -25
- package/dist/outbox.d.ts +5 -0
- package/dist/outbox.js +113 -9
- package/dist/paths.js +24 -5
- package/dist/reporting/rollups.d.ts +53 -0
- package/dist/reporting/rollups.js +148 -0
- package/dist/retro/domain-templates.d.ts +45 -0
- package/dist/retro/domain-templates.js +297 -0
- package/dist/retro/quality-rubric.d.ts +33 -0
- package/dist/retro/quality-rubric.js +213 -0
- package/dist/runtime-cleanup.d.ts +18 -0
- package/dist/runtime-cleanup.js +87 -0
- package/dist/runtime-instance-store.js +5 -31
- package/dist/services/background.d.ts +34 -0
- package/dist/services/background.js +45 -0
- package/dist/services/experiment-randomization.d.ts +21 -0
- package/dist/services/experiment-randomization.js +63 -0
- package/dist/services/instrumentation.d.ts +29 -0
- package/dist/services/instrumentation.js +136 -0
- package/dist/skill-pack-state.d.ts +36 -5
- package/dist/skill-pack-state.js +273 -29
- package/dist/snapshot-store.js +5 -25
- package/dist/stores/json-store.d.ts +11 -0
- package/dist/stores/json-store.js +42 -0
- package/dist/sync/local-agent-telemetry.d.ts +13 -0
- package/dist/sync/local-agent-telemetry.js +128 -0
- package/dist/sync/outbox-replay.d.ts +55 -0
- package/dist/sync/outbox-replay.js +621 -0
- package/dist/team-context-store.d.ts +23 -0
- package/dist/team-context-store.js +116 -0
- package/dist/telemetry/posthog.js +4 -2
- package/dist/tools/core-tools.d.ts +72 -0
- package/dist/tools/core-tools.js +2270 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -0
- package/dist/worker-supervisor.js +23 -0
- package/package.json +14 -4
- package/dashboard/dist/assets/B3ziCA02.js +0 -8
- package/dashboard/dist/assets/BNeJ0kpF.js +0 -1
- package/dashboard/dist/assets/BzkiMPmM.js +0 -215
- package/dashboard/dist/assets/CUV9IHHi.js +0 -1
- package/dashboard/dist/assets/Ie7d9Iq2.css +0 -1
- package/dashboard/dist/assets/sAhvFnpk.js +0 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { idempotencyKey, stableHash } from "../../hash-utils.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { idempotencyKey, stableHash } from "../../hash-utils.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OrgXClient } from "../../api.js";
|
|
2
|
+
import type { KickoffContext, KickoffContextRequest } from "../../types.js";
|
|
3
|
+
export declare function fetchKickoffContextSafe(client: OrgXClient, payload: KickoffContextRequest): Promise<KickoffContext | null>;
|
|
4
|
+
export declare function renderKickoffMessage(input: {
|
|
5
|
+
baseMessage: string;
|
|
6
|
+
kickoff: KickoffContext | null;
|
|
7
|
+
domain: string | null;
|
|
8
|
+
requiredSkills: string[];
|
|
9
|
+
}): {
|
|
10
|
+
message: string;
|
|
11
|
+
contextHash: string | null;
|
|
12
|
+
};
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
function normalizeKickoffContextResponse(value) {
|
|
2
|
+
if (!value || typeof value !== "object")
|
|
3
|
+
return null;
|
|
4
|
+
const record = value;
|
|
5
|
+
if (typeof record.context_hash === "string" && record.context_hash.trim().length > 0) {
|
|
6
|
+
return record;
|
|
7
|
+
}
|
|
8
|
+
if (record.ok === true && record.data && typeof record.data === "object") {
|
|
9
|
+
const data = record.data;
|
|
10
|
+
if (typeof data.context_hash === "string" && data.context_hash.trim().length > 0) {
|
|
11
|
+
return data;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export async function fetchKickoffContextSafe(client, payload) {
|
|
17
|
+
try {
|
|
18
|
+
const anyClient = client;
|
|
19
|
+
if (typeof anyClient.getKickoffContext === "function") {
|
|
20
|
+
const resp = await anyClient.getKickoffContext(payload);
|
|
21
|
+
return normalizeKickoffContextResponse(resp);
|
|
22
|
+
}
|
|
23
|
+
if (typeof anyClient.rawRequest === "function") {
|
|
24
|
+
const resp = await anyClient.rawRequest("POST", "/api/client/kickoff-context", payload ?? {});
|
|
25
|
+
return normalizeKickoffContextResponse(resp);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// best effort: fall back to local kickoff message
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function normalizeKickoffLines(input) {
|
|
34
|
+
if (!Array.isArray(input))
|
|
35
|
+
return [];
|
|
36
|
+
return input
|
|
37
|
+
.map((line) => (typeof line === "string" ? line.trim() : ""))
|
|
38
|
+
.filter((line) => line.length > 0);
|
|
39
|
+
}
|
|
40
|
+
export function renderKickoffMessage(input) {
|
|
41
|
+
const base = input.baseMessage.trim();
|
|
42
|
+
const kickoff = input.kickoff;
|
|
43
|
+
if (!kickoff)
|
|
44
|
+
return { message: base, contextHash: null };
|
|
45
|
+
const title = (kickoff.task?.title ??
|
|
46
|
+
kickoff.workstream?.title ??
|
|
47
|
+
kickoff.initiative?.title ??
|
|
48
|
+
"").trim();
|
|
49
|
+
const overview = (kickoff.overview ?? "").trim();
|
|
50
|
+
const acceptance = normalizeKickoffLines(kickoff.acceptance_criteria ?? null);
|
|
51
|
+
const constraints = normalizeKickoffLines(kickoff.constraints ?? null);
|
|
52
|
+
const risks = normalizeKickoffLines(kickoff.risks ?? null);
|
|
53
|
+
const reporting = normalizeKickoffLines(kickoff.reporting_expectations ?? null);
|
|
54
|
+
const decisions = Array.isArray(kickoff.decisions) ? kickoff.decisions : [];
|
|
55
|
+
const artifacts = Array.isArray(kickoff.artifacts) ? kickoff.artifacts : [];
|
|
56
|
+
const allow = normalizeKickoffLines(kickoff.tool_scope?.allow ?? null);
|
|
57
|
+
const deny = normalizeKickoffLines(kickoff.tool_scope?.deny ?? null);
|
|
58
|
+
const toolNotes = (kickoff.tool_scope?.notes ?? "").trim();
|
|
59
|
+
const personaVoice = (kickoff.persona?.voice ?? "").trim();
|
|
60
|
+
const collaborationStyle = (kickoff.persona?.collaboration_style ?? "").trim();
|
|
61
|
+
const personaDefaults = normalizeKickoffLines(kickoff.persona?.defaults ?? null);
|
|
62
|
+
const runtimeSettings = kickoff.runtime_settings && typeof kickoff.runtime_settings === "object"
|
|
63
|
+
? kickoff.runtime_settings
|
|
64
|
+
: null;
|
|
65
|
+
const customRunInstructions = runtimeSettings && typeof runtimeSettings.custom_run_instructions === "string"
|
|
66
|
+
? runtimeSettings.custom_run_instructions.trim()
|
|
67
|
+
: "";
|
|
68
|
+
const runtimeFlagLines = [
|
|
69
|
+
runtimeSettings && typeof runtimeSettings.decision_v2_enabled === "boolean"
|
|
70
|
+
? `- Decision V2: ${runtimeSettings.decision_v2_enabled ? "enabled" : "disabled"}`
|
|
71
|
+
: null,
|
|
72
|
+
runtimeSettings && typeof runtimeSettings.decision_dedupe_enabled === "boolean"
|
|
73
|
+
? `- Decision dedupe: ${runtimeSettings.decision_dedupe_enabled ? "enabled" : "disabled"}`
|
|
74
|
+
: null,
|
|
75
|
+
runtimeSettings &&
|
|
76
|
+
typeof runtimeSettings.decision_evidence_required_for_blocking === "boolean"
|
|
77
|
+
? `- Blocking evidence required: ${runtimeSettings.decision_evidence_required_for_blocking ? "enabled" : "disabled"}`
|
|
78
|
+
: null,
|
|
79
|
+
runtimeSettings &&
|
|
80
|
+
typeof runtimeSettings.decision_auto_resolve_guarded_enabled === "boolean"
|
|
81
|
+
? `- Guarded auto-resolve: ${runtimeSettings.decision_auto_resolve_guarded_enabled ? "enabled" : "disabled"}`
|
|
82
|
+
: null,
|
|
83
|
+
].filter((line) => Boolean(line));
|
|
84
|
+
const contextHash = kickoff.context_hash?.trim() || null;
|
|
85
|
+
const schemaVersion = (kickoff.schema_version ?? "").trim();
|
|
86
|
+
const lines = [];
|
|
87
|
+
lines.push("# Kickoff");
|
|
88
|
+
lines.push("");
|
|
89
|
+
if (title) {
|
|
90
|
+
lines.push(`## Target`);
|
|
91
|
+
lines.push(`- ${title}`);
|
|
92
|
+
lines.push("");
|
|
93
|
+
}
|
|
94
|
+
if (overview) {
|
|
95
|
+
lines.push("## Overview");
|
|
96
|
+
lines.push(overview);
|
|
97
|
+
lines.push("");
|
|
98
|
+
}
|
|
99
|
+
else if (base) {
|
|
100
|
+
lines.push("## Objective");
|
|
101
|
+
lines.push(base);
|
|
102
|
+
lines.push("");
|
|
103
|
+
}
|
|
104
|
+
if (acceptance.length > 0) {
|
|
105
|
+
lines.push("## Acceptance Criteria");
|
|
106
|
+
for (const item of acceptance)
|
|
107
|
+
lines.push(`- ${item}`);
|
|
108
|
+
lines.push("");
|
|
109
|
+
}
|
|
110
|
+
if (constraints.length > 0) {
|
|
111
|
+
lines.push("## Constraints");
|
|
112
|
+
for (const item of constraints)
|
|
113
|
+
lines.push(`- ${item}`);
|
|
114
|
+
lines.push("");
|
|
115
|
+
}
|
|
116
|
+
if (risks.length > 0) {
|
|
117
|
+
lines.push("## Risks");
|
|
118
|
+
for (const item of risks)
|
|
119
|
+
lines.push(`- ${item}`);
|
|
120
|
+
lines.push("");
|
|
121
|
+
}
|
|
122
|
+
if (decisions.length > 0 || artifacts.length > 0) {
|
|
123
|
+
lines.push("## References");
|
|
124
|
+
for (const item of decisions) {
|
|
125
|
+
const decisionTitle = typeof item?.title === "string"
|
|
126
|
+
? String(item.title).trim()
|
|
127
|
+
: "";
|
|
128
|
+
const id = typeof item?.id === "string" ? String(item.id).trim() : "";
|
|
129
|
+
const label = decisionTitle || id || "decision";
|
|
130
|
+
lines.push(`- Decision: ${label}`);
|
|
131
|
+
}
|
|
132
|
+
for (const item of artifacts) {
|
|
133
|
+
const artifactTitle = typeof item?.title === "string"
|
|
134
|
+
? String(item.title).trim()
|
|
135
|
+
: "";
|
|
136
|
+
const id = typeof item?.id === "string" ? String(item.id).trim() : "";
|
|
137
|
+
const label = artifactTitle || id || "artifact";
|
|
138
|
+
lines.push(`- Artifact: ${label}`);
|
|
139
|
+
}
|
|
140
|
+
lines.push("");
|
|
141
|
+
}
|
|
142
|
+
lines.push("## Operating Mode");
|
|
143
|
+
if (input.domain)
|
|
144
|
+
lines.push(`- Domain: ${input.domain}`);
|
|
145
|
+
if (input.requiredSkills.length > 0) {
|
|
146
|
+
lines.push(`- Skills: ${input.requiredSkills.join(", ")}`);
|
|
147
|
+
}
|
|
148
|
+
lines.push("- Communicate early when blocked. Provide options, tradeoffs, and a recommendation.");
|
|
149
|
+
lines.push("- Verify before claiming done. Prefer proof (commands/tests) over confidence.");
|
|
150
|
+
lines.push("");
|
|
151
|
+
if (runtimeFlagLines.length > 0 || customRunInstructions) {
|
|
152
|
+
lines.push("## Runtime Settings");
|
|
153
|
+
for (const line of runtimeFlagLines)
|
|
154
|
+
lines.push(line);
|
|
155
|
+
if (customRunInstructions) {
|
|
156
|
+
lines.push("- Custom run instructions:");
|
|
157
|
+
lines.push(` ${customRunInstructions}`);
|
|
158
|
+
}
|
|
159
|
+
lines.push("");
|
|
160
|
+
}
|
|
161
|
+
if (personaVoice || collaborationStyle || personaDefaults.length > 0) {
|
|
162
|
+
lines.push("## Behavior");
|
|
163
|
+
if (personaVoice)
|
|
164
|
+
lines.push(`- Voice: ${personaVoice}`);
|
|
165
|
+
if (collaborationStyle)
|
|
166
|
+
lines.push(`- Collaboration style: ${collaborationStyle}`);
|
|
167
|
+
for (const item of personaDefaults)
|
|
168
|
+
lines.push(`- Default: ${item}`);
|
|
169
|
+
lines.push("");
|
|
170
|
+
}
|
|
171
|
+
if (allow.length > 0 || deny.length > 0 || toolNotes) {
|
|
172
|
+
lines.push("## Tool Scope");
|
|
173
|
+
if (allow.length > 0)
|
|
174
|
+
lines.push(`- Allow: ${allow.join(", ")}`);
|
|
175
|
+
if (deny.length > 0)
|
|
176
|
+
lines.push(`- Deny: ${deny.join(", ")}`);
|
|
177
|
+
if (toolNotes)
|
|
178
|
+
lines.push(`- Notes: ${toolNotes}`);
|
|
179
|
+
lines.push("");
|
|
180
|
+
}
|
|
181
|
+
if (reporting.length > 0) {
|
|
182
|
+
lines.push("## Reporting");
|
|
183
|
+
for (const item of reporting)
|
|
184
|
+
lines.push(`- ${item}`);
|
|
185
|
+
lines.push("");
|
|
186
|
+
}
|
|
187
|
+
const teamContext = kickoff.team_context;
|
|
188
|
+
if (teamContext) {
|
|
189
|
+
const completions = Array.isArray(teamContext.recent_completions)
|
|
190
|
+
? teamContext.recent_completions.slice(0, 5)
|
|
191
|
+
: [];
|
|
192
|
+
const teamDecisions = Array.isArray(teamContext.recent_decisions)
|
|
193
|
+
? teamContext.recent_decisions.slice(0, 3)
|
|
194
|
+
: [];
|
|
195
|
+
if (completions.length > 0 || teamDecisions.length > 0) {
|
|
196
|
+
lines.push("## Team Activity");
|
|
197
|
+
lines.push("Recent work by other agents (for awareness, not direct action):");
|
|
198
|
+
for (const c of completions) {
|
|
199
|
+
const outputs = Array.isArray(c.key_outputs) && c.key_outputs.length > 0
|
|
200
|
+
? ` (${c.key_outputs.join(", ")})`
|
|
201
|
+
: "";
|
|
202
|
+
lines.push(`- [${c.domain}] ${c.task_title}: ${c.summary}${outputs}`);
|
|
203
|
+
}
|
|
204
|
+
if (teamDecisions.length > 0) {
|
|
205
|
+
lines.push("");
|
|
206
|
+
lines.push("Recent decisions:");
|
|
207
|
+
for (const d of teamDecisions) {
|
|
208
|
+
lines.push(`- ${d.title}: ${d.resolution}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
lines.push("");
|
|
212
|
+
lines.push("Reference naturally when relevant. Do not summarize back.");
|
|
213
|
+
lines.push("");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (contextHash || schemaVersion) {
|
|
217
|
+
lines.push("## Provenance");
|
|
218
|
+
if (schemaVersion)
|
|
219
|
+
lines.push(`- kickoff_schema: ${schemaVersion}`);
|
|
220
|
+
if (contextHash)
|
|
221
|
+
lines.push(`- kickoff_context_hash: ${contextHash}`);
|
|
222
|
+
lines.push("");
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
message: lines.join("\n").trimEnd(),
|
|
226
|
+
contextHash,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared LLM client for classification, summarization, and generation tasks.
|
|
3
|
+
*
|
|
4
|
+
* Generalizes the pattern from activity-headline.ts into a reusable client
|
|
5
|
+
* with in-memory caching, timeout, and heuristic fallback.
|
|
6
|
+
*/
|
|
7
|
+
export type LlmSource = "llm" | "heuristic";
|
|
8
|
+
export interface LlmRequest {
|
|
9
|
+
/** Namespace for cache keys (e.g. "retro", "turn_summary", "classify_status") */
|
|
10
|
+
taskId: string;
|
|
11
|
+
systemPrompt: string;
|
|
12
|
+
userPrompt: string;
|
|
13
|
+
/** Override default model. Use DEFAULT_GENERATION_MODEL for longer outputs. */
|
|
14
|
+
model?: string;
|
|
15
|
+
/** Default 0.1 */
|
|
16
|
+
temperature?: number;
|
|
17
|
+
/** Default 128 */
|
|
18
|
+
maxTokens?: number;
|
|
19
|
+
/** Default 4000ms */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
/** Default 12h. Set 0 to disable caching. */
|
|
22
|
+
cacheTtlMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface LlmResponse<T> {
|
|
25
|
+
result: T;
|
|
26
|
+
source: LlmSource;
|
|
27
|
+
model: string | null;
|
|
28
|
+
}
|
|
29
|
+
export declare function resolveApiKey(): string | null;
|
|
30
|
+
/** Reset cached key (for testing or key rotation). */
|
|
31
|
+
export declare function resetApiKeyCache(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Call LLM for a text completion with automatic caching and heuristic fallback.
|
|
34
|
+
*
|
|
35
|
+
* @param request - The LLM request configuration
|
|
36
|
+
* @param fallback - Heuristic fallback that produces a result when LLM is unavailable
|
|
37
|
+
* @param parse - Optional transform on the raw LLM text (default: identity)
|
|
38
|
+
*/
|
|
39
|
+
export declare function callLlm(request: LlmRequest, fallback: () => string, parse?: (raw: string) => string | null): Promise<LlmResponse<string>>;
|
|
40
|
+
/**
|
|
41
|
+
* Call LLM expecting a JSON response. Parses and validates via the provided parser.
|
|
42
|
+
*
|
|
43
|
+
* @param request - The LLM request (systemPrompt should instruct JSON output)
|
|
44
|
+
* @param parse - Parse raw JSON string into typed result, return null on failure
|
|
45
|
+
* @param fallback - Heuristic fallback
|
|
46
|
+
*/
|
|
47
|
+
export declare function callLlmJson<T>(request: LlmRequest, parse: (raw: string) => T | null, fallback: () => T): Promise<LlmResponse<T>>;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared LLM client for classification, summarization, and generation tasks.
|
|
3
|
+
*
|
|
4
|
+
* Generalizes the pattern from activity-headline.ts into a reusable client
|
|
5
|
+
* with in-memory caching, timeout, and heuristic fallback.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { pickString } from "./value-utils.js";
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Configuration
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const DEFAULT_MODEL = "openai/gpt-4.1-nano";
|
|
13
|
+
const DEFAULT_GENERATION_MODEL = "openai/gpt-4.1-mini";
|
|
14
|
+
const DEFAULT_TIMEOUT_MS = 4_000;
|
|
15
|
+
const DEFAULT_CACHE_TTL_MS = 12 * 60 * 60_000; // 12 hours
|
|
16
|
+
const CACHE_MAX_ENTRIES = 2_000;
|
|
17
|
+
const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
18
|
+
const cache = new Map();
|
|
19
|
+
function cacheKey(taskId, content) {
|
|
20
|
+
return `${taskId}:${createHash("sha256").update(content).digest("hex")}`;
|
|
21
|
+
}
|
|
22
|
+
function trimCache() {
|
|
23
|
+
while (cache.size > CACHE_MAX_ENTRIES) {
|
|
24
|
+
const firstKey = cache.keys().next().value;
|
|
25
|
+
if (!firstKey)
|
|
26
|
+
break;
|
|
27
|
+
cache.delete(firstKey);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function getCached(key) {
|
|
31
|
+
const entry = cache.get(key);
|
|
32
|
+
if (entry && entry.expiresAt > Date.now())
|
|
33
|
+
return entry;
|
|
34
|
+
if (entry)
|
|
35
|
+
cache.delete(key);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function setCached(key, value, source, ttlMs) {
|
|
39
|
+
if (ttlMs <= 0)
|
|
40
|
+
return;
|
|
41
|
+
cache.set(key, { value, source, expiresAt: Date.now() + ttlMs });
|
|
42
|
+
trimCache();
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// API key resolution
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
let resolvedApiKey;
|
|
48
|
+
export function resolveApiKey() {
|
|
49
|
+
if (resolvedApiKey !== undefined)
|
|
50
|
+
return resolvedApiKey;
|
|
51
|
+
const candidates = [
|
|
52
|
+
process.env.ORGX_LLM_API_KEY ?? "",
|
|
53
|
+
process.env.ORGX_ACTIVITY_SUMMARY_API_KEY ?? "",
|
|
54
|
+
process.env.OPENROUTER_API_KEY ?? "",
|
|
55
|
+
];
|
|
56
|
+
const key = candidates.find((c) => c.trim().length > 0)?.trim() ?? "";
|
|
57
|
+
resolvedApiKey = key || null;
|
|
58
|
+
return resolvedApiKey;
|
|
59
|
+
}
|
|
60
|
+
/** Reset cached key (for testing or key rotation). */
|
|
61
|
+
export function resetApiKeyCache() {
|
|
62
|
+
resolvedApiKey = undefined;
|
|
63
|
+
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// OpenRouter completion
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
function extractCompletionText(payload) {
|
|
68
|
+
const choices = payload.choices;
|
|
69
|
+
if (!Array.isArray(choices) || choices.length === 0)
|
|
70
|
+
return null;
|
|
71
|
+
const first = choices[0];
|
|
72
|
+
if (!first || typeof first !== "object")
|
|
73
|
+
return null;
|
|
74
|
+
const firstRecord = first;
|
|
75
|
+
const message = firstRecord.message;
|
|
76
|
+
if (message && typeof message === "object") {
|
|
77
|
+
const content = message.content;
|
|
78
|
+
if (typeof content === "string")
|
|
79
|
+
return content;
|
|
80
|
+
if (Array.isArray(content)) {
|
|
81
|
+
const textParts = content
|
|
82
|
+
.map((part) => {
|
|
83
|
+
if (typeof part === "string")
|
|
84
|
+
return part;
|
|
85
|
+
if (!part || typeof part !== "object")
|
|
86
|
+
return "";
|
|
87
|
+
const record = part;
|
|
88
|
+
return typeof record.text === "string" ? record.text : "";
|
|
89
|
+
})
|
|
90
|
+
.filter((part) => part.length > 0);
|
|
91
|
+
if (textParts.length > 0)
|
|
92
|
+
return textParts.join(" ");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return pickString(firstRecord, ["text", "content"]);
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Public API: text completion
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Call LLM for a text completion with automatic caching and heuristic fallback.
|
|
102
|
+
*
|
|
103
|
+
* @param request - The LLM request configuration
|
|
104
|
+
* @param fallback - Heuristic fallback that produces a result when LLM is unavailable
|
|
105
|
+
* @param parse - Optional transform on the raw LLM text (default: identity)
|
|
106
|
+
*/
|
|
107
|
+
export async function callLlm(request, fallback, parse) {
|
|
108
|
+
const ttl = request.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
109
|
+
const key = cacheKey(request.taskId, `${request.systemPrompt}\n${request.userPrompt}`);
|
|
110
|
+
const cached = getCached(key);
|
|
111
|
+
if (cached) {
|
|
112
|
+
return { result: cached.value, source: cached.source, model: null };
|
|
113
|
+
}
|
|
114
|
+
const apiKey = resolveApiKey();
|
|
115
|
+
if (!apiKey) {
|
|
116
|
+
const heuristic = fallback();
|
|
117
|
+
setCached(key, heuristic, "heuristic", ttl);
|
|
118
|
+
return { result: heuristic, source: "heuristic", model: null };
|
|
119
|
+
}
|
|
120
|
+
const model = process.env.ORGX_LLM_MODEL?.trim() || request.model || DEFAULT_MODEL;
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
const timeout = setTimeout(() => controller.abort(), request.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
123
|
+
try {
|
|
124
|
+
const response = await fetch(OPENROUTER_URL, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": "application/json",
|
|
128
|
+
Authorization: `Bearer ${apiKey}`,
|
|
129
|
+
},
|
|
130
|
+
body: JSON.stringify({
|
|
131
|
+
model,
|
|
132
|
+
temperature: request.temperature ?? 0.1,
|
|
133
|
+
max_tokens: request.maxTokens ?? 128,
|
|
134
|
+
messages: [
|
|
135
|
+
{ role: "system", content: request.systemPrompt },
|
|
136
|
+
{ role: "user", content: request.userPrompt },
|
|
137
|
+
],
|
|
138
|
+
}),
|
|
139
|
+
signal: controller.signal,
|
|
140
|
+
});
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`LLM request failed (${response.status})`);
|
|
143
|
+
}
|
|
144
|
+
const payload = (await response.json());
|
|
145
|
+
const raw = extractCompletionText(payload) ?? "";
|
|
146
|
+
const parsed = parse ? parse(raw) : raw.trim();
|
|
147
|
+
if (parsed && parsed.length > 0) {
|
|
148
|
+
setCached(key, parsed, "llm", ttl);
|
|
149
|
+
return { result: parsed, source: "llm", model };
|
|
150
|
+
}
|
|
151
|
+
const heuristic = fallback();
|
|
152
|
+
setCached(key, heuristic, "heuristic", ttl);
|
|
153
|
+
return { result: heuristic, source: "heuristic", model };
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
const heuristic = fallback();
|
|
157
|
+
setCached(key, heuristic, "heuristic", ttl);
|
|
158
|
+
return { result: heuristic, source: "heuristic", model: null };
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
clearTimeout(timeout);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Public API: JSON-structured completion
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
/**
|
|
168
|
+
* Call LLM expecting a JSON response. Parses and validates via the provided parser.
|
|
169
|
+
*
|
|
170
|
+
* @param request - The LLM request (systemPrompt should instruct JSON output)
|
|
171
|
+
* @param parse - Parse raw JSON string into typed result, return null on failure
|
|
172
|
+
* @param fallback - Heuristic fallback
|
|
173
|
+
*/
|
|
174
|
+
export async function callLlmJson(request, parse, fallback) {
|
|
175
|
+
const ttl = request.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
176
|
+
const key = cacheKey(request.taskId, `${request.systemPrompt}\n${request.userPrompt}`);
|
|
177
|
+
const cached = getCached(key);
|
|
178
|
+
if (cached) {
|
|
179
|
+
const parsed = parse(cached.value);
|
|
180
|
+
if (parsed !== null) {
|
|
181
|
+
return { result: parsed, source: cached.source, model: null };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const apiKey = resolveApiKey();
|
|
185
|
+
if (!apiKey) {
|
|
186
|
+
const heuristic = fallback();
|
|
187
|
+
return { result: heuristic, source: "heuristic", model: null };
|
|
188
|
+
}
|
|
189
|
+
const model = process.env.ORGX_LLM_MODEL?.trim() || request.model || DEFAULT_GENERATION_MODEL;
|
|
190
|
+
const controller = new AbortController();
|
|
191
|
+
const timeout = setTimeout(() => controller.abort(), request.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
192
|
+
try {
|
|
193
|
+
const response = await fetch(OPENROUTER_URL, {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: {
|
|
196
|
+
"Content-Type": "application/json",
|
|
197
|
+
Authorization: `Bearer ${apiKey}`,
|
|
198
|
+
},
|
|
199
|
+
body: JSON.stringify({
|
|
200
|
+
model,
|
|
201
|
+
temperature: request.temperature ?? 0.1,
|
|
202
|
+
max_tokens: request.maxTokens ?? 512,
|
|
203
|
+
messages: [
|
|
204
|
+
{ role: "system", content: request.systemPrompt },
|
|
205
|
+
{ role: "user", content: request.userPrompt },
|
|
206
|
+
],
|
|
207
|
+
}),
|
|
208
|
+
signal: controller.signal,
|
|
209
|
+
});
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
throw new Error(`LLM request failed (${response.status})`);
|
|
212
|
+
}
|
|
213
|
+
const payload = (await response.json());
|
|
214
|
+
const raw = extractCompletionText(payload) ?? "";
|
|
215
|
+
// Try to extract JSON from markdown fences or raw text
|
|
216
|
+
const jsonStr = extractJsonFromText(raw);
|
|
217
|
+
const parsed = parse(jsonStr);
|
|
218
|
+
if (parsed !== null) {
|
|
219
|
+
setCached(key, jsonStr, "llm", ttl);
|
|
220
|
+
return { result: parsed, source: "llm", model };
|
|
221
|
+
}
|
|
222
|
+
return { result: fallback(), source: "heuristic", model };
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return { result: fallback(), source: "heuristic", model: null };
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
clearTimeout(timeout);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Helpers
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
function extractJsonFromText(text) {
|
|
235
|
+
const trimmed = text.trim();
|
|
236
|
+
// Try markdown JSON fence
|
|
237
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
238
|
+
if (fenceMatch?.[1])
|
|
239
|
+
return fenceMatch[1].trim();
|
|
240
|
+
// Try raw JSON (starts with { or [)
|
|
241
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("["))
|
|
242
|
+
return trimmed;
|
|
243
|
+
// Try to find JSON embedded in text
|
|
244
|
+
const jsonStart = trimmed.indexOf("{");
|
|
245
|
+
if (jsonStart >= 0) {
|
|
246
|
+
const candidate = trimmed.slice(jsonStart);
|
|
247
|
+
try {
|
|
248
|
+
JSON.parse(candidate);
|
|
249
|
+
return candidate;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// fall through
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return trimmed;
|
|
256
|
+
}
|