stratagate-dsh 0.2.63 → 0.2.65
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/CHANGELOG.md +350 -337
- package/README.md +447 -447
- package/README.zh-CN.md +412 -412
- package/cordis.patch.yml +11 -11
- package/dist/client.d.ts +1 -1
- package/dist/client.js +10 -9
- package/dist/plugin.js +223 -70
- package/dist/plugin.js.map +1 -1
- package/dist/repair-profile.js +0 -0
- package/docs/ARCHITECTURE.md +215 -215
- package/docs/DSH.md +192 -192
- package/docs/DSH.zh-CN.md +189 -189
- package/docs/EVALUATION.md +179 -179
- package/package.json +225 -225
package/dist/plugin.js
CHANGED
|
@@ -2752,6 +2752,7 @@ var StrataGate = class _StrataGate {
|
|
|
2752
2752
|
summaryJobs = /* @__PURE__ */ new Map();
|
|
2753
2753
|
elementProjectionJobs = /* @__PURE__ */ new Map();
|
|
2754
2754
|
graphProjectionJobs = /* @__PURE__ */ new Map();
|
|
2755
|
+
manualRetryRuns = /* @__PURE__ */ new Map();
|
|
2755
2756
|
usageReceipts = /* @__PURE__ */ new Map();
|
|
2756
2757
|
successfulModelResponses = [];
|
|
2757
2758
|
ingestionReceipts = /* @__PURE__ */ new Map();
|
|
@@ -2997,34 +2998,90 @@ var StrataGate = class _StrataGate {
|
|
|
2997
2998
|
listSummaryJobs() {
|
|
2998
2999
|
return [...this.summaryJobs.values()];
|
|
2999
3000
|
}
|
|
3000
|
-
|
|
3001
|
+
runManualRetry(key, operation) {
|
|
3002
|
+
const existing = this.manualRetryRuns.get(key);
|
|
3003
|
+
if (existing) return existing;
|
|
3004
|
+
const run = operation().finally(() => {
|
|
3005
|
+
if (this.manualRetryRuns.get(key) === run) this.manualRetryRuns.delete(key);
|
|
3006
|
+
});
|
|
3007
|
+
this.manualRetryRuns.set(key, run);
|
|
3008
|
+
return run;
|
|
3009
|
+
}
|
|
3010
|
+
/** Give one failed Block Summary job a fresh, user-requested retry budget. */
|
|
3001
3011
|
async retryBlockSummary(id) {
|
|
3002
3012
|
const blockId = id.trim();
|
|
3003
3013
|
if (!blockId) throw new TypeError("Block id must not be empty");
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
throw new Error(`Block ${blockId} Summary
|
|
3014
|
+
return this.runManualRetry(`summary:${blockId}`, async () => {
|
|
3015
|
+
const block = this.blocks.find((candidate) => candidate.id === blockId);
|
|
3016
|
+
if (!block) throw new Error(`Unknown block: ${blockId}`);
|
|
3017
|
+
if (block.processingStatus !== "pending") throw new Error(`Block ${blockId} is already ready`);
|
|
3018
|
+
await this.commitMutation(() => {
|
|
3019
|
+
const job = this.summaryJobs.get(blockId);
|
|
3020
|
+
if (!job) throw new Error(`Missing summary job for block: ${blockId}`);
|
|
3021
|
+
if (job.status !== "failed") throw new Error(`Block ${blockId} Summary is ${job.status}, not failed`);
|
|
3022
|
+
this.summaryJobs.set(blockId, {
|
|
3023
|
+
...job,
|
|
3024
|
+
status: "pending",
|
|
3025
|
+
attempts: 0,
|
|
3026
|
+
lastError: null,
|
|
3027
|
+
nextRetryAt: null,
|
|
3028
|
+
updatedAt: toUtc8Iso(this.now())
|
|
3029
|
+
});
|
|
3030
|
+
});
|
|
3031
|
+
const extractedEvents = await this.processBlock(block, { retryFailed: false });
|
|
3032
|
+
const processed = this.blocks.find((candidate) => candidate.id === blockId);
|
|
3033
|
+
const readyBlocks = processed?.processingStatus === "ready" ? [processed] : [];
|
|
3034
|
+
return { sealedBlocks: [], readyBlocks, extractedEvents, projectedElements: [] };
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
3037
|
+
/** Retry only Event extraction for one failed Block without rerunning its Summary. */
|
|
3038
|
+
async retryEventExtraction(id) {
|
|
3039
|
+
const blockId = id.trim();
|
|
3040
|
+
if (!blockId) throw new TypeError("Block id must not be empty");
|
|
3041
|
+
return this.runManualRetry(`extraction:${blockId}`, async () => {
|
|
3042
|
+
const block = this.blocks.find((candidate) => candidate.id === blockId);
|
|
3043
|
+
if (!block) throw new Error(`Unknown block: ${blockId}`);
|
|
3044
|
+
if (block.processingStatus !== "pending" || block.shouldExtract !== true) {
|
|
3045
|
+
throw new Error(`Block ${blockId} is not waiting for Event extraction`);
|
|
3012
3046
|
}
|
|
3013
|
-
this.
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3047
|
+
await this.commitMutation(() => {
|
|
3048
|
+
const job = this.extractionJobs.get(blockId);
|
|
3049
|
+
if (!job) throw new Error(`Missing extraction job for block: ${blockId}`);
|
|
3050
|
+
if (job.status !== "failed") throw new Error(`Event extraction ${blockId} is ${job.status}, not failed`);
|
|
3051
|
+
this.extractionJobs.set(blockId, {
|
|
3052
|
+
...job,
|
|
3053
|
+
attempts: 0,
|
|
3054
|
+
lastError: null,
|
|
3055
|
+
nextRetryAt: null,
|
|
3056
|
+
updatedAt: toUtc8Iso(this.now())
|
|
3057
|
+
});
|
|
3020
3058
|
});
|
|
3059
|
+
return await this.extractEligibleBlock({ blockId, retryFailed: true }) ?? [];
|
|
3060
|
+
});
|
|
3061
|
+
}
|
|
3062
|
+
/** Retry exactly one failed Graph projection batch. */
|
|
3063
|
+
async retryGraphProjection(id) {
|
|
3064
|
+
const jobId = id.trim();
|
|
3065
|
+
if (!jobId) throw new TypeError("Graph projection id must not be empty");
|
|
3066
|
+
return this.runManualRetry(`graph:${jobId}`, async () => {
|
|
3067
|
+
if (!this.graphProjector) throw new Error("Graph projector is not configured");
|
|
3068
|
+
await this.commitMutation(() => {
|
|
3069
|
+
const job = this.requireGraphProjectionJob(jobId);
|
|
3070
|
+
if (job.status !== "failed") throw new Error(`Graph projection ${jobId} is ${job.status}, not failed`);
|
|
3071
|
+
job.status = "pending";
|
|
3072
|
+
job.attempts = 0;
|
|
3073
|
+
job.lastError = null;
|
|
3074
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3075
|
+
});
|
|
3076
|
+
const batch = await this.claimGraphProjection(jobId);
|
|
3077
|
+
if (!batch) throw new Error(`Graph projection ${jobId} could not be claimed`);
|
|
3078
|
+
try {
|
|
3079
|
+
return await this.completeGraphProjection(jobId, await this.graphProjector(batch));
|
|
3080
|
+
} catch (error) {
|
|
3081
|
+
await this.failGraphProjection(jobId, error);
|
|
3082
|
+
throw error;
|
|
3083
|
+
}
|
|
3021
3084
|
});
|
|
3022
|
-
const extractedEvents = await this.processBlock(block, { retryFailed: false });
|
|
3023
|
-
const processed = this.blocks.find((candidate) => candidate.id === blockId);
|
|
3024
|
-
const readyBlocks = processed?.processingStatus === "ready" ? [processed] : [];
|
|
3025
|
-
const projectedElements = await this.projectEligibleElements() ?? [];
|
|
3026
|
-
await this.projectEligibleGraph();
|
|
3027
|
-
return { sealedBlocks: [], readyBlocks, extractedEvents, projectedElements };
|
|
3028
3085
|
}
|
|
3029
3086
|
listElementProjectionJobs() {
|
|
3030
3087
|
return [...this.elementProjectionJobs.values()];
|
|
@@ -3664,9 +3721,9 @@ var StrataGate = class _StrataGate {
|
|
|
3664
3721
|
job.updatedAt = toUtc8Iso(this.now());
|
|
3665
3722
|
});
|
|
3666
3723
|
}
|
|
3667
|
-
async
|
|
3724
|
+
async claimGraphProjection(jobId) {
|
|
3668
3725
|
return this.commitMutation(() => {
|
|
3669
|
-
const job = [...this.graphProjectionJobs.values()].filter((candidate) => candidate.status === "pending" || candidate.status === "failed").sort((left, right) => right.priority - left.priority || left.createdAt.localeCompare(right.createdAt))[0];
|
|
3726
|
+
const job = [...this.graphProjectionJobs.values()].filter((candidate) => (jobId === void 0 || candidate.id === jobId) && (candidate.status === "pending" || candidate.status === "failed")).sort((left, right) => right.priority - left.priority || left.createdAt.localeCompare(right.createdAt))[0];
|
|
3670
3727
|
if (!job) return null;
|
|
3671
3728
|
const events = job.sourceEventIds.flatMap((id) => this.events.find((event) => event.id === id) ?? []);
|
|
3672
3729
|
if (events.length === 0) throw new Error(`Graph projection ${job.id} has no available source events`);
|
|
@@ -3692,6 +3749,9 @@ var StrataGate = class _StrataGate {
|
|
|
3692
3749
|
};
|
|
3693
3750
|
});
|
|
3694
3751
|
}
|
|
3752
|
+
async claimNextGraphProjection() {
|
|
3753
|
+
return this.claimGraphProjection();
|
|
3754
|
+
}
|
|
3695
3755
|
async completeGraphProjection(jobId, result) {
|
|
3696
3756
|
return this.commitMutation(() => {
|
|
3697
3757
|
const job = this.requireGraphProjectionJob(jobId);
|
|
@@ -5716,6 +5776,7 @@ var StrataGateRuntime = class {
|
|
|
5716
5776
|
pendingSurfaceSync = /* @__PURE__ */ new Map();
|
|
5717
5777
|
adminSnapshotCache = /* @__PURE__ */ new Map();
|
|
5718
5778
|
externalImportRuns = /* @__PURE__ */ new Map();
|
|
5779
|
+
adminJobRetryRuns = /* @__PURE__ */ new Map();
|
|
5719
5780
|
feedbackDrafts = /* @__PURE__ */ new Map();
|
|
5720
5781
|
pendingFeedbackSuggestionSessions = /* @__PURE__ */ new Set();
|
|
5721
5782
|
pendingTurns = /* @__PURE__ */ new Map();
|
|
@@ -6715,34 +6776,53 @@ var StrataGateRuntime = class {
|
|
|
6715
6776
|
});
|
|
6716
6777
|
return update;
|
|
6717
6778
|
}
|
|
6718
|
-
async
|
|
6779
|
+
async adminRetryJob(namespace, kind, id) {
|
|
6719
6780
|
const key = namespace.trim();
|
|
6720
|
-
const
|
|
6781
|
+
const jobId = id.trim();
|
|
6721
6782
|
if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
|
|
6722
|
-
if (!
|
|
6783
|
+
if (!jobId) throw new TypeError("StrataGate job id must not be empty");
|
|
6784
|
+
const retryKey = `${key}\0${kind}\0${jobId}`;
|
|
6785
|
+
const existing = this.adminJobRetryRuns.get(retryKey);
|
|
6786
|
+
if (existing) return existing;
|
|
6723
6787
|
const update = this.settingsTail.catch(() => {
|
|
6724
6788
|
}).then(async () => {
|
|
6725
6789
|
await this.flush();
|
|
6726
6790
|
const { memory, owned } = await this.openAdminMemory(key, { derivation: true });
|
|
6727
6791
|
try {
|
|
6728
6792
|
await this.refreshExternalImportMemory(key, memory);
|
|
6729
|
-
const
|
|
6730
|
-
|
|
6731
|
-
const
|
|
6732
|
-
|
|
6733
|
-
|
|
6793
|
+
const graphJob = kind === "graph-projection" ? memory.listGraphProjectionJobs().find((candidate) => candidate.id === jobId) : void 0;
|
|
6794
|
+
const blockId = kind === "graph-projection" ? graphJob?.sourceEventIds.flatMap((eventId) => memory.listEvents().find(({ id: eventCandidateId }) => eventCandidateId === eventId)?.sourceBlockId ?? [])[0] : jobId;
|
|
6795
|
+
const block = blockId ? memory.listBlocks().find((candidate) => candidate.id === blockId) : void 0;
|
|
6796
|
+
if (kind !== "graph-projection" && !block) throw new Error(`Unknown block: ${jobId}`);
|
|
6797
|
+
if (kind === "graph-projection" && !graphJob) throw new Error(`Unknown graph projection: ${jobId}`);
|
|
6798
|
+
const threadId = block?.threadId ?? `admin-job-retry:${kind}:${jobId}`;
|
|
6799
|
+
const session = block?.threadId ? this.knownSessions.get(block.threadId)?.deref() : void 0;
|
|
6800
|
+
const retry = async () => {
|
|
6801
|
+
if (kind === "block-summary") return memory.retryBlockSummary(jobId);
|
|
6802
|
+
if (kind === "event-extraction") return memory.retryEventExtraction(jobId);
|
|
6803
|
+
return memory.retryGraphProjection(jobId);
|
|
6804
|
+
};
|
|
6805
|
+
const result = session && this.namespaceFor(session) === key ? await this.models.run(session, retry) : await this.models.runDetached(threadId, retry);
|
|
6734
6806
|
await this.persistSuccessfulResponses(memory);
|
|
6735
|
-
|
|
6736
|
-
|
|
6807
|
+
const currentBlock = blockId ? memory.listBlocks().find(({ id: candidateId }) => candidateId === blockId) : void 0;
|
|
6808
|
+
if (kind !== "graph-projection" && currentBlock?.processingStatus === "ready" && currentBlock.threadId) {
|
|
6809
|
+
this.pendingSurfaceSync.set(`${key}\0${currentBlock.id}`, { namespace: key, blockId: currentBlock.id });
|
|
6737
6810
|
if (session && this.namespaceFor(session) === key) await this.syncPendingRetrySurface(session, memory);
|
|
6738
6811
|
}
|
|
6739
|
-
const
|
|
6812
|
+
const job = kind === "block-summary" ? memory.listSummaryJobs().find(({ blockId: candidateId }) => candidateId === jobId) : kind === "event-extraction" ? memory.listExtractionJobs().find(({ blockId: candidateId }) => candidateId === jobId) : memory.listGraphProjectionJobs().find(({ id: candidateId }) => candidateId === jobId);
|
|
6813
|
+
const succeeded = kind === "graph-projection" ? job?.status === "completed" : job?.status === "succeeded" || job?.status === "skipped";
|
|
6814
|
+
if (!succeeded) throw new Error(job?.lastError || `${kind} retry did not complete`);
|
|
6740
6815
|
return {
|
|
6816
|
+
kind,
|
|
6817
|
+
jobId,
|
|
6741
6818
|
blockId,
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
summaryJob:
|
|
6745
|
-
|
|
6819
|
+
status: job?.status,
|
|
6820
|
+
job: job ?? null,
|
|
6821
|
+
...kind === "block-summary" ? { summaryJob: job ?? null } : {},
|
|
6822
|
+
result,
|
|
6823
|
+
processingStatus: currentBlock?.processingStatus ?? null,
|
|
6824
|
+
ready: currentBlock?.processingStatus === "ready",
|
|
6825
|
+
surfaceUpdated: !blockId || !this.pendingSurfaceSync.has(`${key}\0${blockId}`)
|
|
6746
6826
|
};
|
|
6747
6827
|
} finally {
|
|
6748
6828
|
if (owned) await memory.close();
|
|
@@ -6751,7 +6831,14 @@ var StrataGateRuntime = class {
|
|
|
6751
6831
|
this.settingsTail = update.then(() => {
|
|
6752
6832
|
}, () => {
|
|
6753
6833
|
});
|
|
6754
|
-
|
|
6834
|
+
const tracked = update.finally(() => {
|
|
6835
|
+
if (this.adminJobRetryRuns.get(retryKey) === tracked) this.adminJobRetryRuns.delete(retryKey);
|
|
6836
|
+
});
|
|
6837
|
+
this.adminJobRetryRuns.set(retryKey, tracked);
|
|
6838
|
+
return tracked;
|
|
6839
|
+
}
|
|
6840
|
+
async adminRetryBlockSummary(namespace, id) {
|
|
6841
|
+
return this.adminRetryJob(namespace, "block-summary", id);
|
|
6755
6842
|
}
|
|
6756
6843
|
async applyBlockDecayLambda(value) {
|
|
6757
6844
|
await this.flush();
|
|
@@ -7623,7 +7710,7 @@ function clusterKnowledgeGraph(rawNodes, rawEdges) {
|
|
|
7623
7710
|
}
|
|
7624
7711
|
|
|
7625
7712
|
// src/web.ts
|
|
7626
|
-
var STRATAGATE_DSH_VERSION = "0.2.
|
|
7713
|
+
var STRATAGATE_DSH_VERSION = "0.2.65";
|
|
7627
7714
|
var LEGACY_THREAD_ID = "__legacy__";
|
|
7628
7715
|
var nodeRequire2 = createRequire2(import.meta.url);
|
|
7629
7716
|
function installedPackageVersion(names) {
|
|
@@ -7766,40 +7853,80 @@ async function overview(runtime, cachedEntries) {
|
|
|
7766
7853
|
const rows = [];
|
|
7767
7854
|
for (const { namespace, snapshot } of entries) {
|
|
7768
7855
|
if (!snapshot) continue;
|
|
7769
|
-
const
|
|
7770
|
-
const
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7856
|
+
const recovered = recoverSnapshotView(snapshot);
|
|
7857
|
+
const blockDetails = (blockIds) => [...new Set(blockIds)].flatMap((blockId) => {
|
|
7858
|
+
const displayBlocks = recovered.blocks.filter(({ source, threadId }) => source.id === blockId && threadId !== LEGACY_THREAD_ID);
|
|
7859
|
+
if (displayBlocks.length > 0) {
|
|
7860
|
+
return displayBlocks.map((block2) => ({
|
|
7861
|
+
id: block2.id,
|
|
7862
|
+
sourceId: block2.source.id,
|
|
7863
|
+
sequence: block2.source.sequence,
|
|
7864
|
+
title: block2.source.l0Title ?? null,
|
|
7865
|
+
threadId: block2.threadId,
|
|
7866
|
+
turnRange: block2.turnRange,
|
|
7867
|
+
shouldExtract: block2.source.shouldExtract ?? null
|
|
7868
|
+
}));
|
|
7869
|
+
}
|
|
7870
|
+
const block = snapshot.blocks.find(({ id }) => id === blockId);
|
|
7871
|
+
return block ? [{
|
|
7872
|
+
id: block.id,
|
|
7873
|
+
sourceId: block.id,
|
|
7874
|
+
sequence: block.sequence,
|
|
7875
|
+
title: block.l0Title ?? null,
|
|
7876
|
+
threadId: block.threadId ?? null,
|
|
7877
|
+
turnRange: [block.startTurn, block.endTurn],
|
|
7878
|
+
shouldExtract: block.shouldExtract ?? null
|
|
7879
|
+
}] : [];
|
|
7880
|
+
});
|
|
7881
|
+
const describeBlockJob = (kind, job) => {
|
|
7882
|
+
const details = blockDetails([job.blockId]);
|
|
7883
|
+
return {
|
|
7788
7884
|
id: job.blockId,
|
|
7789
|
-
kind
|
|
7885
|
+
kind,
|
|
7886
|
+
status: job.status,
|
|
7790
7887
|
attempts: job.attempts,
|
|
7888
|
+
nextRetryAt: job.nextRetryAt,
|
|
7791
7889
|
lastError: job.lastError?.slice(0, 500) ?? null,
|
|
7792
7890
|
lastErrorFull: job.lastError,
|
|
7793
|
-
updatedAt: job.updatedAt
|
|
7794
|
-
|
|
7795
|
-
|
|
7891
|
+
updatedAt: job.updatedAt,
|
|
7892
|
+
threadId: details[0]?.threadId ?? null,
|
|
7893
|
+
blockIds: [job.blockId],
|
|
7894
|
+
threadIds: [...new Set(details.flatMap(({ threadId }) => threadId ?? []))],
|
|
7895
|
+
blockDetails: details,
|
|
7896
|
+
sequence: details[0]?.sequence ?? null,
|
|
7897
|
+
turnRange: details[0]?.turnRange ?? null
|
|
7898
|
+
};
|
|
7899
|
+
};
|
|
7900
|
+
const describeGraphJob = (job) => {
|
|
7901
|
+
const blockIds = [...new Set(job.sourceEventIds.flatMap((eventId) => snapshot.events.find(({ id }) => id === eventId)?.sourceBlockId ?? []))];
|
|
7902
|
+
const details = blockDetails(blockIds);
|
|
7903
|
+
return {
|
|
7796
7904
|
id: job.id,
|
|
7797
7905
|
kind: "graph-projection",
|
|
7906
|
+
status: job.status,
|
|
7798
7907
|
attempts: job.attempts,
|
|
7908
|
+
nextRetryAt: null,
|
|
7799
7909
|
lastError: job.lastError?.slice(0, 500) ?? null,
|
|
7800
7910
|
lastErrorFull: job.lastError,
|
|
7801
|
-
updatedAt: job.updatedAt
|
|
7802
|
-
|
|
7911
|
+
updatedAt: job.updatedAt,
|
|
7912
|
+
blockIds,
|
|
7913
|
+
threadIds: [...new Set(details.flatMap(({ threadId }) => threadId ?? []))],
|
|
7914
|
+
blockDetails: details,
|
|
7915
|
+
sequences: details.map(({ sequence }) => sequence),
|
|
7916
|
+
sourceEventIds: job.sourceEventIds
|
|
7917
|
+
};
|
|
7918
|
+
};
|
|
7919
|
+
const failedJobs = snapshot.summaryJobs.filter(({ status }) => status === "failed").length + snapshot.extractionJobs.filter(({ status }) => status === "failed").length + snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").length;
|
|
7920
|
+
const processingJobs = snapshot.summaryJobs.filter(({ status, nextRetryAt }) => status === "pending" || status === "running" || status === "failed" && nextRetryAt !== null).length + snapshot.extractionJobs.filter(({ status, nextRetryAt }) => status === "running" || status === "failed" && nextRetryAt !== null).length + snapshot.graphProjectionJobs.filter(({ status }) => status === "pending" || status === "running").length;
|
|
7921
|
+
const failedJobDetails = [
|
|
7922
|
+
...snapshot.summaryJobs.filter(({ status }) => status === "failed").map((job) => describeBlockJob("block-summary", job)),
|
|
7923
|
+
...snapshot.extractionJobs.filter(({ status }) => status === "failed").map((job) => describeBlockJob("event-extraction", job)),
|
|
7924
|
+
...snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").map(describeGraphJob)
|
|
7925
|
+
];
|
|
7926
|
+
const processingJobDetails = [
|
|
7927
|
+
...snapshot.summaryJobs.filter(({ status, nextRetryAt }) => status === "pending" || status === "running" || status === "failed" && nextRetryAt !== null).map((job) => describeBlockJob("block-summary", job)),
|
|
7928
|
+
...snapshot.extractionJobs.filter(({ status, nextRetryAt }) => status === "running" || status === "failed" && nextRetryAt !== null).map((job) => describeBlockJob("event-extraction", job)),
|
|
7929
|
+
...snapshot.graphProjectionJobs.filter(({ status }) => status === "pending" || status === "running").map(describeGraphJob)
|
|
7803
7930
|
];
|
|
7804
7931
|
const timestamps = [
|
|
7805
7932
|
...snapshot.blocks.map(({ createdAt }) => createdAt),
|
|
@@ -7834,6 +7961,7 @@ async function overview(runtime, cachedEntries) {
|
|
|
7834
7961
|
failedJobs,
|
|
7835
7962
|
processingJobs,
|
|
7836
7963
|
failedJobDetails,
|
|
7964
|
+
processingJobDetails,
|
|
7837
7965
|
successfulModelResponses: snapshot.successfulModelResponses ?? [],
|
|
7838
7966
|
lastActivityAt: timestamps.at(-1) ?? null
|
|
7839
7967
|
});
|
|
@@ -8345,11 +8473,33 @@ async function retryBlockSummary(runtime, url) {
|
|
|
8345
8473
|
const snapshot = await requiredSnapshot(runtime, namespace);
|
|
8346
8474
|
const job = snapshot.summaryJobs.find(({ blockId: candidateId }) => candidateId === blockId);
|
|
8347
8475
|
if (!job) throw new AdminHttpError(404, `Unknown Block Summary job: ${blockId}`);
|
|
8348
|
-
if (job.status !== "failed"
|
|
8349
|
-
throw new AdminHttpError(409, "Block Summary is not in a terminal failed state");
|
|
8350
|
-
}
|
|
8476
|
+
if (job.status !== "failed") throw new AdminHttpError(409, `Block Summary is ${job.status}, not failed`);
|
|
8351
8477
|
return runtime.adminRetryBlockSummary(namespace, blockId);
|
|
8352
8478
|
}
|
|
8479
|
+
async function retryJob(runtime, url) {
|
|
8480
|
+
const namespace = url.searchParams.get("namespace")?.trim() ?? "";
|
|
8481
|
+
const kind = url.searchParams.get("kind")?.trim() ?? "";
|
|
8482
|
+
const jobId = url.searchParams.get("jobId")?.trim() ?? "";
|
|
8483
|
+
if (!namespace) throw new AdminHttpError(400, "namespace is required");
|
|
8484
|
+
if (!["block-summary", "event-extraction", "graph-projection"].includes(kind)) {
|
|
8485
|
+
throw new AdminHttpError(400, "kind must be block-summary, event-extraction, or graph-projection");
|
|
8486
|
+
}
|
|
8487
|
+
if (!jobId) throw new AdminHttpError(400, "jobId is required");
|
|
8488
|
+
if (jobId.startsWith("virtual:")) throw new AdminHttpError(409, "Recovered legacy fragments cannot be retried");
|
|
8489
|
+
const snapshot = await requiredSnapshot(runtime, namespace);
|
|
8490
|
+
const job = kind === "block-summary" ? snapshot.summaryJobs.find(({ blockId }) => blockId === jobId) : kind === "event-extraction" ? snapshot.extractionJobs.find(({ blockId }) => blockId === jobId) : snapshot.graphProjectionJobs.find(({ id }) => id === jobId);
|
|
8491
|
+
if (!job) throw new AdminHttpError(404, `Unknown ${kind} job: ${jobId}`);
|
|
8492
|
+
if (job.status !== "failed") throw new AdminHttpError(409, `${kind} job is ${job.status}, not failed`);
|
|
8493
|
+
try {
|
|
8494
|
+
return await runtime.adminRetryJob(
|
|
8495
|
+
namespace,
|
|
8496
|
+
kind,
|
|
8497
|
+
jobId
|
|
8498
|
+
);
|
|
8499
|
+
} catch (error) {
|
|
8500
|
+
throw new AdminHttpError(422, error instanceof Error ? error.message : String(error));
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8353
8503
|
function receiptSources(snapshot, receipt) {
|
|
8354
8504
|
const events = snapshot.events.filter(({ id }) => receipt.eventIds.includes(id));
|
|
8355
8505
|
const elements = snapshot.elements.filter(({ id }) => receipt.elementIds.includes(id));
|
|
@@ -8390,7 +8540,7 @@ async function dashboard(runtime, url, ifNoneMatch) {
|
|
|
8390
8540
|
const selected = entries.find(({ namespace }) => namespace === requestedNamespace) ?? entries[0];
|
|
8391
8541
|
const threadId = url.searchParams.get("threadId")?.trim() ?? "";
|
|
8392
8542
|
const revisionKey = entries.map(({ namespace, revision }) => `${namespace}:${revision}`).join("|");
|
|
8393
|
-
const etag = `"${createHash2("sha256").update(`${revisionKey}\0${selected?.namespace ?? ""}\0${threadId}`).digest("base64url").slice(0, 24)}"`;
|
|
8543
|
+
const etag = `"${createHash2("sha256").update(`${STRATAGATE_DSH_VERSION}\0${revisionKey}\0${selected?.namespace ?? ""}\0${threadId}`).digest("base64url").slice(0, 24)}"`;
|
|
8394
8544
|
if (ifNoneMatch.split(",").map((value) => value.trim()).includes(etag)) return { etag, notModified: true };
|
|
8395
8545
|
const overviewValue = await overview(runtime, entries);
|
|
8396
8546
|
if (!selected) {
|
|
@@ -8465,6 +8615,9 @@ async function handleAdminRequest(runtime, req, res) {
|
|
|
8465
8615
|
} else if (path === "/api/stratagate/blocks/retry-summary") {
|
|
8466
8616
|
if (req.method !== "POST") throw new AdminHttpError(405, "StrataGate Block Summary retry requires POST");
|
|
8467
8617
|
sendJson(res, 200, await retryBlockSummary(runtime, url));
|
|
8618
|
+
} else if (path === "/api/stratagate/jobs/retry") {
|
|
8619
|
+
if (req.method !== "POST") throw new AdminHttpError(405, "StrataGate job retry requires POST");
|
|
8620
|
+
sendJson(res, 200, await retryJob(runtime, url));
|
|
8468
8621
|
} else if (path === "/api/stratagate/import") {
|
|
8469
8622
|
if (req.method === "GET") {
|
|
8470
8623
|
const operation = url.searchParams.get("operation");
|