@x12i/memorix-service 3.1.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-services/associate-prep.d.ts +37 -0
- package/dist/ai-services/associate-prep.d.ts.map +1 -0
- package/dist/ai-services/associate-prep.js +208 -0
- package/dist/ai-services/associate-prep.js.map +1 -0
- package/dist/ai-services/chunk-markdown.d.ts +22 -0
- package/dist/ai-services/chunk-markdown.d.ts.map +1 -0
- package/dist/ai-services/chunk-markdown.js +159 -0
- package/dist/ai-services/chunk-markdown.js.map +1 -0
- package/dist/ai-services/client.d.ts +1 -1
- package/dist/ai-services/client.d.ts.map +1 -1
- package/dist/ai-services/client.js +2 -2
- package/dist/ai-services/client.js.map +1 -1
- package/dist/ai-services/normalize-output.d.ts +7 -1
- package/dist/ai-services/normalize-output.d.ts.map +1 -1
- package/dist/ai-services/normalize-output.js +340 -60
- package/dist/ai-services/normalize-output.js.map +1 -1
- package/dist/ai-services/skill-execution.d.ts +3 -2
- package/dist/ai-services/skill-execution.d.ts.map +1 -1
- package/dist/ai-services/skill-execution.js +24 -4
- package/dist/ai-services/skill-execution.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +4 -0
- package/dist/app.js.map +1 -1
- package/dist/connector-catalog.d.ts +79 -0
- package/dist/connector-catalog.d.ts.map +1 -0
- package/dist/connector-catalog.js +286 -0
- package/dist/connector-catalog.js.map +1 -0
- package/dist/connector-role.d.ts +22 -0
- package/dist/connector-role.d.ts.map +1 -0
- package/dist/connector-role.js +71 -0
- package/dist/connector-role.js.map +1 -0
- package/dist/destination-runtime.d.ts +8 -0
- package/dist/destination-runtime.d.ts.map +1 -0
- package/dist/destination-runtime.js +11 -0
- package/dist/destination-runtime.js.map +1 -0
- package/dist/execution-target.d.ts +141 -0
- package/dist/execution-target.d.ts.map +1 -0
- package/dist/execution-target.js +853 -0
- package/dist/execution-target.js.map +1 -0
- package/dist/generic-connectors.d.ts +43 -0
- package/dist/generic-connectors.d.ts.map +1 -0
- package/dist/generic-connectors.js +198 -0
- package/dist/generic-connectors.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/jobs-scope.d.ts +21 -0
- package/dist/jobs-scope.d.ts.map +1 -0
- package/dist/jobs-scope.js +83 -0
- package/dist/jobs-scope.js.map +1 -0
- package/dist/live.d.ts +11 -0
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +700 -73
- package/dist/live.js.map +1 -1
- package/dist/magit/magit-pack-service.d.ts +80 -0
- package/dist/magit/magit-pack-service.d.ts.map +1 -1
- package/dist/magit/magit-pack-service.js +233 -11
- package/dist/magit/magit-pack-service.js.map +1 -1
- package/dist/magit/pack-to-patchset.d.ts +16 -0
- package/dist/magit/pack-to-patchset.d.ts.map +1 -1
- package/dist/magit/pack-to-patchset.js +46 -0
- package/dist/magit/pack-to-patchset.js.map +1 -1
- package/dist/magit/package-catalog-bridge.d.ts +5 -5
- package/dist/magit/package-catalog-bridge.d.ts.map +1 -1
- package/dist/magit/package-catalog-bridge.js +3 -3
- package/dist/magit/package-catalog-bridge.js.map +1 -1
- package/dist/mgc-packages.d.ts +11 -0
- package/dist/mgc-packages.d.ts.map +1 -0
- package/dist/mgc-packages.js +11 -0
- package/dist/mgc-packages.js.map +1 -0
- package/dist/openapi/openapi.json +3445 -791
- package/dist/pipeline-compile.d.ts +46 -3
- package/dist/pipeline-compile.d.ts.map +1 -1
- package/dist/pipeline-compile.js +455 -89
- package/dist/pipeline-compile.js.map +1 -1
- package/dist/pipeline-invoke.d.ts +63 -0
- package/dist/pipeline-invoke.d.ts.map +1 -0
- package/dist/pipeline-invoke.js +114 -0
- package/dist/pipeline-invoke.js.map +1 -0
- package/dist/pipeline-ports.d.ts +13 -0
- package/dist/pipeline-ports.d.ts.map +1 -0
- package/dist/pipeline-ports.js +104 -0
- package/dist/pipeline-ports.js.map +1 -0
- package/dist/platform.d.ts +30 -1
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +239 -8
- package/dist/platform.js.map +1 -1
- package/dist/routes/connector-invocations.d.ts.map +1 -1
- package/dist/routes/connector-invocations.js +18 -21
- package/dist/routes/connector-invocations.js.map +1 -1
- package/dist/routes/jobs.d.ts +4 -0
- package/dist/routes/jobs.d.ts.map +1 -0
- package/dist/routes/jobs.js +888 -0
- package/dist/routes/jobs.js.map +1 -0
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +148 -79
- package/dist/routes/memory.js.map +1 -1
- package/dist/routes/metadata.d.ts.map +1 -1
- package/dist/routes/metadata.js +171 -40
- package/dist/routes/metadata.js.map +1 -1
- package/dist/routes/pipelines.d.ts.map +1 -1
- package/dist/routes/pipelines.js +98 -22
- package/dist/routes/pipelines.js.map +1 -1
- package/dist/routes/relationships.d.ts.map +1 -1
- package/dist/routes/relationships.js +80 -2
- package/dist/routes/relationships.js.map +1 -1
- package/dist/runx/client.d.ts +18 -0
- package/dist/runx/client.d.ts.map +1 -0
- package/dist/runx/client.js +59 -0
- package/dist/runx/client.js.map +1 -0
- package/openapi/openapi.json +3445 -791
- package/package.json +16 -9
package/dist/live.js
CHANGED
|
@@ -8,8 +8,41 @@ import { createTrafficRing, createInstrumentedFetch, } from "@x12i/api-live-view
|
|
|
8
8
|
import { createServiceInvokePorts, } from "@x12i/memorix-pipeline/v2";
|
|
9
9
|
import { issueCallbackGrant } from "./callback-grant.js";
|
|
10
10
|
import { createAiServicesClient } from "./ai-services/client.js";
|
|
11
|
+
import { chunkMarkdown, markdownApproxTokens, } from "./ai-services/chunk-markdown.js";
|
|
11
12
|
import { normalizeAiServiceStepOutput } from "./ai-services/normalize-output.js";
|
|
12
13
|
import { defaultSkillExecutionForItem } from "./ai-services/skill-execution.js";
|
|
14
|
+
import { associateDraftChunkSize, associateExistingCap, chunkArray, draftKey as associateDraftKey, mergeAssociateOutputs, prepareExistingKnowxForAssociate, slimDraftsForAssociate, slimKnowxForAssociate, } from "./ai-services/associate-prep.js";
|
|
15
|
+
import { deliverViaPlatform } from "./destination-runtime.js";
|
|
16
|
+
const DEFAULT_MAX_DOC_TOKENS = 200_000;
|
|
17
|
+
/** Serial by default — OpenRouter cheap/default often 408s under parallel load. */
|
|
18
|
+
const DEFAULT_CHUNK_CONCURRENCY = 1;
|
|
19
|
+
const DEFAULT_CHUNK_RETRIES = 4;
|
|
20
|
+
const DEFAULT_CHUNK_TARGET_TOKENS = 1500;
|
|
21
|
+
/** Wired by platform so chunked AI extract can patch the running step attempt. */
|
|
22
|
+
let stepProgressReporter = null;
|
|
23
|
+
export function setStepProgressReporter(reporter) {
|
|
24
|
+
stepProgressReporter = reporter;
|
|
25
|
+
}
|
|
26
|
+
async function reportStepProgress(req, progress) {
|
|
27
|
+
const attemptId = req.attemptId?.trim();
|
|
28
|
+
if (!attemptId || !stepProgressReporter)
|
|
29
|
+
return;
|
|
30
|
+
try {
|
|
31
|
+
await stepProgressReporter(req.scope.orgId, attemptId, {
|
|
32
|
+
...progress,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
/* progress is best-effort */
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function envInt(name, fallback) {
|
|
40
|
+
const raw = process.env[name]?.trim();
|
|
41
|
+
if (!raw)
|
|
42
|
+
return fallback;
|
|
43
|
+
const n = Number(raw);
|
|
44
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
|
|
45
|
+
}
|
|
13
46
|
export const memorixLiveRing = createTrafficRing();
|
|
14
47
|
/** Outbound Memory connector pulls (opx-source, REST fixtures). */
|
|
15
48
|
export const memoryPullFetch = createInstrumentedFetch(memorixLiveRing, {
|
|
@@ -33,27 +66,56 @@ function resolveAccess(raw) {
|
|
|
33
66
|
return raw.filter((a) => a === "explore" || a === "checkpoints" || a === "raw");
|
|
34
67
|
}
|
|
35
68
|
const aiClient = createAiServicesClient({ fetchImpl: pipelineWorkerFetch });
|
|
69
|
+
/**
|
|
70
|
+
* Paginate + collect knowx catalog for associate.
|
|
71
|
+
* Slim/cap applied later via prepareExistingKnowxForAssociate (same knobs as
|
|
72
|
+
* memorix-knowx-pack associate-ai.ts).
|
|
73
|
+
*/
|
|
36
74
|
async function fetchExistingKnowxForAssociate(memorix) {
|
|
37
75
|
const objectTypes = ["content", "people", "work-items"];
|
|
38
76
|
const out = [];
|
|
77
|
+
const seen = new Set();
|
|
39
78
|
const headers = {
|
|
40
79
|
"x-memorix-org-id": memorix.orgId,
|
|
41
80
|
"x-memorix-agent-ids": memorix.agentIds.join(","),
|
|
42
81
|
};
|
|
43
82
|
if (memorix.grant)
|
|
44
83
|
headers["x-memorix-invocation-grant"] = memorix.grant;
|
|
84
|
+
// Soft upper bound before slim/cap — avoid unbounded Explore walks.
|
|
85
|
+
const hardCap = Math.max(associateExistingCap() * 3, 360);
|
|
45
86
|
for (const objectType of objectTypes) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
let cursor;
|
|
88
|
+
for (let page = 0; page < 20 && out.length < hardCap; page++) {
|
|
89
|
+
try {
|
|
90
|
+
const q = new URLSearchParams({
|
|
91
|
+
objectType,
|
|
92
|
+
contentType: "knowx",
|
|
93
|
+
limit: "200",
|
|
94
|
+
});
|
|
95
|
+
if (cursor)
|
|
96
|
+
q.set("cursor", cursor);
|
|
97
|
+
const url = `${memorix.baseUrl}/api/data/content?${q}`;
|
|
98
|
+
const res = await pipelineWorkerFetch(url, { headers });
|
|
99
|
+
if (!res.ok)
|
|
100
|
+
break;
|
|
101
|
+
const body = (await res.json());
|
|
102
|
+
for (const raw of body.records ?? []) {
|
|
103
|
+
const slim = slimKnowxForAssociate(raw);
|
|
104
|
+
const key = associateDraftKey(slim);
|
|
105
|
+
if (!key || seen.has(key))
|
|
106
|
+
continue;
|
|
107
|
+
seen.add(key);
|
|
108
|
+
out.push(slim ?? raw);
|
|
109
|
+
if (out.length >= hardCap)
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
cursor = body.pageInfo?.nextCursor ?? undefined;
|
|
113
|
+
if (!cursor)
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
57
119
|
}
|
|
58
120
|
}
|
|
59
121
|
return out;
|
|
@@ -92,49 +154,407 @@ function collectKnowxDrafts(inputs) {
|
|
|
92
154
|
}
|
|
93
155
|
return out;
|
|
94
156
|
}
|
|
95
|
-
/**
|
|
96
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Shape KnowX FR `input` for skill invoke.
|
|
159
|
+
* ai-skills@0.3+ maps FR input to root `{{input}}` — do not stamp templateTokens.input.
|
|
160
|
+
*/
|
|
161
|
+
function prepareKnowxSkillInvoke(itemId, serviceId, inputs) {
|
|
97
162
|
const isExtract = serviceId === "knowx-extract" || itemId === "knowx.extract";
|
|
98
163
|
const isAssociate = serviceId === "knowx-associate-knowledge" ||
|
|
99
164
|
itemId === "knowx.associate-knowledge";
|
|
100
|
-
let skillInput = inputs;
|
|
101
165
|
if (isAssociate) {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
166
|
+
const collected = collectKnowxDrafts(inputs);
|
|
167
|
+
const drafts = slimDraftsForAssociate(collected.length
|
|
168
|
+
? collected
|
|
169
|
+
: Array.isArray(inputs.knowxDrafts)
|
|
170
|
+
? inputs.knowxDrafts
|
|
171
|
+
: []);
|
|
172
|
+
const draftKeys = new Set(drafts
|
|
173
|
+
.map((d) => associateDraftKey(d))
|
|
174
|
+
.filter((k) => Boolean(k)));
|
|
175
|
+
const existingRaw = Array.isArray(inputs.existingKnowx)
|
|
176
|
+
? inputs.existingKnowx
|
|
177
|
+
: [];
|
|
178
|
+
const existingKnowx = prepareExistingKnowxForAssociate({
|
|
179
|
+
existing: existingRaw,
|
|
180
|
+
draftKeys,
|
|
181
|
+
cap: associateExistingCap(),
|
|
182
|
+
});
|
|
183
|
+
return { knowxDrafts: drafts, existingKnowx };
|
|
107
184
|
}
|
|
108
|
-
|
|
109
|
-
|
|
185
|
+
if (isExtract) {
|
|
186
|
+
// Chunk fan-out builds a per-chunk bag; keep legacy fields for short docs / fixtures.
|
|
187
|
+
return {
|
|
110
188
|
text: inputs.text,
|
|
111
189
|
knowledgeSource: inputs.knowledgeSource,
|
|
112
190
|
contentId: inputs.contentId,
|
|
113
191
|
contentIds: inputs.contentIds,
|
|
114
192
|
contentDrafts: inputs.contentDrafts,
|
|
193
|
+
chunkText: inputs.chunkText,
|
|
194
|
+
chunkIndex: inputs.chunkIndex,
|
|
195
|
+
chunkCount: inputs.chunkCount,
|
|
196
|
+
contentHash: inputs.contentHash,
|
|
197
|
+
contentRevision: inputs.contentRevision,
|
|
198
|
+
pageStart: inputs.pageStart,
|
|
199
|
+
pageEnd: inputs.pageEnd,
|
|
115
200
|
};
|
|
116
201
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
202
|
+
return inputs;
|
|
203
|
+
}
|
|
204
|
+
function asRecord(v) {
|
|
205
|
+
return v && typeof v === "object" && !Array.isArray(v)
|
|
206
|
+
? v
|
|
207
|
+
: null;
|
|
208
|
+
}
|
|
209
|
+
/** Pull markdown sources for knowx.extract (contentDrafts, promote envelope, or inline text). */
|
|
210
|
+
function collectExtractSources(inputs) {
|
|
211
|
+
const out = [];
|
|
212
|
+
const drafts = [];
|
|
213
|
+
const top = inputs.contentDrafts;
|
|
214
|
+
if (Array.isArray(top))
|
|
215
|
+
drafts.push(...top);
|
|
216
|
+
for (const value of Object.values(inputs)) {
|
|
217
|
+
const o = asRecord(value);
|
|
218
|
+
if (!o)
|
|
219
|
+
continue;
|
|
220
|
+
for (const key of ["value", "body", "data", "output", "result"]) {
|
|
221
|
+
const nested = asRecord(o[key]);
|
|
222
|
+
if (nested && Array.isArray(nested.contentDrafts)) {
|
|
223
|
+
drafts.push(...nested.contentDrafts);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (Array.isArray(o.contentDrafts)) {
|
|
227
|
+
drafts.push(...o.contentDrafts);
|
|
228
|
+
}
|
|
120
229
|
}
|
|
121
|
-
|
|
122
|
-
|
|
230
|
+
const seen = new Set();
|
|
231
|
+
for (const d of drafts) {
|
|
232
|
+
const md = typeof d.markdown === "string" ? d.markdown.trim() : "";
|
|
233
|
+
if (!md)
|
|
234
|
+
continue;
|
|
235
|
+
const key = `${d.contentId ?? ""}:${d.contentHash ?? md.slice(0, 40)}`;
|
|
236
|
+
if (seen.has(key))
|
|
237
|
+
continue;
|
|
238
|
+
seen.add(key);
|
|
239
|
+
out.push({
|
|
240
|
+
contentId: typeof d.contentId === "string" ? d.contentId : undefined,
|
|
241
|
+
contentHash: typeof d.contentHash === "string" ? d.contentHash : undefined,
|
|
242
|
+
contentRevision: typeof d.revision === "number" ? d.revision : undefined,
|
|
243
|
+
title: typeof d.title === "string" ? d.title : undefined,
|
|
244
|
+
markdown: md,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (!out.length) {
|
|
248
|
+
const text = typeof inputs.text === "string" ? inputs.text.trim() : "";
|
|
249
|
+
const ks = asRecord(inputs.knowledgeSource);
|
|
250
|
+
const ksBytes = typeof ks?.bytes === "string" ? String(ks.bytes).trim() : "";
|
|
251
|
+
const markdown = text || ksBytes;
|
|
252
|
+
if (markdown) {
|
|
253
|
+
out.push({
|
|
254
|
+
contentId: typeof inputs.contentId === "string" ? inputs.contentId : undefined,
|
|
255
|
+
markdown,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
function draftKey(draft) {
|
|
262
|
+
if (!draft || typeof draft !== "object")
|
|
263
|
+
return "";
|
|
264
|
+
const d = draft;
|
|
265
|
+
const ot = String(d.objectType ?? "").trim();
|
|
266
|
+
const id = String(d.recordId ?? "").trim();
|
|
267
|
+
if (!ot || !id)
|
|
268
|
+
return "";
|
|
269
|
+
return `${ot}::${id}`;
|
|
270
|
+
}
|
|
271
|
+
function draftConfidence(draft) {
|
|
272
|
+
if (!draft || typeof draft !== "object")
|
|
273
|
+
return 0;
|
|
274
|
+
const data = asRecord(draft.data);
|
|
275
|
+
const epistemic = asRecord(data?.epistemic);
|
|
276
|
+
const score = Number(epistemic?.confidenceScore ?? 0);
|
|
277
|
+
return Number.isFinite(score) ? score : 0;
|
|
278
|
+
}
|
|
279
|
+
/** Merge chunk outputs: dedupe knowxDrafts by objectType::recordId (keep highest confidence). */
|
|
280
|
+
export function mergeKnowxExtractChunkOutputs(parts) {
|
|
281
|
+
const byKey = new Map();
|
|
282
|
+
const untitled = [];
|
|
283
|
+
let docHash = "";
|
|
284
|
+
for (const part of parts) {
|
|
285
|
+
if (typeof part.docHash === "string" && part.docHash && !docHash) {
|
|
286
|
+
docHash = part.docHash;
|
|
287
|
+
}
|
|
288
|
+
const drafts = Array.isArray(part.knowxDrafts) ? part.knowxDrafts : [];
|
|
289
|
+
for (const draft of drafts) {
|
|
290
|
+
const key = draftKey(draft);
|
|
291
|
+
if (!key) {
|
|
292
|
+
untitled.push(draft);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const prev = byKey.get(key);
|
|
296
|
+
if (!prev || draftConfidence(draft) >= draftConfidence(prev)) {
|
|
297
|
+
byKey.set(key, draft);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
123
300
|
}
|
|
124
|
-
const priorTokens = execution.templateTokens && typeof execution.templateTokens === "object"
|
|
125
|
-
? execution.templateTokens
|
|
126
|
-
: {};
|
|
127
301
|
return {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
input: inputJson,
|
|
134
|
-
},
|
|
135
|
-
},
|
|
302
|
+
knowxDrafts: [...byKey.values(), ...untitled],
|
|
303
|
+
...(docHash ? { docHash } : {}),
|
|
304
|
+
kind: "ai-services",
|
|
305
|
+
live: true,
|
|
306
|
+
chunkCount: parts.length,
|
|
136
307
|
};
|
|
137
308
|
}
|
|
309
|
+
async function mapPool(items, concurrency, fn) {
|
|
310
|
+
const out = new Array(items.length);
|
|
311
|
+
let next = 0;
|
|
312
|
+
const workers = Array.from({ length: Math.min(concurrency, Math.max(1, items.length)) }, async () => {
|
|
313
|
+
while (next < items.length) {
|
|
314
|
+
const i = next++;
|
|
315
|
+
out[i] = await fn(items[i], i);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
await Promise.all(workers);
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
function isAbortError(err) {
|
|
322
|
+
if (!err || typeof err !== "object")
|
|
323
|
+
return false;
|
|
324
|
+
const e = err;
|
|
325
|
+
return (e.name === "AbortError" ||
|
|
326
|
+
e.code === "ABORT_ERR" ||
|
|
327
|
+
/aborted|AbortError/i.test(String(e.message ?? "")));
|
|
328
|
+
}
|
|
329
|
+
async function invokeKnowxExtractChunked(req, args) {
|
|
330
|
+
const sources = collectExtractSources(args.inputs);
|
|
331
|
+
if (!sources.length) {
|
|
332
|
+
// No markdown bag — fall through to a single invoke with the prepared bag
|
|
333
|
+
// (activity-log / knowledgeSource fixtures).
|
|
334
|
+
return invokeAiServiceOnce(req, {
|
|
335
|
+
...args,
|
|
336
|
+
invokeInput: prepareKnowxSkillInvoke(args.itemId, args.serviceId, args.inputs),
|
|
337
|
+
correlationSuffix: "extract",
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const maxDocTokens = envInt("KNOWX_AI_MAX_DOC_TOKENS", DEFAULT_MAX_DOC_TOKENS);
|
|
341
|
+
const targetTokens = envInt("KNOWX_AI_CHUNK_TOKENS", DEFAULT_CHUNK_TARGET_TOKENS);
|
|
342
|
+
const concurrency = envInt("KNOWX_AI_CHUNK_CONCURRENCY", DEFAULT_CHUNK_CONCURRENCY);
|
|
343
|
+
const work = [];
|
|
344
|
+
for (const source of sources) {
|
|
345
|
+
const tokens = markdownApproxTokens(source.markdown);
|
|
346
|
+
if (tokens > maxDocTokens) {
|
|
347
|
+
return {
|
|
348
|
+
status: "failed",
|
|
349
|
+
retryable: false,
|
|
350
|
+
error: {
|
|
351
|
+
code: "AI_DOC_TOO_LARGE",
|
|
352
|
+
message: `Document ~${tokens} tokens exceeds KNOWX_AI_MAX_DOC_TOKENS=${maxDocTokens}${source.contentId ? ` (contentId=${source.contentId})` : ""}`,
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const chunks = chunkMarkdown(source.markdown, {
|
|
357
|
+
targetTokens,
|
|
358
|
+
overlapRatio: 0.1,
|
|
359
|
+
});
|
|
360
|
+
if (!chunks.length)
|
|
361
|
+
continue;
|
|
362
|
+
for (const chunk of chunks) {
|
|
363
|
+
work.push({ source, chunk });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!work.length) {
|
|
367
|
+
return {
|
|
368
|
+
status: "failed",
|
|
369
|
+
retryable: false,
|
|
370
|
+
error: {
|
|
371
|
+
code: "AI_EXTRACT_EMPTY",
|
|
372
|
+
message: "knowx.extract found no extractable markdown chunks",
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
// Short single-chunk docs: one invoke (preserve fixture behaviour).
|
|
377
|
+
if (work.length === 1 && work[0].chunk.count === 1) {
|
|
378
|
+
const w = work[0];
|
|
379
|
+
const bag = {
|
|
380
|
+
text: w.chunk.text,
|
|
381
|
+
contentId: w.source.contentId ?? args.inputs.contentId,
|
|
382
|
+
contentIds: w.source.contentId
|
|
383
|
+
? [w.source.contentId]
|
|
384
|
+
: args.inputs.contentIds,
|
|
385
|
+
contentHash: w.source.contentHash,
|
|
386
|
+
contentRevision: w.source.contentRevision,
|
|
387
|
+
chunkText: w.chunk.text,
|
|
388
|
+
chunkIndex: 0,
|
|
389
|
+
chunkCount: 1,
|
|
390
|
+
pageStart: w.chunk.pageStart,
|
|
391
|
+
pageEnd: w.chunk.pageEnd,
|
|
392
|
+
knowledgeSource: args.inputs.knowledgeSource,
|
|
393
|
+
};
|
|
394
|
+
return invokeAiServiceOnce(req, {
|
|
395
|
+
...args,
|
|
396
|
+
invokeInput: prepareKnowxSkillInvoke(args.itemId, args.serviceId, bag),
|
|
397
|
+
correlationSuffix: "c0",
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const retries = envInt("KNOWX_AI_CHUNK_RETRIES", DEFAULT_CHUNK_RETRIES);
|
|
401
|
+
let draftsSoFar = 0;
|
|
402
|
+
let chunksDone = 0;
|
|
403
|
+
await reportStepProgress(req, {
|
|
404
|
+
chunkIndex: 0,
|
|
405
|
+
chunkCount: work.length,
|
|
406
|
+
draftsSoFar: 0,
|
|
407
|
+
message: `Extracting chunk 0/${work.length}`,
|
|
408
|
+
});
|
|
409
|
+
const results = await mapPool(work, concurrency, async (w, i) => {
|
|
410
|
+
const bag = {
|
|
411
|
+
chunkText: w.chunk.text,
|
|
412
|
+
chunkIndex: w.chunk.index,
|
|
413
|
+
chunkCount: w.chunk.count,
|
|
414
|
+
contentId: w.source.contentId,
|
|
415
|
+
contentHash: w.source.contentHash,
|
|
416
|
+
contentRevision: w.source.contentRevision,
|
|
417
|
+
pageStart: w.chunk.pageStart,
|
|
418
|
+
pageEnd: w.chunk.pageEnd,
|
|
419
|
+
title: w.source.title,
|
|
420
|
+
// Prefer chunkText in the prompt; keep text as the chunk for {{input}} templates.
|
|
421
|
+
text: w.chunk.text,
|
|
422
|
+
};
|
|
423
|
+
let last;
|
|
424
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
425
|
+
last = await invokeAiServiceOnce(req, {
|
|
426
|
+
...args,
|
|
427
|
+
invokeInput: prepareKnowxSkillInvoke(args.itemId, args.serviceId, bag),
|
|
428
|
+
correlationSuffix: `c${w.chunk.index}-${i}:a${attempt}`,
|
|
429
|
+
timeoutMs: 180_000,
|
|
430
|
+
});
|
|
431
|
+
if (last.status === "succeeded") {
|
|
432
|
+
const part = (last.output ?? {});
|
|
433
|
+
const drafts = Array.isArray(part.knowxDrafts) ? part.knowxDrafts.length : 0;
|
|
434
|
+
chunksDone += 1;
|
|
435
|
+
draftsSoFar += drafts;
|
|
436
|
+
await reportStepProgress(req, {
|
|
437
|
+
chunkIndex: chunksDone,
|
|
438
|
+
chunkCount: work.length,
|
|
439
|
+
draftsSoFar,
|
|
440
|
+
message: `Extracting chunk ${chunksDone}/${work.length}`,
|
|
441
|
+
});
|
|
442
|
+
return last;
|
|
443
|
+
}
|
|
444
|
+
const msg = `${last.error?.code ?? ""} ${last.error?.message ?? ""}`;
|
|
445
|
+
const retryable = last.retryable || /timeout|408|429|REMOTE_UNREACHABLE|INVOKE_FAILED/i.test(msg);
|
|
446
|
+
if (!retryable || attempt >= retries)
|
|
447
|
+
return last;
|
|
448
|
+
await new Promise((r) => setTimeout(r, 1500 * (attempt + 1)));
|
|
449
|
+
}
|
|
450
|
+
return last;
|
|
451
|
+
});
|
|
452
|
+
const succeeded = [];
|
|
453
|
+
for (const r of results) {
|
|
454
|
+
if (r.status !== "succeeded" || !r.output) {
|
|
455
|
+
return {
|
|
456
|
+
status: "failed",
|
|
457
|
+
retryable: r.retryable ?? false,
|
|
458
|
+
error: r.error ?? {
|
|
459
|
+
code: "AI_EXTRACT_CHUNK_FAILED",
|
|
460
|
+
message: "knowx.extract chunk invoke failed",
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
succeeded.push(r.output);
|
|
465
|
+
}
|
|
466
|
+
const merged = mergeKnowxExtractChunkOutputs(succeeded);
|
|
467
|
+
if (!Array.isArray(merged.knowxDrafts) || merged.knowxDrafts.length === 0) {
|
|
468
|
+
return {
|
|
469
|
+
status: "failed",
|
|
470
|
+
retryable: false,
|
|
471
|
+
error: {
|
|
472
|
+
code: "AI_OUTPUT_MISSING_DRAFTS",
|
|
473
|
+
message: "knowx.extract chunk fan-out produced no knowxDrafts after merge",
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
await reportStepProgress(req, {
|
|
478
|
+
chunkIndex: work.length,
|
|
479
|
+
chunkCount: work.length,
|
|
480
|
+
draftsSoFar: Array.isArray(merged.knowxDrafts)
|
|
481
|
+
? merged.knowxDrafts.length
|
|
482
|
+
: draftsSoFar,
|
|
483
|
+
message: "Extract complete",
|
|
484
|
+
});
|
|
485
|
+
return { status: "succeeded", output: merged };
|
|
486
|
+
}
|
|
487
|
+
async function invokeAiServiceOnce(req, args) {
|
|
488
|
+
const correlationId = `${req.runId}:${req.stepId}:${req.attempt}:${args.correlationSuffix}`;
|
|
489
|
+
try {
|
|
490
|
+
const { status, body } = await aiClient.invoke(args.endpoint, {
|
|
491
|
+
itemId: args.itemId,
|
|
492
|
+
input: args.invokeInput,
|
|
493
|
+
execution: Object.keys(args.execution).length
|
|
494
|
+
? args.execution
|
|
495
|
+
: undefined,
|
|
496
|
+
correlationId,
|
|
497
|
+
}, correlationId, args.timeoutMs);
|
|
498
|
+
if (status < 200 || status >= 300 || body.ok === false) {
|
|
499
|
+
return {
|
|
500
|
+
status: "failed",
|
|
501
|
+
retryable: Boolean(body.error?.retryable),
|
|
502
|
+
error: {
|
|
503
|
+
code: body.error?.code ?? "AI_SERVICE_INVOKE_FAILED",
|
|
504
|
+
message: body.error?.message ?? `HTTP ${status}`,
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
const normalized = normalizeAiServiceStepOutput(body, {
|
|
509
|
+
itemId: args.itemId,
|
|
510
|
+
serviceId: args.serviceId,
|
|
511
|
+
});
|
|
512
|
+
if (!normalized.ok) {
|
|
513
|
+
try {
|
|
514
|
+
const dumpPath = `/tmp/knowx-ai-unparseable-${args.correlationSuffix}-${Date.now()}.json`;
|
|
515
|
+
const { writeFileSync } = await import("node:fs");
|
|
516
|
+
writeFileSync(dumpPath, JSON.stringify({
|
|
517
|
+
itemId: args.itemId,
|
|
518
|
+
serviceId: args.serviceId,
|
|
519
|
+
correlationSuffix: args.correlationSuffix,
|
|
520
|
+
code: normalized.code,
|
|
521
|
+
message: normalized.message,
|
|
522
|
+
body,
|
|
523
|
+
}, null, 2));
|
|
524
|
+
console.error(`[ai-services] unparseable dump ${dumpPath}`);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
/* ignore dump errors */
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
status: "failed",
|
|
531
|
+
retryable: normalized.code === "AI_OUTPUT_EMPTY",
|
|
532
|
+
error: { code: normalized.code, message: normalized.message },
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
return { status: "succeeded", output: normalized.output };
|
|
536
|
+
}
|
|
537
|
+
catch (err) {
|
|
538
|
+
if (isAbortError(err)) {
|
|
539
|
+
return {
|
|
540
|
+
status: "failed",
|
|
541
|
+
retryable: true,
|
|
542
|
+
error: {
|
|
543
|
+
code: "AI_EXTRACT_TIMEOUT",
|
|
544
|
+
message: err instanceof Error ? err.message : "AI extract timed out",
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
status: "failed",
|
|
550
|
+
retryable: true,
|
|
551
|
+
error: {
|
|
552
|
+
code: "REMOTE_UNREACHABLE",
|
|
553
|
+
message: err instanceof Error ? err.message : String(err),
|
|
554
|
+
},
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|
|
138
558
|
async function invokeAiServiceRemote(req, memorix) {
|
|
139
559
|
const c = (req.config ?? {});
|
|
140
560
|
const endpoint = String(c.endpoint ?? "").replace(/\/+$/, "");
|
|
@@ -155,7 +575,6 @@ async function invokeAiServiceRemote(req, memorix) {
|
|
|
155
575
|
error: { code: "AI_ITEM_MISSING", message: "itemId required" },
|
|
156
576
|
};
|
|
157
577
|
}
|
|
158
|
-
const correlationId = `${req.runId}:${req.stepId}:${req.attempt}`;
|
|
159
578
|
const inputs = {
|
|
160
579
|
...(req.inputs && typeof req.inputs === "object"
|
|
161
580
|
? req.inputs
|
|
@@ -165,7 +584,8 @@ async function invokeAiServiceRemote(req, memorix) {
|
|
|
165
584
|
if ((serviceId === "knowx-associate-knowledge" ||
|
|
166
585
|
itemId === "knowx.associate-knowledge") &&
|
|
167
586
|
memorix &&
|
|
168
|
-
inputs.existingKnowx === undefined
|
|
587
|
+
(inputs.existingKnowx === undefined ||
|
|
588
|
+
(Array.isArray(inputs.existingKnowx) && inputs.existingKnowx.length === 0))) {
|
|
169
589
|
const existing = await fetchExistingKnowxForAssociate(memorix);
|
|
170
590
|
if (existing.length)
|
|
171
591
|
inputs.existingKnowx = existing;
|
|
@@ -179,57 +599,156 @@ async function invokeAiServiceRemote(req, memorix) {
|
|
|
179
599
|
stepId: req.stepId,
|
|
180
600
|
})
|
|
181
601
|
: undefined;
|
|
182
|
-
|
|
602
|
+
const execution = {
|
|
183
603
|
...skillDefaults,
|
|
184
604
|
...configExec,
|
|
185
605
|
};
|
|
186
|
-
|
|
606
|
+
const isKnowxExtract = serviceId === "knowx-extract" || itemId === "knowx.extract";
|
|
607
|
+
const isKnowxAssociate = serviceId === "knowx-associate-knowledge" ||
|
|
608
|
+
itemId === "knowx.associate-knowledge";
|
|
187
609
|
if (aiServiceType === "skill" &&
|
|
610
|
+
isKnowxExtract &&
|
|
188
611
|
(itemId.startsWith("knowx.") || serviceId.startsWith("knowx-"))) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
612
|
+
return invokeKnowxExtractChunked(req, {
|
|
613
|
+
endpoint,
|
|
614
|
+
itemId,
|
|
615
|
+
serviceId,
|
|
616
|
+
execution,
|
|
617
|
+
inputs,
|
|
618
|
+
});
|
|
192
619
|
}
|
|
193
|
-
|
|
194
|
-
|
|
620
|
+
if (aiServiceType === "skill" &&
|
|
621
|
+
isKnowxAssociate &&
|
|
622
|
+
(itemId.startsWith("knowx.") || serviceId.startsWith("knowx-"))) {
|
|
623
|
+
return invokeKnowxAssociateChunked(req, {
|
|
624
|
+
endpoint,
|
|
195
625
|
itemId,
|
|
196
|
-
|
|
197
|
-
execution
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
626
|
+
serviceId,
|
|
627
|
+
execution,
|
|
628
|
+
inputs,
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
let invokeInput = inputs;
|
|
632
|
+
if (aiServiceType === "skill" &&
|
|
633
|
+
(itemId.startsWith("knowx.") || serviceId.startsWith("knowx-"))) {
|
|
634
|
+
invokeInput = prepareKnowxSkillInvoke(itemId, serviceId, inputs);
|
|
635
|
+
}
|
|
636
|
+
return invokeAiServiceOnce(req, {
|
|
637
|
+
endpoint,
|
|
638
|
+
itemId,
|
|
639
|
+
serviceId,
|
|
640
|
+
execution,
|
|
641
|
+
invokeInput,
|
|
642
|
+
correlationSuffix: "once",
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Fan-out knowx.associate-knowledge when draft count exceeds chunk size.
|
|
647
|
+
* Mirrors memorix-knowx-pack associate-ai draft chunking.
|
|
648
|
+
*/
|
|
649
|
+
async function invokeKnowxAssociateChunked(req, args) {
|
|
650
|
+
const prepared = prepareKnowxSkillInvoke(args.itemId, args.serviceId, args.inputs);
|
|
651
|
+
const drafts = Array.isArray(prepared.knowxDrafts)
|
|
652
|
+
? prepared.knowxDrafts
|
|
653
|
+
: [];
|
|
654
|
+
const existingKnowx = Array.isArray(prepared.existingKnowx)
|
|
655
|
+
? prepared.existingKnowx
|
|
656
|
+
: [];
|
|
657
|
+
const chunks = chunkArray(drafts, associateDraftChunkSize());
|
|
658
|
+
const outputs = [];
|
|
659
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
660
|
+
const chunkDrafts = chunks[i];
|
|
661
|
+
await reportStepProgress(req, {
|
|
662
|
+
chunkIndex: i + 1,
|
|
663
|
+
chunkCount: chunks.length,
|
|
664
|
+
message: `Associate chunk ${i + 1}/${chunks.length}`,
|
|
665
|
+
});
|
|
666
|
+
let once = await invokeAiServiceOnce(req, {
|
|
667
|
+
endpoint: args.endpoint,
|
|
668
|
+
itemId: args.itemId,
|
|
669
|
+
serviceId: args.serviceId,
|
|
670
|
+
execution: args.execution,
|
|
671
|
+
invokeInput: {
|
|
672
|
+
knowxDrafts: chunkDrafts,
|
|
673
|
+
existingKnowx,
|
|
674
|
+
},
|
|
675
|
+
correlationSuffix: `assoc-${i + 1}-of-${chunks.length}`,
|
|
676
|
+
});
|
|
677
|
+
// Retries: empty companion responses are flaky on deepseek-v4-flash.
|
|
678
|
+
const retriable = (code) => code === "AI_OUTPUT_UNPARSEABLE" ||
|
|
679
|
+
code === "AI_OUTPUT_MISSING_ASSOCIATE" ||
|
|
680
|
+
code === "AI_OUTPUT_EMPTY";
|
|
681
|
+
for (let attempt = 0; attempt < 2 && once.status !== "succeeded"; attempt++) {
|
|
682
|
+
if (!retriable(once.error?.code))
|
|
683
|
+
break;
|
|
684
|
+
once = await invokeAiServiceOnce(req, {
|
|
685
|
+
endpoint: args.endpoint,
|
|
686
|
+
itemId: args.itemId,
|
|
687
|
+
serviceId: args.serviceId,
|
|
688
|
+
execution: args.execution,
|
|
689
|
+
invokeInput: {
|
|
690
|
+
knowxDrafts: chunkDrafts,
|
|
691
|
+
existingKnowx,
|
|
207
692
|
},
|
|
208
|
-
|
|
693
|
+
correlationSuffix: `assoc-${i + 1}-of-${chunks.length}-retry${attempt + 1}`,
|
|
694
|
+
});
|
|
209
695
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
696
|
+
if (once.status !== "succeeded" && retriable(once.error?.code)) {
|
|
697
|
+
const shrunk = existingKnowx.length > 4
|
|
698
|
+
? existingKnowx.slice(0, Math.max(4, Math.floor(existingKnowx.length / 2)))
|
|
699
|
+
: existingKnowx;
|
|
700
|
+
once = await invokeAiServiceOnce(req, {
|
|
701
|
+
endpoint: args.endpoint,
|
|
702
|
+
itemId: args.itemId,
|
|
703
|
+
serviceId: args.serviceId,
|
|
704
|
+
execution: args.execution,
|
|
705
|
+
invokeInput: {
|
|
706
|
+
knowxDrafts: chunkDrafts,
|
|
707
|
+
existingKnowx: shrunk,
|
|
708
|
+
},
|
|
709
|
+
correlationSuffix: `assoc-${i + 1}-of-${chunks.length}-shrink`,
|
|
710
|
+
});
|
|
217
711
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
712
|
+
// Extra retries with full catalog — empty responses are intermittent.
|
|
713
|
+
for (let attempt = 0; attempt < 3 && once.status !== "succeeded" && retriable(once.error?.code); attempt++) {
|
|
714
|
+
once = await invokeAiServiceOnce(req, {
|
|
715
|
+
endpoint: args.endpoint,
|
|
716
|
+
itemId: args.itemId,
|
|
717
|
+
serviceId: args.serviceId,
|
|
718
|
+
execution: args.execution,
|
|
719
|
+
invokeInput: {
|
|
720
|
+
knowxDrafts: chunkDrafts,
|
|
721
|
+
existingKnowx,
|
|
722
|
+
},
|
|
723
|
+
correlationSuffix: `assoc-${i + 1}-of-${chunks.length}-persist${attempt + 1}`,
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
if (once.status !== "succeeded") {
|
|
727
|
+
return once;
|
|
728
|
+
}
|
|
729
|
+
const out = once.output && typeof once.output === "object"
|
|
730
|
+
? once.output
|
|
731
|
+
: {};
|
|
732
|
+
outputs.push(out);
|
|
222
733
|
}
|
|
223
|
-
|
|
734
|
+
const merged = mergeAssociateOutputs(outputs);
|
|
735
|
+
if (!asRecord(merged.classification) &&
|
|
736
|
+
!Array.isArray(merged.relationshipPlans)) {
|
|
224
737
|
return {
|
|
225
738
|
status: "failed",
|
|
226
|
-
retryable:
|
|
739
|
+
retryable: false,
|
|
227
740
|
error: {
|
|
228
|
-
code: "
|
|
229
|
-
message:
|
|
741
|
+
code: "AI_OUTPUT_MISSING_ASSOCIATE",
|
|
742
|
+
message: "knowx.associate-knowledge output missing classification / relationshipPlans",
|
|
230
743
|
},
|
|
231
744
|
};
|
|
232
745
|
}
|
|
746
|
+
await reportStepProgress(req, {
|
|
747
|
+
chunkIndex: chunks.length,
|
|
748
|
+
chunkCount: chunks.length,
|
|
749
|
+
message: "Associate complete",
|
|
750
|
+
});
|
|
751
|
+
return { status: "succeeded", output: merged };
|
|
233
752
|
}
|
|
234
753
|
const baseServiceInvokePorts = createServiceInvokePorts({
|
|
235
754
|
remoteFetch: pipelineWorkerFetch,
|
|
@@ -262,6 +781,9 @@ export const serviceInvokePorts = {
|
|
|
262
781
|
invokeInProcess: baseServiceInvokePorts.invokeInProcess,
|
|
263
782
|
invokeRemote: async (req) => {
|
|
264
783
|
const c = (req.config ?? {});
|
|
784
|
+
if (c.invokeProtocol === "memorix-destination/1") {
|
|
785
|
+
return invokeDestinationDeliver(req);
|
|
786
|
+
}
|
|
265
787
|
if (c.invokeProtocol === "ai-services/1" ||
|
|
266
788
|
(typeof c.aiServiceType === "string" &&
|
|
267
789
|
c.aiServiceType &&
|
|
@@ -292,4 +814,109 @@ export const serviceInvokePorts = {
|
|
|
292
814
|
return baseServiceInvokePorts.invokeRemote(req);
|
|
293
815
|
},
|
|
294
816
|
};
|
|
817
|
+
function inventDeliverItems(inputs) {
|
|
818
|
+
if (!inputs || typeof inputs !== "object")
|
|
819
|
+
return [];
|
|
820
|
+
const o = inputs;
|
|
821
|
+
if (Array.isArray(o.items)) {
|
|
822
|
+
return o.items.filter((x) => Boolean(x) && typeof x === "object");
|
|
823
|
+
}
|
|
824
|
+
if (Array.isArray(o.records)) {
|
|
825
|
+
return o.records
|
|
826
|
+
.filter((x) => Boolean(x) && typeof x === "object")
|
|
827
|
+
.map((r) => ({
|
|
828
|
+
recordId: typeof r.recordId === "string" ? r.recordId : typeof r.id === "string" ? r.id : undefined,
|
|
829
|
+
objectType: typeof r.objectType === "string" ? r.objectType : undefined,
|
|
830
|
+
contentType: typeof r.contentType === "string" ? r.contentType : undefined,
|
|
831
|
+
payload: (r.payload && typeof r.payload === "object"
|
|
832
|
+
? r.payload
|
|
833
|
+
: r),
|
|
834
|
+
identity: typeof r.identity === "string" ? r.identity : undefined,
|
|
835
|
+
}));
|
|
836
|
+
}
|
|
837
|
+
// Single-item envelope from work selector.
|
|
838
|
+
if (typeof o.recordId === "string" || typeof o.id === "string" || o.payload) {
|
|
839
|
+
return [
|
|
840
|
+
{
|
|
841
|
+
recordId: typeof o.recordId === "string"
|
|
842
|
+
? o.recordId
|
|
843
|
+
: typeof o.id === "string"
|
|
844
|
+
? o.id
|
|
845
|
+
: undefined,
|
|
846
|
+
objectType: typeof o.objectType === "string" ? o.objectType : undefined,
|
|
847
|
+
contentType: typeof o.contentType === "string" ? o.contentType : undefined,
|
|
848
|
+
payload: o.payload && typeof o.payload === "object"
|
|
849
|
+
? o.payload
|
|
850
|
+
: o,
|
|
851
|
+
identity: typeof o.identity === "string" ? o.identity : undefined,
|
|
852
|
+
},
|
|
853
|
+
];
|
|
854
|
+
}
|
|
855
|
+
return [];
|
|
856
|
+
}
|
|
857
|
+
async function invokeDestinationDeliver(req) {
|
|
858
|
+
const c = (req.config ?? {});
|
|
859
|
+
const destinationId = String(c.destinationId ?? "").trim();
|
|
860
|
+
if (!destinationId) {
|
|
861
|
+
return {
|
|
862
|
+
status: "failed",
|
|
863
|
+
retryable: false,
|
|
864
|
+
error: {
|
|
865
|
+
code: "DESTINATION_ID_MISSING",
|
|
866
|
+
message: "destinationId required for memorix-destination/1",
|
|
867
|
+
},
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
const items = inventDeliverItems(req.inputs);
|
|
871
|
+
if (!items.length) {
|
|
872
|
+
return {
|
|
873
|
+
status: "failed",
|
|
874
|
+
retryable: false,
|
|
875
|
+
error: {
|
|
876
|
+
code: "DELIVER_ITEMS_MISSING",
|
|
877
|
+
message: "items, records, or a single item payload required for destination deliver",
|
|
878
|
+
},
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
const receipt = await deliverViaPlatform({
|
|
883
|
+
orgId: req.scope.orgId,
|
|
884
|
+
destinationId,
|
|
885
|
+
items,
|
|
886
|
+
workRef: `${req.runId}:${req.stepId}`,
|
|
887
|
+
mode: c.mode === "context" || c.mode === "knowledge"
|
|
888
|
+
? c.mode
|
|
889
|
+
: undefined,
|
|
890
|
+
});
|
|
891
|
+
if (receipt.status === "failed") {
|
|
892
|
+
return {
|
|
893
|
+
status: "failed",
|
|
894
|
+
retryable: false,
|
|
895
|
+
error: {
|
|
896
|
+
code: receipt.failureSummary?.code ?? "MEMORY_DELIVER_FAILED",
|
|
897
|
+
message: receipt.failureSummary?.message ??
|
|
898
|
+
`deliver to ${destinationId} failed`,
|
|
899
|
+
},
|
|
900
|
+
output: receipt,
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
status: "succeeded",
|
|
905
|
+
output: receipt,
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
catch (err) {
|
|
909
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
910
|
+
return {
|
|
911
|
+
status: "failed",
|
|
912
|
+
retryable: false,
|
|
913
|
+
error: {
|
|
914
|
+
code: message.includes("MEMORY_DELIVERER_UNAVAILABLE")
|
|
915
|
+
? "MEMORY_DELIVERER_UNAVAILABLE"
|
|
916
|
+
: "MEMORY_DELIVER_ERROR",
|
|
917
|
+
message,
|
|
918
|
+
},
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
}
|
|
295
922
|
//# sourceMappingURL=live.js.map
|