@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,621 @@
|
|
|
1
|
+
import { registerArtifact } from "../artifacts/register-artifact.js";
|
|
2
|
+
import { appendOutboxDeadLetter, readOutbox, readOutboxSummary, replaceOutbox, } from "../outbox.js";
|
|
3
|
+
import { extractProgressOutboxMessage } from "../reporting/outbox-replay.js";
|
|
4
|
+
import { RETRO_ARTIFACT_SCHEMA_VERSION } from "../contracts/retro-schema.js";
|
|
5
|
+
import { classifyOutboxReplaySkip } from "../event-sanitization.js";
|
|
6
|
+
const OUTBOX_MAX_REPLAY_FAILURES = (() => {
|
|
7
|
+
const raw = Number(process.env.ORGX_OUTBOX_MAX_REPLAY_FAILURES ?? "");
|
|
8
|
+
if (!Number.isFinite(raw))
|
|
9
|
+
return 3;
|
|
10
|
+
return Math.max(1, Math.min(20, Math.floor(raw)));
|
|
11
|
+
})();
|
|
12
|
+
export function createOutboxReplayer(deps) {
|
|
13
|
+
const { client, logger, toErrorMessage, stableHash, resolveReportingContext, pickStringField, pickStringArrayField, toReportingPhase, parseRetroEntityType, isUuid, } = deps;
|
|
14
|
+
async function replayOutboxEvent(event) {
|
|
15
|
+
const payload = event.payload ?? {};
|
|
16
|
+
function normalizeRunFields(context) {
|
|
17
|
+
// We prefer correlation IDs for replay because many local adapters use UUID-like
|
|
18
|
+
// session IDs that do *not* exist as server-side run IDs.
|
|
19
|
+
if (context.correlationId) {
|
|
20
|
+
return { run_id: undefined, correlation_id: context.correlationId };
|
|
21
|
+
}
|
|
22
|
+
if (context.runId) {
|
|
23
|
+
return {
|
|
24
|
+
run_id: undefined,
|
|
25
|
+
correlation_id: `openclaw_run_${stableHash(context.runId).slice(0, 24)}`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return { run_id: undefined, correlation_id: undefined };
|
|
29
|
+
}
|
|
30
|
+
function normalizeRetro(payload) {
|
|
31
|
+
const retro = payload.retro && typeof payload.retro === "object" && !Array.isArray(payload.retro)
|
|
32
|
+
? payload.retro
|
|
33
|
+
: null;
|
|
34
|
+
const trimAndClamp = (value, maxLength) => value.trim().slice(0, maxLength);
|
|
35
|
+
const summary = retro && typeof retro.summary === "string"
|
|
36
|
+
? trimAndClamp(retro.summary, 4000)
|
|
37
|
+
: "";
|
|
38
|
+
if (!retro || !summary) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const normalizeStringList = (value) => {
|
|
42
|
+
if (!Array.isArray(value))
|
|
43
|
+
return undefined;
|
|
44
|
+
const normalized = value
|
|
45
|
+
.filter((item) => typeof item === "string")
|
|
46
|
+
.map((item) => trimAndClamp(item, 1000))
|
|
47
|
+
.filter((item) => item.length > 0)
|
|
48
|
+
.slice(0, 25);
|
|
49
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
50
|
+
};
|
|
51
|
+
const pickRetroStringList = (snakeCaseKey, camelCaseKey) => normalizeStringList(retro[snakeCaseKey]) ?? normalizeStringList(retro[camelCaseKey]);
|
|
52
|
+
const followUpsRaw = Array.isArray(retro.follow_ups)
|
|
53
|
+
? retro.follow_ups
|
|
54
|
+
: Array.isArray(retro.followUps)
|
|
55
|
+
? retro.followUps
|
|
56
|
+
: [];
|
|
57
|
+
const followUps = [];
|
|
58
|
+
for (const candidate of followUpsRaw) {
|
|
59
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const title = typeof candidate.title === "string"
|
|
63
|
+
? trimAndClamp(candidate.title, 500)
|
|
64
|
+
: "";
|
|
65
|
+
if (!title) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const priorityRaw = typeof candidate.priority === "string"
|
|
69
|
+
? candidate.priority.trim().toLowerCase()
|
|
70
|
+
: "";
|
|
71
|
+
const priority = priorityRaw === "p0" || priorityRaw === "p1" || priorityRaw === "p2"
|
|
72
|
+
? priorityRaw
|
|
73
|
+
: undefined;
|
|
74
|
+
const reason = typeof candidate.reason === "string"
|
|
75
|
+
? trimAndClamp(candidate.reason, 2000)
|
|
76
|
+
: "";
|
|
77
|
+
const normalizedFollowUp = { title };
|
|
78
|
+
if (priority) {
|
|
79
|
+
normalizedFollowUp.priority = priority;
|
|
80
|
+
}
|
|
81
|
+
if (reason) {
|
|
82
|
+
normalizedFollowUp.reason = reason;
|
|
83
|
+
}
|
|
84
|
+
followUps.push(normalizedFollowUp);
|
|
85
|
+
if (followUps.length >= 25) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const signals = retro.signals && typeof retro.signals === "object" && !Array.isArray(retro.signals)
|
|
90
|
+
? retro.signals
|
|
91
|
+
: undefined;
|
|
92
|
+
return {
|
|
93
|
+
schema_version: RETRO_ARTIFACT_SCHEMA_VERSION,
|
|
94
|
+
summary,
|
|
95
|
+
what_went_well: pickRetroStringList("what_went_well", "whatWentWell"),
|
|
96
|
+
what_went_wrong: pickRetroStringList("what_went_wrong", "whatWentWrong"),
|
|
97
|
+
decisions: pickRetroStringList("decisions", "keyDecisions"),
|
|
98
|
+
follow_ups: followUps.length > 0 ? followUps : undefined,
|
|
99
|
+
signals,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (event.type === "progress") {
|
|
103
|
+
const message = extractProgressOutboxMessage(payload);
|
|
104
|
+
if (!message) {
|
|
105
|
+
logger.warn?.("[orgx] Dropping invalid progress outbox event", {
|
|
106
|
+
eventId: event.id,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const context = resolveReportingContext(payload);
|
|
111
|
+
if (!context.ok) {
|
|
112
|
+
throw new Error(context.error);
|
|
113
|
+
}
|
|
114
|
+
const rawPhase = pickStringField(payload, "phase") ?? "implementing";
|
|
115
|
+
const progressPct = typeof payload.progress_pct === "number"
|
|
116
|
+
? payload.progress_pct
|
|
117
|
+
: typeof payload.progressPct === "number"
|
|
118
|
+
? payload.progressPct
|
|
119
|
+
: undefined;
|
|
120
|
+
const phase = rawPhase === "intent" ||
|
|
121
|
+
rawPhase === "execution" ||
|
|
122
|
+
rawPhase === "blocked" ||
|
|
123
|
+
rawPhase === "review" ||
|
|
124
|
+
rawPhase === "handoff" ||
|
|
125
|
+
rawPhase === "completed"
|
|
126
|
+
? rawPhase
|
|
127
|
+
: toReportingPhase(rawPhase, progressPct);
|
|
128
|
+
const metaRaw = payload.metadata;
|
|
129
|
+
const meta = metaRaw && typeof metaRaw === "object" && !Array.isArray(metaRaw)
|
|
130
|
+
? metaRaw
|
|
131
|
+
: {};
|
|
132
|
+
const baseMetadata = {
|
|
133
|
+
...meta,
|
|
134
|
+
source: "orgx_openclaw_outbox_replay",
|
|
135
|
+
outbox_event_id: event.id,
|
|
136
|
+
};
|
|
137
|
+
let emitPayload = {
|
|
138
|
+
initiative_id: context.value.initiativeId,
|
|
139
|
+
run_id: context.value.runId,
|
|
140
|
+
correlation_id: context.value.correlationId,
|
|
141
|
+
source_client: context.value.sourceClient,
|
|
142
|
+
message,
|
|
143
|
+
phase,
|
|
144
|
+
progress_pct: progressPct,
|
|
145
|
+
level: pickStringField(payload, "level"),
|
|
146
|
+
next_step: pickStringField(payload, "next_step") ??
|
|
147
|
+
pickStringField(payload, "nextStep") ??
|
|
148
|
+
undefined,
|
|
149
|
+
metadata: baseMetadata,
|
|
150
|
+
};
|
|
151
|
+
try {
|
|
152
|
+
await client.emitActivity(emitPayload);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
// Some locally-buffered events carry a UUID that *looks* like an OrgX run_id
|
|
156
|
+
// but was only ever used as a local correlation/grouping key. If OrgX
|
|
157
|
+
// doesn't recognize it, retry by treating it as correlation_id so OrgX can
|
|
158
|
+
// create/attach a run deterministically.
|
|
159
|
+
const msg = toErrorMessage(err);
|
|
160
|
+
if (emitPayload.run_id &&
|
|
161
|
+
/(?:^|\b)404\b/.test(msg) &&
|
|
162
|
+
/\brun\b/i.test(msg) &&
|
|
163
|
+
/not found/i.test(msg)) {
|
|
164
|
+
const replayCorrelationId = `openclaw_run_${stableHash(emitPayload.run_id).slice(0, 24)}`;
|
|
165
|
+
await client.emitActivity({
|
|
166
|
+
...emitPayload,
|
|
167
|
+
run_id: undefined,
|
|
168
|
+
correlation_id: replayCorrelationId,
|
|
169
|
+
metadata: {
|
|
170
|
+
...(emitPayload.metadata ?? {}),
|
|
171
|
+
replay_run_id_as_correlation: true,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (event.type === "decision") {
|
|
182
|
+
const question = pickStringField(payload, "question");
|
|
183
|
+
if (!question) {
|
|
184
|
+
logger.warn?.("[orgx] Dropping invalid decision outbox event", {
|
|
185
|
+
eventId: event.id,
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const context = resolveReportingContext(payload);
|
|
190
|
+
if (!context.ok) {
|
|
191
|
+
throw new Error(context.error);
|
|
192
|
+
}
|
|
193
|
+
const runFields = normalizeRunFields({
|
|
194
|
+
runId: context.value.runId,
|
|
195
|
+
correlationId: context.value.correlationId,
|
|
196
|
+
});
|
|
197
|
+
// Payloads should include a stable idempotency_key when enqueued, but older
|
|
198
|
+
// events may not. Derive a deterministic fallback so outbox replay won't
|
|
199
|
+
// double-create the same remote decision.
|
|
200
|
+
const fallbackKey = stableHash(JSON.stringify({
|
|
201
|
+
t: "decision",
|
|
202
|
+
initiative_id: context.value.initiativeId,
|
|
203
|
+
run_id: context.value.runId ?? null,
|
|
204
|
+
correlation_id: context.value.correlationId ?? null,
|
|
205
|
+
question,
|
|
206
|
+
})).slice(0, 24);
|
|
207
|
+
const resolvedIdempotencyKey = pickStringField(payload, "idempotency_key") ??
|
|
208
|
+
pickStringField(payload, "idempotencyKey") ??
|
|
209
|
+
`openclaw:decision:${fallbackKey}`;
|
|
210
|
+
await client.applyChangeset({
|
|
211
|
+
initiative_id: context.value.initiativeId,
|
|
212
|
+
run_id: runFields.run_id,
|
|
213
|
+
correlation_id: runFields.correlation_id,
|
|
214
|
+
source_client: context.value.sourceClient,
|
|
215
|
+
idempotency_key: resolvedIdempotencyKey,
|
|
216
|
+
operations: [
|
|
217
|
+
{
|
|
218
|
+
op: "decision.create",
|
|
219
|
+
title: question,
|
|
220
|
+
summary: pickStringField(payload, "context") ?? undefined,
|
|
221
|
+
urgency: pickStringField(payload, "urgency") ?? "medium",
|
|
222
|
+
options: pickStringArrayField(payload, "options"),
|
|
223
|
+
blocking: typeof payload.blocking === "boolean" ? payload.blocking : true,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (event.type === "changeset") {
|
|
230
|
+
const context = resolveReportingContext(payload);
|
|
231
|
+
if (!context.ok) {
|
|
232
|
+
throw new Error(context.error);
|
|
233
|
+
}
|
|
234
|
+
const runFields = normalizeRunFields({
|
|
235
|
+
runId: context.value.runId,
|
|
236
|
+
correlationId: context.value.correlationId,
|
|
237
|
+
});
|
|
238
|
+
const operations = Array.isArray(payload.operations)
|
|
239
|
+
? payload.operations
|
|
240
|
+
: [];
|
|
241
|
+
if (operations.length === 0) {
|
|
242
|
+
logger.warn?.("[orgx] Dropping invalid changeset outbox event", {
|
|
243
|
+
eventId: event.id,
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// Status updates are the most common offline replay payload, and `updateEntity`
|
|
248
|
+
// is the most widely supported primitive across OrgX deployments. Prefer it
|
|
249
|
+
// when the changeset contains only simple status mutations.
|
|
250
|
+
const statusOps = operations
|
|
251
|
+
.map((op) => {
|
|
252
|
+
if (!op || typeof op !== "object")
|
|
253
|
+
return null;
|
|
254
|
+
const record = op;
|
|
255
|
+
const kind = typeof record.op === "string" ? record.op.trim() : "";
|
|
256
|
+
if (kind === "task.update") {
|
|
257
|
+
const taskId = typeof record.task_id === "string" ? record.task_id.trim() : "";
|
|
258
|
+
const statusRaw = typeof record.status === "string" ? record.status.trim() : "";
|
|
259
|
+
const normalized = statusRaw.toLowerCase().replace(/\s+/g, "_");
|
|
260
|
+
const status = normalized === "completed" || normalized === "complete" || normalized === "finished"
|
|
261
|
+
? "done"
|
|
262
|
+
: normalized === "inprogress"
|
|
263
|
+
? "in_progress"
|
|
264
|
+
: normalized;
|
|
265
|
+
if (!taskId || !status)
|
|
266
|
+
return null;
|
|
267
|
+
return { type: "task", id: taskId, status };
|
|
268
|
+
}
|
|
269
|
+
if (kind === "milestone.update") {
|
|
270
|
+
const milestoneId = typeof record.milestone_id === "string" ? record.milestone_id.trim() : "";
|
|
271
|
+
const statusRaw = typeof record.status === "string" ? record.status.trim() : "";
|
|
272
|
+
const normalized = statusRaw.toLowerCase().replace(/\s+/g, "_");
|
|
273
|
+
const status = normalized === "done" || normalized === "complete" || normalized === "finished"
|
|
274
|
+
? "completed"
|
|
275
|
+
: normalized === "inprogress"
|
|
276
|
+
? "in_progress"
|
|
277
|
+
: normalized === "todo" || normalized === "not_started" || normalized === "pending"
|
|
278
|
+
? "planned"
|
|
279
|
+
: normalized === "blocked" || normalized === "stuck"
|
|
280
|
+
? "at_risk"
|
|
281
|
+
: normalized;
|
|
282
|
+
if (!milestoneId || !status)
|
|
283
|
+
return null;
|
|
284
|
+
return { type: "milestone", id: milestoneId, status };
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
})
|
|
288
|
+
.filter((item) => Boolean(item));
|
|
289
|
+
if (statusOps.length === operations.length) {
|
|
290
|
+
for (const op of statusOps) {
|
|
291
|
+
await client.updateEntity(op.type, op.id, { status: op.status });
|
|
292
|
+
}
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
// Payloads should include a stable idempotency_key when enqueued, but older
|
|
296
|
+
// events may not. Derive a deterministic fallback so outbox replay won't
|
|
297
|
+
// double-create the same remote change.
|
|
298
|
+
const fallbackKey = stableHash(JSON.stringify({
|
|
299
|
+
t: "changeset",
|
|
300
|
+
initiative_id: context.value.initiativeId,
|
|
301
|
+
run_id: context.value.runId ?? null,
|
|
302
|
+
correlation_id: context.value.correlationId ?? null,
|
|
303
|
+
operations,
|
|
304
|
+
})).slice(0, 24);
|
|
305
|
+
const resolvedIdempotencyKey = pickStringField(payload, "idempotency_key") ??
|
|
306
|
+
pickStringField(payload, "idempotencyKey") ??
|
|
307
|
+
`openclaw:changeset:${fallbackKey}`;
|
|
308
|
+
await client.applyChangeset({
|
|
309
|
+
initiative_id: context.value.initiativeId,
|
|
310
|
+
run_id: runFields.run_id,
|
|
311
|
+
correlation_id: runFields.correlation_id,
|
|
312
|
+
source_client: context.value.sourceClient,
|
|
313
|
+
idempotency_key: resolvedIdempotencyKey,
|
|
314
|
+
operations,
|
|
315
|
+
});
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (event.type === "outcome") {
|
|
319
|
+
const context = resolveReportingContext(payload);
|
|
320
|
+
if (!context.ok) {
|
|
321
|
+
throw new Error(context.error);
|
|
322
|
+
}
|
|
323
|
+
const runFields = normalizeRunFields({
|
|
324
|
+
runId: context.value.runId,
|
|
325
|
+
correlationId: context.value.correlationId,
|
|
326
|
+
});
|
|
327
|
+
const executionId = pickStringField(payload, "execution_id") ??
|
|
328
|
+
pickStringField(payload, "executionId");
|
|
329
|
+
const executionType = pickStringField(payload, "execution_type") ??
|
|
330
|
+
pickStringField(payload, "executionType");
|
|
331
|
+
const agentId = pickStringField(payload, "agent_id") ??
|
|
332
|
+
pickStringField(payload, "agentId");
|
|
333
|
+
const success = typeof payload.success === "boolean"
|
|
334
|
+
? payload.success
|
|
335
|
+
: null;
|
|
336
|
+
if (!executionId || !executionType || !agentId || success === null) {
|
|
337
|
+
logger.warn?.("[orgx] Dropping invalid outcome outbox event", {
|
|
338
|
+
eventId: event.id,
|
|
339
|
+
});
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const metaRaw = payload.metadata;
|
|
343
|
+
const meta = metaRaw && typeof metaRaw === "object" && !Array.isArray(metaRaw)
|
|
344
|
+
? metaRaw
|
|
345
|
+
: {};
|
|
346
|
+
await client.recordRunOutcome({
|
|
347
|
+
initiative_id: context.value.initiativeId,
|
|
348
|
+
run_id: runFields.run_id,
|
|
349
|
+
correlation_id: runFields.correlation_id,
|
|
350
|
+
source_client: context.value.sourceClient,
|
|
351
|
+
execution_id: executionId,
|
|
352
|
+
execution_type: executionType,
|
|
353
|
+
agent_id: agentId,
|
|
354
|
+
task_type: pickStringField(payload, "task_type") ??
|
|
355
|
+
pickStringField(payload, "taskType") ??
|
|
356
|
+
undefined,
|
|
357
|
+
domain: pickStringField(payload, "domain") ?? undefined,
|
|
358
|
+
started_at: pickStringField(payload, "started_at") ??
|
|
359
|
+
pickStringField(payload, "startedAt") ??
|
|
360
|
+
undefined,
|
|
361
|
+
completed_at: pickStringField(payload, "completed_at") ??
|
|
362
|
+
pickStringField(payload, "completedAt") ??
|
|
363
|
+
undefined,
|
|
364
|
+
inputs: payload.inputs && typeof payload.inputs === "object"
|
|
365
|
+
? payload.inputs
|
|
366
|
+
: undefined,
|
|
367
|
+
outputs: payload.outputs && typeof payload.outputs === "object"
|
|
368
|
+
? payload.outputs
|
|
369
|
+
: undefined,
|
|
370
|
+
steps: Array.isArray(payload.steps)
|
|
371
|
+
? payload.steps
|
|
372
|
+
: undefined,
|
|
373
|
+
success,
|
|
374
|
+
quality_score: typeof payload.quality_score === "number"
|
|
375
|
+
? payload.quality_score
|
|
376
|
+
: typeof payload.qualityScore === "number"
|
|
377
|
+
? payload.qualityScore
|
|
378
|
+
: undefined,
|
|
379
|
+
duration_vs_estimate: typeof payload.duration_vs_estimate === "number"
|
|
380
|
+
? payload.duration_vs_estimate
|
|
381
|
+
: typeof payload.durationVsEstimate === "number"
|
|
382
|
+
? payload.durationVsEstimate
|
|
383
|
+
: undefined,
|
|
384
|
+
cost_vs_budget: typeof payload.cost_vs_budget === "number"
|
|
385
|
+
? payload.cost_vs_budget
|
|
386
|
+
: typeof payload.costVsBudget === "number"
|
|
387
|
+
? payload.costVsBudget
|
|
388
|
+
: undefined,
|
|
389
|
+
human_interventions: typeof payload.human_interventions === "number"
|
|
390
|
+
? payload.human_interventions
|
|
391
|
+
: typeof payload.humanInterventions === "number"
|
|
392
|
+
? payload.humanInterventions
|
|
393
|
+
: undefined,
|
|
394
|
+
user_satisfaction: typeof payload.user_satisfaction === "number"
|
|
395
|
+
? payload.user_satisfaction
|
|
396
|
+
: typeof payload.userSatisfaction === "number"
|
|
397
|
+
? payload.userSatisfaction
|
|
398
|
+
: undefined,
|
|
399
|
+
errors: Array.isArray(payload.errors)
|
|
400
|
+
? payload.errors.filter((e) => typeof e === "string")
|
|
401
|
+
: undefined,
|
|
402
|
+
metadata: {
|
|
403
|
+
...meta,
|
|
404
|
+
source: "orgx_openclaw_outbox_replay",
|
|
405
|
+
outbox_event_id: event.id,
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (event.type === "retro") {
|
|
411
|
+
const context = resolveReportingContext(payload);
|
|
412
|
+
if (!context.ok) {
|
|
413
|
+
throw new Error(context.error);
|
|
414
|
+
}
|
|
415
|
+
const runFields = normalizeRunFields({
|
|
416
|
+
runId: context.value.runId,
|
|
417
|
+
correlationId: context.value.correlationId,
|
|
418
|
+
});
|
|
419
|
+
const normalizedRetro = normalizeRetro(payload);
|
|
420
|
+
if (!normalizedRetro) {
|
|
421
|
+
logger.warn?.("[orgx] Dropping invalid retro outbox event", {
|
|
422
|
+
eventId: event.id,
|
|
423
|
+
});
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const entityTypeRaw = pickStringField(payload, "entity_type") ??
|
|
427
|
+
pickStringField(payload, "entityType");
|
|
428
|
+
const parsedEntityType = parseRetroEntityType(entityTypeRaw) ?? null;
|
|
429
|
+
// Server-side enum parity can lag behind local clients. Only attach to the
|
|
430
|
+
// entity types that are guaranteed to exist today.
|
|
431
|
+
const entityType = parsedEntityType === "initiative" || parsedEntityType === "task"
|
|
432
|
+
? parsedEntityType
|
|
433
|
+
: null;
|
|
434
|
+
const entityIdRaw = pickStringField(payload, "entity_id") ??
|
|
435
|
+
pickStringField(payload, "entityId") ??
|
|
436
|
+
null;
|
|
437
|
+
const entityId = isUuid(entityIdRaw ?? undefined) ? entityIdRaw : null;
|
|
438
|
+
await client.recordRunRetro({
|
|
439
|
+
initiative_id: context.value.initiativeId,
|
|
440
|
+
run_id: runFields.run_id,
|
|
441
|
+
correlation_id: runFields.correlation_id,
|
|
442
|
+
source_client: context.value.sourceClient,
|
|
443
|
+
entity_type: entityType && entityId ? entityType : undefined,
|
|
444
|
+
entity_id: entityType && entityId ? entityId : undefined,
|
|
445
|
+
title: pickStringField(payload, "title") ?? undefined,
|
|
446
|
+
idempotency_key: pickStringField(payload, "idempotency_key") ??
|
|
447
|
+
pickStringField(payload, "idempotencyKey") ??
|
|
448
|
+
undefined,
|
|
449
|
+
retro: normalizedRetro,
|
|
450
|
+
markdown: pickStringField(payload, "markdown") ?? undefined,
|
|
451
|
+
});
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (event.type === "artifact") {
|
|
455
|
+
// Artifacts are first-class UX loop closure (activity stream + entity modals).
|
|
456
|
+
// Try to persist upstream; if this fails, keep the event queued for retry.
|
|
457
|
+
const payload = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload)
|
|
458
|
+
? event.payload
|
|
459
|
+
: {};
|
|
460
|
+
const name = pickStringField(payload, "name") ?? pickStringField(payload, "title") ?? "";
|
|
461
|
+
const artifactType = pickStringField(payload, "artifact_type") ?? "other";
|
|
462
|
+
const entityType = pickStringField(payload, "entity_type") ?? "";
|
|
463
|
+
const entityId = pickStringField(payload, "entity_id") ?? "";
|
|
464
|
+
const artifactId = pickStringField(payload, "artifact_id") ?? null;
|
|
465
|
+
const description = pickStringField(payload, "description") ?? undefined;
|
|
466
|
+
const externalUrl = pickStringField(payload, "url") ?? pickStringField(payload, "artifact_url") ?? null;
|
|
467
|
+
const content = pickStringField(payload, "content") ?? pickStringField(payload, "preview_markdown") ?? null;
|
|
468
|
+
const confidenceRaw = payload.confidence_score;
|
|
469
|
+
const confidenceScore = typeof confidenceRaw === "number" &&
|
|
470
|
+
Number.isFinite(confidenceRaw) &&
|
|
471
|
+
confidenceRaw >= 0 &&
|
|
472
|
+
confidenceRaw <= 1
|
|
473
|
+
? confidenceRaw
|
|
474
|
+
: null;
|
|
475
|
+
const allowedEntityType = entityType === "initiative" ||
|
|
476
|
+
entityType === "milestone" ||
|
|
477
|
+
entityType === "task" ||
|
|
478
|
+
entityType === "decision" ||
|
|
479
|
+
entityType === "project"
|
|
480
|
+
? entityType
|
|
481
|
+
: null;
|
|
482
|
+
if (!allowedEntityType || !entityId.trim() || !name.trim()) {
|
|
483
|
+
logger.warn?.("[orgx] Dropping invalid artifact outbox event", {
|
|
484
|
+
eventId: event.id,
|
|
485
|
+
entityType,
|
|
486
|
+
entityId,
|
|
487
|
+
});
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const result = await registerArtifact(client, client.getBaseUrl(), {
|
|
491
|
+
artifact_id: artifactId,
|
|
492
|
+
entity_type: allowedEntityType,
|
|
493
|
+
entity_id: entityId,
|
|
494
|
+
name: name.trim(),
|
|
495
|
+
artifact_type: artifactType.trim() || "other",
|
|
496
|
+
confidence_score: confidenceScore,
|
|
497
|
+
description,
|
|
498
|
+
external_url: externalUrl,
|
|
499
|
+
preview_markdown: content,
|
|
500
|
+
status: "draft",
|
|
501
|
+
metadata: {
|
|
502
|
+
source: "outbox_replay",
|
|
503
|
+
outbox_event_id: event.id,
|
|
504
|
+
confidence_score: confidenceScore,
|
|
505
|
+
...(payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata)
|
|
506
|
+
? payload.metadata
|
|
507
|
+
: {}),
|
|
508
|
+
},
|
|
509
|
+
validate_persistence: process.env.ORGX_VALIDATE_ARTIFACT_PERSISTENCE === "1",
|
|
510
|
+
});
|
|
511
|
+
if (!result.ok) {
|
|
512
|
+
throw new Error(result.persistence.last_error ?? "artifact registration failed");
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async function flushOutboxQueues() {
|
|
518
|
+
const attemptAt = new Date().toISOString();
|
|
519
|
+
deps.writeOutboxReplayState({
|
|
520
|
+
...deps.readOutboxReplayState(),
|
|
521
|
+
status: "running",
|
|
522
|
+
lastReplayAttemptAt: attemptAt,
|
|
523
|
+
lastReplayError: null,
|
|
524
|
+
});
|
|
525
|
+
let hadReplayFailure = false;
|
|
526
|
+
let lastReplayError = null;
|
|
527
|
+
// Outbox files are keyed by *session id* (e.g. initiative/run correlation),
|
|
528
|
+
// not by event type.
|
|
529
|
+
const outboxSummary = await readOutboxSummary();
|
|
530
|
+
const queues = Object.entries(outboxSummary.pendingByQueue)
|
|
531
|
+
.filter(([, count]) => typeof count === "number" && count > 0)
|
|
532
|
+
.map(([queueId]) => queueId)
|
|
533
|
+
.sort();
|
|
534
|
+
for (const queue of queues) {
|
|
535
|
+
const pending = await readOutbox(queue);
|
|
536
|
+
if (pending.length === 0) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const remaining = [];
|
|
540
|
+
for (const event of pending) {
|
|
541
|
+
const skipReason = classifyOutboxReplaySkip(event);
|
|
542
|
+
if (skipReason) {
|
|
543
|
+
await appendOutboxDeadLetter(queue, event, `dropped_before_replay:${skipReason}`, null);
|
|
544
|
+
logger.warn?.("[orgx] Dropping non-replayable outbox event", {
|
|
545
|
+
queue,
|
|
546
|
+
eventId: event.id,
|
|
547
|
+
reason: skipReason,
|
|
548
|
+
});
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
try {
|
|
552
|
+
await replayOutboxEvent(event);
|
|
553
|
+
}
|
|
554
|
+
catch (err) {
|
|
555
|
+
hadReplayFailure = true;
|
|
556
|
+
lastReplayError = toErrorMessage(err);
|
|
557
|
+
const nextFailures = typeof event.replayFailures === "number" && Number.isFinite(event.replayFailures)
|
|
558
|
+
? Math.max(0, Math.floor(event.replayFailures)) + 1
|
|
559
|
+
: 1;
|
|
560
|
+
if (nextFailures >= OUTBOX_MAX_REPLAY_FAILURES) {
|
|
561
|
+
await appendOutboxDeadLetter(queue, {
|
|
562
|
+
...event,
|
|
563
|
+
replayFailures: nextFailures,
|
|
564
|
+
lastReplayError,
|
|
565
|
+
lastReplayAt: new Date().toISOString(),
|
|
566
|
+
}, "max_replay_failures", lastReplayError);
|
|
567
|
+
logger.warn?.("[orgx] Dead-lettering outbox event after max replay failures", {
|
|
568
|
+
queue,
|
|
569
|
+
eventId: event.id,
|
|
570
|
+
failures: nextFailures,
|
|
571
|
+
maxFailures: OUTBOX_MAX_REPLAY_FAILURES,
|
|
572
|
+
error: lastReplayError,
|
|
573
|
+
});
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
remaining.push({
|
|
577
|
+
...event,
|
|
578
|
+
replayFailures: nextFailures,
|
|
579
|
+
lastReplayError,
|
|
580
|
+
lastReplayAt: new Date().toISOString(),
|
|
581
|
+
});
|
|
582
|
+
logger.warn?.("[orgx] Outbox replay failed", {
|
|
583
|
+
queue,
|
|
584
|
+
eventId: event.id,
|
|
585
|
+
replayFailures: nextFailures,
|
|
586
|
+
error: lastReplayError,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
await replaceOutbox(queue, remaining);
|
|
591
|
+
const replayedCount = pending.length - remaining.length;
|
|
592
|
+
if (replayedCount > 0) {
|
|
593
|
+
logger.info?.("[orgx] Replayed buffered outbox events", {
|
|
594
|
+
queue,
|
|
595
|
+
replayed: replayedCount,
|
|
596
|
+
remaining: remaining.length,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (hadReplayFailure) {
|
|
601
|
+
deps.writeOutboxReplayState({
|
|
602
|
+
...deps.readOutboxReplayState(),
|
|
603
|
+
status: "error",
|
|
604
|
+
lastReplayFailureAt: new Date().toISOString(),
|
|
605
|
+
lastReplayError,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
deps.writeOutboxReplayState({
|
|
610
|
+
...deps.readOutboxReplayState(),
|
|
611
|
+
status: "success",
|
|
612
|
+
lastReplaySuccessAt: new Date().toISOString(),
|
|
613
|
+
lastReplayError: null,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
replayOutboxEvent,
|
|
619
|
+
flushOutboxQueues,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type TeamCompletion = {
|
|
2
|
+
domain: string;
|
|
3
|
+
task_title: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
key_outputs?: string[];
|
|
6
|
+
completed_at: string;
|
|
7
|
+
};
|
|
8
|
+
export type TeamDecision = {
|
|
9
|
+
title: string;
|
|
10
|
+
resolution: string;
|
|
11
|
+
affected_domains?: string[];
|
|
12
|
+
resolved_at: string;
|
|
13
|
+
};
|
|
14
|
+
type PersistedTeamContext = {
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
recent_completions: TeamCompletion[];
|
|
17
|
+
recent_decisions: TeamDecision[];
|
|
18
|
+
};
|
|
19
|
+
export declare function readTeamContext(initiativeId: string): PersistedTeamContext;
|
|
20
|
+
export declare function appendTeamCompletion(initiativeId: string, completion: TeamCompletion): void;
|
|
21
|
+
export declare function appendTeamDecision(initiativeId: string, decision: TeamDecision): void;
|
|
22
|
+
export declare function clearTeamContext(initiativeId?: string): void;
|
|
23
|
+
export {};
|