@useorgx/openclaw-plugin 0.4.9 → 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 +34 -8
- package/dist/agent-context-store.js +79 -17
- package/dist/agent-run-store.js +44 -3
- package/dist/agent-suite.d.ts +9 -0
- package/dist/agent-suite.js +149 -9
- 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/chat-store.d.ts +157 -0
- package/dist/chat-store.js +586 -0
- package/dist/cli/orgx.js +11 -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 +159 -0
- package/dist/contracts/shared-types.js +177 -1
- 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 +227 -2
- package/dist/entities/auto-assignment.js +43 -17
- 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/hooks/post-reporting-event.mjs +1 -5
- package/dist/http/helpers/activity-headline.js +13 -132
- package/dist/http/helpers/auto-continue-engine.d.ts +198 -10
- package/dist/http/helpers/auto-continue-engine.js +2531 -186
- package/dist/http/helpers/autopilot-operations.d.ts +19 -0
- package/dist/http/helpers/autopilot-operations.js +182 -31
- package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
- package/dist/http/helpers/autopilot-runtime.js +308 -20
- package/dist/http/helpers/autopilot-slice-utils.d.ts +18 -0
- package/dist/http/helpers/autopilot-slice-utils.js +516 -93
- package/dist/http/helpers/decision-mapper.d.ts +40 -0
- package/dist/http/helpers/decision-mapper.js +223 -7
- package/dist/http/helpers/dispatch-lifecycle.d.ts +19 -2
- package/dist/http/helpers/dispatch-lifecycle.js +242 -37
- package/dist/http/helpers/kickoff-context.js +74 -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 +102 -3
- package/dist/http/helpers/mission-control.js +498 -9
- 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.js +7 -2
- package/dist/http/helpers/workspace-scope.d.ts +15 -0
- package/dist/http/helpers/workspace-scope.js +170 -0
- package/dist/http/index.js +1354 -97
- package/dist/http/routes/agent-suite.d.ts +9 -0
- package/dist/http/routes/agent-suite.js +207 -8
- package/dist/http/routes/agents-catalog.js +64 -19
- package/dist/http/routes/chat.d.ts +19 -0
- package/dist/http/routes/chat.js +522 -0
- package/dist/http/routes/decision-actions.d.ts +8 -1
- package/dist/http/routes/decision-actions.js +42 -5
- 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 +16 -0
- package/dist/http/routes/entities.js +294 -6
- package/dist/http/routes/live-legacy.d.ts +5 -0
- package/dist/http/routes/live-legacy.js +23 -509
- package/dist/http/routes/live-misc.d.ts +12 -0
- package/dist/http/routes/live-misc.js +251 -31
- package/dist/http/routes/live-snapshot.d.ts +48 -2
- package/dist/http/routes/live-snapshot.js +638 -19
- 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 +49 -1
- package/dist/http/routes/mission-control-actions.js +1334 -84
- package/dist/http/routes/mission-control-read.d.ts +48 -3
- package/dist/http/routes/mission-control-read.js +1593 -20
- 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 +5 -2
- package/dist/http/routes/run-control.js +10 -0
- package/dist/http/routes/sentinels-catalog.d.ts +7 -0
- package/dist/http/routes/sentinels-catalog.js +24 -0
- package/dist/http/routes/summary.js +10 -3
- package/dist/http/routes/usage.d.ts +24 -0
- package/dist/http/routes/usage.js +362 -0
- package/dist/http/routes/work-artifacts.js +28 -9
- package/dist/index.js +165 -27
- 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 +89 -7
- 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/services/background.d.ts +11 -0
- package/dist/services/background.js +22 -0
- package/dist/services/experiment-randomization.d.ts +21 -0
- package/dist/services/experiment-randomization.js +63 -0
- package/dist/skill-pack-state.d.ts +36 -5
- package/dist/skill-pack-state.js +273 -29
- package/dist/sync/local-agent-telemetry.d.ts +13 -0
- package/dist/sync/local-agent-telemetry.js +128 -0
- package/dist/sync/outbox-replay.js +131 -24
- 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 +10 -14
- package/dist/tools/core-tools.js +1289 -24
- 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/B5NEElEI.css +0 -1
- package/dashboard/dist/assets/BhapSNAs.js +0 -215
- package/dashboard/dist/assets/iFdvE7lx.js +0 -1
- package/dashboard/dist/assets/jRJsmpYM.js +0 -1
- package/dashboard/dist/assets/sAhvFnpk.js +0 -4
|
@@ -3,6 +3,7 @@ import { getOrgxPluginConfigDir, getOrgxPluginConfigPath } from "./paths.js";
|
|
|
3
3
|
import { backupCorruptFileSync, writeJsonFileAtomicSync } from "./fs-utils.js";
|
|
4
4
|
import { ensureStoreDirSync, parseJsonSafe, } from "./stores/json-store.js";
|
|
5
5
|
const MAX_PINS = 240;
|
|
6
|
+
const MAX_SUPPRESSIONS = 2_000;
|
|
6
7
|
function storeDir() {
|
|
7
8
|
return getOrgxPluginConfigDir();
|
|
8
9
|
}
|
|
@@ -28,31 +29,56 @@ function normalizeEntry(input) {
|
|
|
28
29
|
updatedAt: input.updatedAt,
|
|
29
30
|
};
|
|
30
31
|
}
|
|
32
|
+
function normalizeSuppression(input) {
|
|
33
|
+
return {
|
|
34
|
+
initiativeId: input.initiativeId.trim(),
|
|
35
|
+
workstreamId: input.workstreamId.trim(),
|
|
36
|
+
createdAt: input.createdAt,
|
|
37
|
+
updatedAt: input.updatedAt,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function entryKey(input) {
|
|
41
|
+
return `${input.initiativeId}:${input.workstreamId}`;
|
|
42
|
+
}
|
|
43
|
+
function createEmptyStore() {
|
|
44
|
+
return {
|
|
45
|
+
version: 2,
|
|
46
|
+
updatedAt: new Date().toISOString(),
|
|
47
|
+
pins: [],
|
|
48
|
+
suppressions: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
31
51
|
export function readNextUpQueuePins() {
|
|
32
52
|
const file = storeFile();
|
|
33
53
|
try {
|
|
34
54
|
if (!existsSync(file)) {
|
|
35
|
-
return
|
|
55
|
+
return createEmptyStore();
|
|
36
56
|
}
|
|
37
57
|
const raw = readFileSync(file, "utf8");
|
|
38
58
|
const parsed = parseJsonSafe(raw);
|
|
39
59
|
if (!parsed || typeof parsed !== "object") {
|
|
40
60
|
backupCorruptFileSync(file);
|
|
41
|
-
return
|
|
61
|
+
return createEmptyStore();
|
|
42
62
|
}
|
|
43
63
|
const pins = Array.isArray(parsed.pins) ? parsed.pins : [];
|
|
64
|
+
const suppressions = Array.isArray(parsed.suppressions)
|
|
65
|
+
? parsed.suppressions
|
|
66
|
+
: [];
|
|
44
67
|
return {
|
|
45
|
-
version:
|
|
68
|
+
version: 2,
|
|
46
69
|
updatedAt: typeof parsed.updatedAt === "string"
|
|
47
70
|
? parsed.updatedAt
|
|
48
71
|
: new Date().toISOString(),
|
|
49
72
|
pins: pins
|
|
50
73
|
.filter((entry) => Boolean(entry && typeof entry === "object"))
|
|
51
74
|
.map((entry) => normalizeEntry(entry)),
|
|
75
|
+
suppressions: suppressions
|
|
76
|
+
.filter((entry) => Boolean(entry && typeof entry === "object"))
|
|
77
|
+
.map((entry) => normalizeSuppression(entry)),
|
|
52
78
|
};
|
|
53
79
|
}
|
|
54
80
|
catch {
|
|
55
|
-
return
|
|
81
|
+
return createEmptyStore();
|
|
56
82
|
}
|
|
57
83
|
}
|
|
58
84
|
export function upsertNextUpQueuePin(input) {
|
|
@@ -65,7 +91,7 @@ export function upsertNextUpQueuePin(input) {
|
|
|
65
91
|
const now = new Date().toISOString();
|
|
66
92
|
const next = readNextUpQueuePins();
|
|
67
93
|
const key = `${initiativeId}:${workstreamId}`;
|
|
68
|
-
const existing = next.pins.find((pin) =>
|
|
94
|
+
const existing = next.pins.find((pin) => entryKey(pin) === key);
|
|
69
95
|
const updated = normalizeEntry({
|
|
70
96
|
initiativeId,
|
|
71
97
|
workstreamId,
|
|
@@ -74,7 +100,8 @@ export function upsertNextUpQueuePin(input) {
|
|
|
74
100
|
createdAt: existing?.createdAt ?? now,
|
|
75
101
|
updatedAt: now,
|
|
76
102
|
});
|
|
77
|
-
next.pins = [updated, ...next.pins.filter((pin) =>
|
|
103
|
+
next.pins = [updated, ...next.pins.filter((pin) => entryKey(pin) !== key)].slice(0, MAX_PINS);
|
|
104
|
+
next.suppressions = next.suppressions.filter((suppression) => entryKey(suppression) !== key);
|
|
78
105
|
next.updatedAt = now;
|
|
79
106
|
try {
|
|
80
107
|
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
@@ -93,7 +120,7 @@ export function removeNextUpQueuePin(input) {
|
|
|
93
120
|
ensureStoreDir();
|
|
94
121
|
const next = readNextUpQueuePins();
|
|
95
122
|
const key = `${initiativeId}:${workstreamId}`;
|
|
96
|
-
const filtered = next.pins.filter((pin) =>
|
|
123
|
+
const filtered = next.pins.filter((pin) => entryKey(pin) !== key);
|
|
97
124
|
if (filtered.length === next.pins.length)
|
|
98
125
|
return next;
|
|
99
126
|
next.pins = filtered;
|
|
@@ -144,6 +171,9 @@ export function setNextUpQueuePinOrder(input) {
|
|
|
144
171
|
ordered.push(pin);
|
|
145
172
|
}
|
|
146
173
|
next.pins = ordered.slice(0, MAX_PINS);
|
|
174
|
+
if (seen.size > 0 && next.suppressions.length > 0) {
|
|
175
|
+
next.suppressions = next.suppressions.filter((suppression) => !seen.has(entryKey(suppression)));
|
|
176
|
+
}
|
|
147
177
|
next.updatedAt = now;
|
|
148
178
|
try {
|
|
149
179
|
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
@@ -153,3 +183,55 @@ export function setNextUpQueuePinOrder(input) {
|
|
|
153
183
|
}
|
|
154
184
|
return next;
|
|
155
185
|
}
|
|
186
|
+
export function suppressNextUpQueueItem(input) {
|
|
187
|
+
const initiativeId = input.initiativeId.trim();
|
|
188
|
+
const workstreamId = input.workstreamId.trim();
|
|
189
|
+
if (!initiativeId || !workstreamId) {
|
|
190
|
+
return readNextUpQueuePins();
|
|
191
|
+
}
|
|
192
|
+
ensureStoreDir();
|
|
193
|
+
const now = new Date().toISOString();
|
|
194
|
+
const next = readNextUpQueuePins();
|
|
195
|
+
const key = `${initiativeId}:${workstreamId}`;
|
|
196
|
+
const existing = next.suppressions.find((suppression) => entryKey(suppression) === key);
|
|
197
|
+
next.suppressions = [
|
|
198
|
+
normalizeSuppression({
|
|
199
|
+
initiativeId,
|
|
200
|
+
workstreamId,
|
|
201
|
+
createdAt: existing?.createdAt ?? now,
|
|
202
|
+
updatedAt: now,
|
|
203
|
+
}),
|
|
204
|
+
...next.suppressions.filter((suppression) => entryKey(suppression) !== key),
|
|
205
|
+
].slice(0, MAX_SUPPRESSIONS);
|
|
206
|
+
next.pins = next.pins.filter((pin) => entryKey(pin) !== key);
|
|
207
|
+
next.updatedAt = now;
|
|
208
|
+
try {
|
|
209
|
+
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// best effort
|
|
213
|
+
}
|
|
214
|
+
return next;
|
|
215
|
+
}
|
|
216
|
+
export function unsuppressNextUpQueueItem(input) {
|
|
217
|
+
const initiativeId = input.initiativeId.trim();
|
|
218
|
+
const workstreamId = input.workstreamId.trim();
|
|
219
|
+
if (!initiativeId || !workstreamId) {
|
|
220
|
+
return readNextUpQueuePins();
|
|
221
|
+
}
|
|
222
|
+
ensureStoreDir();
|
|
223
|
+
const next = readNextUpQueuePins();
|
|
224
|
+
const key = `${initiativeId}:${workstreamId}`;
|
|
225
|
+
const filtered = next.suppressions.filter((suppression) => entryKey(suppression) !== key);
|
|
226
|
+
if (filtered.length === next.suppressions.length)
|
|
227
|
+
return next;
|
|
228
|
+
next.suppressions = filtered;
|
|
229
|
+
next.updatedAt = new Date().toISOString();
|
|
230
|
+
try {
|
|
231
|
+
writeJsonFileAtomicSync(storeFile(), next, 0o600);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// best effort
|
|
235
|
+
}
|
|
236
|
+
return next;
|
|
237
|
+
}
|
package/dist/outbox.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export interface OutboxEvent {
|
|
|
11
11
|
payload: Record<string, unknown>;
|
|
12
12
|
/** Converted to a LiveActivityItem for dashboard display. */
|
|
13
13
|
activityItem: LiveActivityItem;
|
|
14
|
+
/** Internal replay diagnostics for bounded retry/dead-letter handling. */
|
|
15
|
+
replayFailures?: number;
|
|
16
|
+
lastReplayError?: string | null;
|
|
17
|
+
lastReplayAt?: string | null;
|
|
14
18
|
}
|
|
15
19
|
export interface OutboxSummary {
|
|
16
20
|
pendingTotal: number;
|
|
@@ -18,6 +22,7 @@ export interface OutboxSummary {
|
|
|
18
22
|
oldestEventAt: string | null;
|
|
19
23
|
newestEventAt: string | null;
|
|
20
24
|
}
|
|
25
|
+
export declare function appendOutboxDeadLetter(sessionId: string, event: OutboxEvent, reason: string, error?: string | null): Promise<void>;
|
|
21
26
|
export declare function readOutbox(sessionId: string): Promise<OutboxEvent[]>;
|
|
22
27
|
export declare function appendToOutbox(sessionId: string, event: OutboxEvent): Promise<void>;
|
|
23
28
|
export declare function replaceOutbox(sessionId: string, events: OutboxEvent[]): Promise<void>;
|
package/dist/outbox.js
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Events are flushed on next successful sync.
|
|
5
5
|
*/
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import { readFile, writeFile, mkdir, chmod, rename, unlink, readdir, } from "node:fs/promises";
|
|
7
|
+
import { readFile, writeFile, mkdir, chmod, rename, unlink, readdir, appendFile, } from "node:fs/promises";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { classifyOutboxReplaySkip } from "./event-sanitization.js";
|
|
9
10
|
import { getOrgxOutboxDir } from "./paths.js";
|
|
10
11
|
function outboxDir() {
|
|
11
12
|
return getOrgxOutboxDir();
|
|
@@ -36,6 +37,11 @@ async function hardenPath(path, mode) {
|
|
|
36
37
|
// best effort
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
/** Events older than 7 days are stale — sync will never recover them. */
|
|
41
|
+
const OUTBOX_EVENT_TTL_MS = 7 * 24 * 60 * 60_000;
|
|
42
|
+
/** Hard cap per session to prevent unbounded growth if sync repeatedly fails. */
|
|
43
|
+
const OUTBOX_MAX_EVENTS_PER_SESSION = 500;
|
|
44
|
+
const DEAD_LETTER_DIRNAME = "_dead-letter";
|
|
39
45
|
async function ensureDir() {
|
|
40
46
|
const dir = outboxDir();
|
|
41
47
|
try {
|
|
@@ -46,6 +52,12 @@ async function ensureDir() {
|
|
|
46
52
|
// Directory may already exist
|
|
47
53
|
}
|
|
48
54
|
}
|
|
55
|
+
function deadLetterDir() {
|
|
56
|
+
return join(outboxDir(), DEAD_LETTER_DIRNAME);
|
|
57
|
+
}
|
|
58
|
+
function deadLetterPath(sessionId) {
|
|
59
|
+
return join(deadLetterDir(), `${normalizeSessionId(sessionId)}.jsonl`);
|
|
60
|
+
}
|
|
49
61
|
function outboxPath(sessionId) {
|
|
50
62
|
return join(outboxDir(), `${normalizeSessionId(sessionId)}.json`);
|
|
51
63
|
}
|
|
@@ -89,13 +101,79 @@ async function writeFileAtomic(targetPath, content, mode) {
|
|
|
89
101
|
}
|
|
90
102
|
await hardenPath(targetPath, mode);
|
|
91
103
|
}
|
|
104
|
+
async function appendOutboxDeadLetterRecord(sessionId, record) {
|
|
105
|
+
await ensureDir();
|
|
106
|
+
const dir = deadLetterDir();
|
|
107
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
108
|
+
await hardenPath(dir, 0o700);
|
|
109
|
+
const targetPath = deadLetterPath(sessionId);
|
|
110
|
+
await appendFile(targetPath, `${JSON.stringify(record)}\n`, {
|
|
111
|
+
encoding: "utf8",
|
|
112
|
+
mode: 0o600,
|
|
113
|
+
});
|
|
114
|
+
await hardenPath(targetPath, 0o600);
|
|
115
|
+
}
|
|
116
|
+
export async function appendOutboxDeadLetter(sessionId, event, reason, error) {
|
|
117
|
+
const droppedAt = new Date().toISOString();
|
|
118
|
+
await appendOutboxDeadLetterRecord(sessionId, {
|
|
119
|
+
droppedAt,
|
|
120
|
+
queueId: normalizeSessionId(sessionId),
|
|
121
|
+
reason,
|
|
122
|
+
error: error ?? null,
|
|
123
|
+
event,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/** Drop stale events and enforce per-session cap. Returns true if any were removed. */
|
|
127
|
+
function pruneOutboxEvents(events) {
|
|
128
|
+
const cutoff = Date.now() - OUTBOX_EVENT_TTL_MS;
|
|
129
|
+
const fresh = events.filter((e) => {
|
|
130
|
+
const epoch = Date.parse(e.timestamp);
|
|
131
|
+
return Number.isFinite(epoch) && epoch >= cutoff;
|
|
132
|
+
});
|
|
133
|
+
// Keep newest events if over cap.
|
|
134
|
+
const capped = fresh.length > OUTBOX_MAX_EVENTS_PER_SESSION
|
|
135
|
+
? fresh.slice(fresh.length - OUTBOX_MAX_EVENTS_PER_SESSION)
|
|
136
|
+
: fresh;
|
|
137
|
+
return { pruned: capped, changed: capped.length !== events.length };
|
|
138
|
+
}
|
|
92
139
|
export async function readOutbox(sessionId) {
|
|
93
|
-
|
|
140
|
+
let targetPath;
|
|
141
|
+
try {
|
|
142
|
+
targetPath = outboxPath(sessionId);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
94
147
|
try {
|
|
95
148
|
const raw = await readFile(targetPath, "utf8");
|
|
96
149
|
try {
|
|
97
150
|
const parsed = JSON.parse(raw);
|
|
98
|
-
|
|
151
|
+
const events = Array.isArray(parsed) ? parsed : [];
|
|
152
|
+
const { pruned, changed } = pruneOutboxEvents(events);
|
|
153
|
+
const filtered = [];
|
|
154
|
+
let filteredChanged = changed;
|
|
155
|
+
for (const event of pruned) {
|
|
156
|
+
const skipReason = classifyOutboxReplaySkip(event);
|
|
157
|
+
if (!skipReason) {
|
|
158
|
+
filtered.push(event);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
filteredChanged = true;
|
|
162
|
+
await appendOutboxDeadLetter(sessionId, event, `pruned_on_read:${skipReason}`, null);
|
|
163
|
+
}
|
|
164
|
+
// Write back if stale events were dropped.
|
|
165
|
+
if (filteredChanged) {
|
|
166
|
+
if (filtered.length === 0) {
|
|
167
|
+
try {
|
|
168
|
+
await unlink(targetPath);
|
|
169
|
+
}
|
|
170
|
+
catch { /* ok */ }
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
await writeFileAtomic(targetPath, JSON.stringify(filtered), 0o600);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return filtered;
|
|
99
177
|
}
|
|
100
178
|
catch {
|
|
101
179
|
await backupCorruptOutboxFile(targetPath);
|
|
@@ -107,9 +185,21 @@ export async function readOutbox(sessionId) {
|
|
|
107
185
|
}
|
|
108
186
|
}
|
|
109
187
|
export async function appendToOutbox(sessionId, event) {
|
|
188
|
+
let normalizedSessionId;
|
|
189
|
+
try {
|
|
190
|
+
normalizedSessionId = normalizeSessionId(sessionId);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const skipReason = classifyOutboxReplaySkip(event);
|
|
196
|
+
if (skipReason) {
|
|
197
|
+
await appendOutboxDeadLetter(normalizedSessionId, event, `suppressed_on_append:${skipReason}`, null);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
110
200
|
await ensureDir();
|
|
111
|
-
const targetPath = outboxPath(
|
|
112
|
-
const existing = await readOutbox(
|
|
201
|
+
const targetPath = outboxPath(normalizedSessionId);
|
|
202
|
+
const existing = await readOutbox(normalizedSessionId);
|
|
113
203
|
const idx = existing.findIndex((item) => item.id === event.id);
|
|
114
204
|
if (idx >= 0) {
|
|
115
205
|
existing[idx] = event;
|
|
@@ -117,11 +207,18 @@ export async function appendToOutbox(sessionId, event) {
|
|
|
117
207
|
else {
|
|
118
208
|
existing.push(event);
|
|
119
209
|
}
|
|
120
|
-
await writeFileAtomic(targetPath, JSON.stringify(existing
|
|
210
|
+
await writeFileAtomic(targetPath, JSON.stringify(existing), 0o600);
|
|
121
211
|
}
|
|
122
212
|
export async function replaceOutbox(sessionId, events) {
|
|
213
|
+
let normalizedSessionId;
|
|
214
|
+
try {
|
|
215
|
+
normalizedSessionId = normalizeSessionId(sessionId);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
123
220
|
await ensureDir();
|
|
124
|
-
const targetPath = outboxPath(
|
|
221
|
+
const targetPath = outboxPath(normalizedSessionId);
|
|
125
222
|
if (events.length === 0) {
|
|
126
223
|
try {
|
|
127
224
|
await unlink(targetPath);
|
|
@@ -132,7 +229,7 @@ export async function replaceOutbox(sessionId, events) {
|
|
|
132
229
|
return;
|
|
133
230
|
}
|
|
134
231
|
}
|
|
135
|
-
await writeFileAtomic(targetPath, JSON.stringify(events
|
|
232
|
+
await writeFileAtomic(targetPath, JSON.stringify(events), 0o600);
|
|
136
233
|
}
|
|
137
234
|
export async function readAllOutboxItems() {
|
|
138
235
|
try {
|
|
@@ -209,8 +306,15 @@ export async function readOutboxSummary() {
|
|
|
209
306
|
}
|
|
210
307
|
}
|
|
211
308
|
export async function clearOutbox(sessionId) {
|
|
309
|
+
let normalizedSessionId;
|
|
310
|
+
try {
|
|
311
|
+
normalizedSessionId = normalizeSessionId(sessionId);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
212
316
|
try {
|
|
213
|
-
await unlink(outboxPath(
|
|
317
|
+
await unlink(outboxPath(normalizedSessionId));
|
|
214
318
|
}
|
|
215
319
|
catch {
|
|
216
320
|
// File may not exist
|
package/dist/paths.js
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
function normalizeDirOverride(value) {
|
|
4
|
-
|
|
4
|
+
let trimmed = (value ?? "").trim();
|
|
5
5
|
if (!trimmed)
|
|
6
6
|
return null;
|
|
7
|
-
|
|
7
|
+
// `.env` values are often quoted; normalize them before validation.
|
|
8
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
9
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
10
|
+
trimmed = trimmed.slice(1, -1).trim();
|
|
11
|
+
if (!trimmed)
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
// Reject control characters to avoid malformed or ambiguous filesystem paths.
|
|
15
|
+
if (/[\u0000-\u001f\u007f]/.test(trimmed))
|
|
16
|
+
return null;
|
|
17
|
+
// Reject common escaped/encoded null-byte sequences as well.
|
|
18
|
+
if (/\\0|\\x00|\\u0000|%00/i.test(trimmed))
|
|
8
19
|
return null;
|
|
9
20
|
return trimmed;
|
|
10
21
|
}
|
|
22
|
+
function resolveOverridePath(override) {
|
|
23
|
+
if (override === "~")
|
|
24
|
+
return homedir();
|
|
25
|
+
if (override.startsWith("~/") || override.startsWith("~\\")) {
|
|
26
|
+
return resolve(homedir(), override.slice(2));
|
|
27
|
+
}
|
|
28
|
+
return resolve(override);
|
|
29
|
+
}
|
|
11
30
|
/**
|
|
12
31
|
* Root directory for persistent OrgX plugin files.
|
|
13
32
|
*
|
|
@@ -17,7 +36,7 @@ function normalizeDirOverride(value) {
|
|
|
17
36
|
export function getOrgxPluginConfigDir() {
|
|
18
37
|
const override = normalizeDirOverride(process.env.ORGX_OPENCLAW_PLUGIN_CONFIG_DIR);
|
|
19
38
|
if (override)
|
|
20
|
-
return
|
|
39
|
+
return resolveOverridePath(override);
|
|
21
40
|
return join(homedir(), ".config", "useorgx", "openclaw-plugin");
|
|
22
41
|
}
|
|
23
42
|
export function getOrgxPluginConfigPath(filename) {
|
|
@@ -32,7 +51,7 @@ export function getOrgxPluginConfigPath(filename) {
|
|
|
32
51
|
export function getOpenClawDir() {
|
|
33
52
|
const override = normalizeDirOverride(process.env.OPENCLAW_HOME);
|
|
34
53
|
if (override)
|
|
35
|
-
return
|
|
54
|
+
return resolveOverridePath(override);
|
|
36
55
|
return join(homedir(), ".openclaw");
|
|
37
56
|
}
|
|
38
57
|
/**
|
|
@@ -44,6 +63,6 @@ export function getOpenClawDir() {
|
|
|
44
63
|
export function getOrgxOutboxDir() {
|
|
45
64
|
const override = normalizeDirOverride(process.env.ORGX_OUTBOX_DIR);
|
|
46
65
|
if (override)
|
|
47
|
-
return
|
|
66
|
+
return resolveOverridePath(override);
|
|
48
67
|
return join(getOpenClawDir(), "orgx-outbox");
|
|
49
68
|
}
|
|
@@ -18,3 +18,56 @@ export declare function computeWorkstreamRollup(taskStatuses?: unknown[]): TaskS
|
|
|
18
18
|
status: WorkstreamRollupStatus;
|
|
19
19
|
progressPct: number;
|
|
20
20
|
};
|
|
21
|
+
export type TaskCompletionReadiness = "ready" | "needs_proof" | "needs_review";
|
|
22
|
+
export interface TaskCompletionReadinessResult {
|
|
23
|
+
ready: boolean;
|
|
24
|
+
status: TaskCompletionReadiness;
|
|
25
|
+
hasArtifact: boolean;
|
|
26
|
+
hasSchemaValidatedArtifact: boolean;
|
|
27
|
+
hasQualityScore: boolean;
|
|
28
|
+
qualityScore: number | null;
|
|
29
|
+
qualityThreshold: number;
|
|
30
|
+
missingItems: string[];
|
|
31
|
+
warnings: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Evaluate whether a task has sufficient proof chain evidence to be marked
|
|
35
|
+
* complete. In phase 1 this is advisory (warn-only); callers decide whether
|
|
36
|
+
* to hard-block or soft-warn based on workspace config.
|
|
37
|
+
*/
|
|
38
|
+
export declare function computeTaskCompletionReadiness(input: {
|
|
39
|
+
artifacts?: Array<{
|
|
40
|
+
schema_validated?: boolean;
|
|
41
|
+
atomic_unit_type?: string;
|
|
42
|
+
}>;
|
|
43
|
+
qualityScore?: number | null;
|
|
44
|
+
qualityThreshold?: number;
|
|
45
|
+
hasOutcomeEvent?: boolean;
|
|
46
|
+
}): TaskCompletionReadinessResult;
|
|
47
|
+
export type LifecycleState = 'Queued' | 'Dispatching' | 'In Progress' | 'Blocked' | 'Completed' | 'Paused' | 'Failed';
|
|
48
|
+
/**
|
|
49
|
+
* Derive an honest lifecycle state for an entity based on its raw status
|
|
50
|
+
* and child statuses. This is the authoritative backend derivation —
|
|
51
|
+
* the dashboard mirrors this logic in status-taxonomy.ts.
|
|
52
|
+
*/
|
|
53
|
+
export declare function deriveLifecycleState(rawStatus: unknown, childStatuses?: unknown[]): LifecycleState;
|
|
54
|
+
/**
|
|
55
|
+
* Compute cascaded progress: parent progress derived from child task states.
|
|
56
|
+
* Returns a percentage (0–100) representing weighted completion.
|
|
57
|
+
*/
|
|
58
|
+
export declare function cascadeProgressFromChildren(childStatuses: unknown[]): number;
|
|
59
|
+
export type EvalPassRateDriftResult = {
|
|
60
|
+
alert: boolean;
|
|
61
|
+
baselinePassRate: number;
|
|
62
|
+
rollingPassRate7d: number;
|
|
63
|
+
dropPct: number;
|
|
64
|
+
thresholdDropPct: number;
|
|
65
|
+
baselineSamples: number;
|
|
66
|
+
rollingSamples: number;
|
|
67
|
+
};
|
|
68
|
+
export declare function detectEvalPassRateDrift(input: {
|
|
69
|
+
passRates: unknown[];
|
|
70
|
+
thresholdDropPct?: number;
|
|
71
|
+
rollingWindowDays?: number;
|
|
72
|
+
baselineWindowDays?: number;
|
|
73
|
+
}): EvalPassRateDriftResult | null;
|
|
@@ -82,3 +82,151 @@ export function computeWorkstreamRollup(taskStatuses = []) {
|
|
|
82
82
|
progressPct,
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
+
function normalizePassRate(value) {
|
|
86
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
87
|
+
if (!Number.isFinite(n))
|
|
88
|
+
return null;
|
|
89
|
+
if (n < 0)
|
|
90
|
+
return 0;
|
|
91
|
+
if (n <= 1)
|
|
92
|
+
return n;
|
|
93
|
+
if (n <= 100)
|
|
94
|
+
return n / 100;
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
function average(values) {
|
|
98
|
+
if (values.length <= 0)
|
|
99
|
+
return 0;
|
|
100
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
101
|
+
return total / values.length;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Evaluate whether a task has sufficient proof chain evidence to be marked
|
|
105
|
+
* complete. In phase 1 this is advisory (warn-only); callers decide whether
|
|
106
|
+
* to hard-block or soft-warn based on workspace config.
|
|
107
|
+
*/
|
|
108
|
+
export function computeTaskCompletionReadiness(input) {
|
|
109
|
+
const artifacts = Array.isArray(input.artifacts) ? input.artifacts : [];
|
|
110
|
+
const qualityThreshold = typeof input.qualityThreshold === "number" && Number.isFinite(input.qualityThreshold)
|
|
111
|
+
? input.qualityThreshold
|
|
112
|
+
: 4;
|
|
113
|
+
const qualityScore = typeof input.qualityScore === "number" && Number.isFinite(input.qualityScore)
|
|
114
|
+
? input.qualityScore
|
|
115
|
+
: null;
|
|
116
|
+
const hasArtifact = artifacts.length > 0;
|
|
117
|
+
const hasSchemaValidatedArtifact = artifacts.some((a) => a.schema_validated === true && typeof a.atomic_unit_type === "string");
|
|
118
|
+
const hasQualityScore = qualityScore !== null;
|
|
119
|
+
const qualityMeetsThreshold = qualityScore !== null && qualityScore >= qualityThreshold;
|
|
120
|
+
const missingItems = [];
|
|
121
|
+
const warnings = [];
|
|
122
|
+
if (!hasArtifact) {
|
|
123
|
+
missingItems.push("No artifact registered for this task.");
|
|
124
|
+
}
|
|
125
|
+
else if (!hasSchemaValidatedArtifact) {
|
|
126
|
+
warnings.push("Artifact(s) present but none pass domain schema validation.");
|
|
127
|
+
}
|
|
128
|
+
if (!hasQualityScore) {
|
|
129
|
+
missingItems.push("No quality score recorded.");
|
|
130
|
+
}
|
|
131
|
+
else if (!qualityMeetsThreshold) {
|
|
132
|
+
missingItems.push(`Quality score ${qualityScore} below threshold ${qualityThreshold}.`);
|
|
133
|
+
}
|
|
134
|
+
if (input.hasOutcomeEvent === false) {
|
|
135
|
+
warnings.push("No outcome event recorded (L5 Impact not met).");
|
|
136
|
+
}
|
|
137
|
+
const ready = hasArtifact && hasQualityScore && qualityMeetsThreshold;
|
|
138
|
+
let status;
|
|
139
|
+
if (ready) {
|
|
140
|
+
status = "ready";
|
|
141
|
+
}
|
|
142
|
+
else if (hasArtifact && hasQualityScore) {
|
|
143
|
+
status = "needs_review";
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
status = "needs_proof";
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
ready,
|
|
150
|
+
status,
|
|
151
|
+
hasArtifact,
|
|
152
|
+
hasSchemaValidatedArtifact,
|
|
153
|
+
hasQualityScore,
|
|
154
|
+
qualityScore,
|
|
155
|
+
qualityThreshold,
|
|
156
|
+
missingItems,
|
|
157
|
+
warnings,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Derive an honest lifecycle state for an entity based on its raw status
|
|
162
|
+
* and child statuses. This is the authoritative backend derivation —
|
|
163
|
+
* the dashboard mirrors this logic in status-taxonomy.ts.
|
|
164
|
+
*/
|
|
165
|
+
export function deriveLifecycleState(rawStatus, childStatuses) {
|
|
166
|
+
const s = classifyTaskState(rawStatus);
|
|
167
|
+
if (s === 'done')
|
|
168
|
+
return 'Completed';
|
|
169
|
+
if (s === 'blocked')
|
|
170
|
+
return 'Blocked';
|
|
171
|
+
if (!childStatuses || childStatuses.length === 0) {
|
|
172
|
+
if (s === 'active')
|
|
173
|
+
return 'In Progress';
|
|
174
|
+
return 'Queued';
|
|
175
|
+
}
|
|
176
|
+
const counts = summarizeTaskStatuses(childStatuses);
|
|
177
|
+
if (counts.total > 0 && counts.done >= counts.total)
|
|
178
|
+
return 'Completed';
|
|
179
|
+
if (counts.blocked > 0 && counts.active === 0 && counts.done < counts.total)
|
|
180
|
+
return 'Blocked';
|
|
181
|
+
if (counts.active > 0 || counts.done > 0)
|
|
182
|
+
return 'In Progress';
|
|
183
|
+
return 'Queued';
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Compute cascaded progress: parent progress derived from child task states.
|
|
187
|
+
* Returns a percentage (0–100) representing weighted completion.
|
|
188
|
+
*/
|
|
189
|
+
export function cascadeProgressFromChildren(childStatuses) {
|
|
190
|
+
if (!Array.isArray(childStatuses) || childStatuses.length === 0)
|
|
191
|
+
return 0;
|
|
192
|
+
const counts = summarizeTaskStatuses(childStatuses);
|
|
193
|
+
return toPercent(counts.done, counts.total);
|
|
194
|
+
}
|
|
195
|
+
export function detectEvalPassRateDrift(input) {
|
|
196
|
+
const thresholdDropPct = typeof input.thresholdDropPct === "number" && Number.isFinite(input.thresholdDropPct) && input.thresholdDropPct >= 0
|
|
197
|
+
? input.thresholdDropPct
|
|
198
|
+
: 5;
|
|
199
|
+
const rollingWindowDays = typeof input.rollingWindowDays === "number" && Number.isFinite(input.rollingWindowDays) && input.rollingWindowDays > 0
|
|
200
|
+
? Math.floor(input.rollingWindowDays)
|
|
201
|
+
: 7;
|
|
202
|
+
const baselineWindowDays = typeof input.baselineWindowDays === "number" &&
|
|
203
|
+
Number.isFinite(input.baselineWindowDays) &&
|
|
204
|
+
input.baselineWindowDays > 0
|
|
205
|
+
? Math.floor(input.baselineWindowDays)
|
|
206
|
+
: rollingWindowDays;
|
|
207
|
+
const normalizedPassRates = (Array.isArray(input.passRates) ? input.passRates : [])
|
|
208
|
+
.map(normalizePassRate)
|
|
209
|
+
.filter((value) => value != null);
|
|
210
|
+
const requiredSamples = rollingWindowDays + baselineWindowDays;
|
|
211
|
+
if (normalizedPassRates.length < requiredSamples)
|
|
212
|
+
return null;
|
|
213
|
+
const rollingStart = normalizedPassRates.length - rollingWindowDays;
|
|
214
|
+
const baselineEnd = rollingStart;
|
|
215
|
+
const baselineStart = Math.max(0, baselineEnd - baselineWindowDays);
|
|
216
|
+
const rollingSlice = normalizedPassRates.slice(rollingStart);
|
|
217
|
+
const baselineSlice = normalizedPassRates.slice(baselineStart, baselineEnd);
|
|
218
|
+
if (rollingSlice.length <= 0 || baselineSlice.length <= 0)
|
|
219
|
+
return null;
|
|
220
|
+
const baselinePassRate = average(baselineSlice);
|
|
221
|
+
const rollingPassRate7d = average(rollingSlice);
|
|
222
|
+
const dropPct = Number(((baselinePassRate - rollingPassRate7d) * 100).toFixed(2));
|
|
223
|
+
return {
|
|
224
|
+
alert: dropPct > thresholdDropPct,
|
|
225
|
+
baselinePassRate: Number(baselinePassRate.toFixed(4)),
|
|
226
|
+
rollingPassRate7d: Number(rollingPassRate7d.toFixed(4)),
|
|
227
|
+
dropPct,
|
|
228
|
+
thresholdDropPct,
|
|
229
|
+
baselineSamples: baselineSlice.length,
|
|
230
|
+
rollingSamples: rollingSlice.length,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
type RetroFollowUp = {
|
|
2
|
+
title: string;
|
|
3
|
+
priority?: "p0" | "p1" | "p2";
|
|
4
|
+
reason?: string;
|
|
5
|
+
};
|
|
6
|
+
export type OrgxAgentRetroDomain = "engineering" | "product" | "design" | "marketing" | "sales" | "operations" | "orchestration" | "general";
|
|
7
|
+
export declare function buildRetroTemplateForAgent(input: {
|
|
8
|
+
agentId: string | null | undefined;
|
|
9
|
+
success: boolean;
|
|
10
|
+
taskId: string | null | undefined;
|
|
11
|
+
runId: string;
|
|
12
|
+
errorMessage: string | null | undefined;
|
|
13
|
+
}): {
|
|
14
|
+
domain: OrgxAgentRetroDomain;
|
|
15
|
+
summary: string;
|
|
16
|
+
whatWentWell: string[];
|
|
17
|
+
whatWentWrong: string[];
|
|
18
|
+
decisions: string[];
|
|
19
|
+
followUps: RetroFollowUp[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Generate a structured retro using an LLM, falling back to the heuristic
|
|
23
|
+
* template produced by {@link buildRetroTemplateForAgent}.
|
|
24
|
+
*
|
|
25
|
+
* Caller: src/index.ts (~line 971) — `buildRetroTemplateForAgent` is invoked
|
|
26
|
+
* inside the session-stopped handler. Migrate that call site to use this
|
|
27
|
+
* function when ready to enable LLM-powered retros.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildRetroWithLlm(input: {
|
|
30
|
+
agentId: string | null | undefined;
|
|
31
|
+
success: boolean;
|
|
32
|
+
taskId: string | null | undefined;
|
|
33
|
+
runId: string;
|
|
34
|
+
errorMessage: string | null | undefined;
|
|
35
|
+
executionContext?: string | null;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
domain: OrgxAgentRetroDomain;
|
|
38
|
+
summary: string;
|
|
39
|
+
whatWentWell: string[];
|
|
40
|
+
whatWentWrong: string[];
|
|
41
|
+
decisions: string[];
|
|
42
|
+
followUps: RetroFollowUp[];
|
|
43
|
+
source: "llm" | "heuristic";
|
|
44
|
+
}>;
|
|
45
|
+
export {};
|