@bastani/atomic 0.9.44-alpha.2 → 0.9.44-alpha.3
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 +15 -0
- package/dist/builtin/intercom/CHANGELOG.md +7 -0
- package/dist/builtin/intercom/broker/framing.ts +18 -1
- package/dist/builtin/intercom/index.bundle.mjs +19 -4
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/index.bundle.mjs +2510 -1308
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +7 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +313 -76
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +313 -76
- package/dist/builtin/workflows/src/index.js +313 -76
- package/dist/builtin/workflows/src/local-worker-entry.mjs +313 -76
- package/examples/extensions/gondolin/package-lock.json +3 -3
- package/npm-shrinkwrap.json +67 -51
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.9.44-alpha.
|
|
3
|
+
"version": "0.9.44-alpha.3",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
|
|
6
6
|
"contributors": [
|
|
@@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.44-alpha.3] - 2026-10-25
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- A workflow whose stages send many Intercom messages to stage patterns (for example `workflow:<rootRunId>/child-*/**`) no longer saturates the workflow host. The host matched every queued pattern message against every stage on each change; it now indexes the patterns and each stage's paths once. In a 150-child fan-out test, delivered sends went from 566 of 900 to 900 of 900 and host event-loop lag p99 from about 1 s to under 200 ms ([#3931](https://github.com/bastani-inc/atomic/issues/3931))
|
|
14
|
+
- Workflow journal events no longer store a run's whole list of pending stage messages each time. Pattern sends stay queued until the root run ends, so every queued message rewrote the growing list and the journal grew quadratically with the number of sends. Each event now records only what changed; older journals still read as before ([#3929](https://github.com/bastani-inc/atomic/issues/3929))
|
|
15
|
+
|
|
9
16
|
## [0.9.44-alpha.2] - 2026-10-24
|
|
10
17
|
|
|
11
18
|
### Fixed
|
|
@@ -2120,7 +2120,20 @@ function createWorkflowBoundarySegmentsResolver(runs) {
|
|
|
2120
2120
|
};
|
|
2121
2121
|
}
|
|
2122
2122
|
function workflowBoundaryHops(runs, runId) {
|
|
2123
|
+
return boundaryHopsFrom(new Map(runs.map((run) => [run.id, run])), runId);
|
|
2124
|
+
}
|
|
2125
|
+
function createWorkflowBoundaryHopsResolver(runs) {
|
|
2123
2126
|
const runById = new Map(runs.map((run) => [run.id, run]));
|
|
2127
|
+
const cache = new Map;
|
|
2128
|
+
return (runId) => {
|
|
2129
|
+
if (cache.has(runId))
|
|
2130
|
+
return cache.get(runId);
|
|
2131
|
+
const hops = boundaryHopsFrom(runById, runId);
|
|
2132
|
+
cache.set(runId, hops);
|
|
2133
|
+
return hops;
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
function boundaryHopsFrom(runById, runId) {
|
|
2124
2137
|
const hops = [];
|
|
2125
2138
|
let current = runById.get(runId);
|
|
2126
2139
|
while (current !== undefined && current.parentRunId !== undefined) {
|
|
@@ -2146,6 +2159,35 @@ function workflowStagePathVariants(hops, stageSegments) {
|
|
|
2146
2159
|
function stageMatchesPathPattern(patternSegments, hops, stageSegments) {
|
|
2147
2160
|
return workflowStagePathVariants(hops, stageSegments).some((candidate) => matchStagePathSegments(patternSegments, candidate));
|
|
2148
2161
|
}
|
|
2162
|
+
function createStagePathPatternIndex(patterns) {
|
|
2163
|
+
const byFirstSegment = new Map;
|
|
2164
|
+
const globFirst = [];
|
|
2165
|
+
patterns.forEach((pattern, index) => {
|
|
2166
|
+
const first = pattern[0];
|
|
2167
|
+
if (first === undefined || first.includes("*")) {
|
|
2168
|
+
globFirst.push(index);
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
const bucket = byFirstSegment.get(first);
|
|
2172
|
+
if (bucket === undefined)
|
|
2173
|
+
byFirstSegment.set(first, [index]);
|
|
2174
|
+
else
|
|
2175
|
+
bucket.push(index);
|
|
2176
|
+
});
|
|
2177
|
+
return {
|
|
2178
|
+
matching(hops, stageSegments) {
|
|
2179
|
+
if (patterns.length === 0)
|
|
2180
|
+
return [];
|
|
2181
|
+
const variants = workflowStagePathVariants(hops, stageSegments);
|
|
2182
|
+
const candidates = new Set(globFirst);
|
|
2183
|
+
for (const variant of variants) {
|
|
2184
|
+
for (const index of byFirstSegment.get(variant[0]) ?? [])
|
|
2185
|
+
candidates.add(index);
|
|
2186
|
+
}
|
|
2187
|
+
return [...candidates].sort((left, right) => left - right).filter((index) => variants.some((variant) => matchStagePathSegments(patterns[index], variant)));
|
|
2188
|
+
}
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2149
2191
|
function pendingStageTarget(runId, stage) {
|
|
2150
2192
|
return "workflowGraphTarget" in stage ? stage.workflowGraphTarget : { runId, stageId: stage.id };
|
|
2151
2193
|
}
|
|
@@ -6808,12 +6850,16 @@ function removeProcessExitCleanup(cleanup) {
|
|
|
6808
6850
|
if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
|
|
6809
6851
|
process.off("exit", runProcessExitCleanups);
|
|
6810
6852
|
}
|
|
6853
|
+
function writtenHandle(event) {
|
|
6854
|
+
if (event.type === RUN_EVENT_TYPES.promptAsked || event.type === RUN_EVENT_TYPES.promptReleased)
|
|
6855
|
+
return event.payload.handle;
|
|
6856
|
+
if (event.type === RUN_EVENT_TYPES.checkpointRecorded || event.type === RUN_EVENT_TYPES.messageSettled || event.type === RUN_EVENT_TYPES.messageDelivered)
|
|
6857
|
+
return;
|
|
6858
|
+
return event.payload;
|
|
6859
|
+
}
|
|
6811
6860
|
function snapshotNeedsHistory(tail) {
|
|
6812
|
-
const
|
|
6813
|
-
|
|
6814
|
-
return false;
|
|
6815
|
-
const payload = event.payload;
|
|
6816
|
-
return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
|
|
6861
|
+
const handle = writtenHandle(tail.snapshot.event);
|
|
6862
|
+
return handle !== undefined && (handle.pendingStageMessageChange !== undefined || hasPendingStageDeliveryDelta(handle.pendingStageMessages));
|
|
6817
6863
|
}
|
|
6818
6864
|
function* invoke(action) {
|
|
6819
6865
|
try {
|
|
@@ -6927,14 +6973,11 @@ class JournalDurableBackend {
|
|
|
6927
6973
|
const payload = event.payload;
|
|
6928
6974
|
this.projection.archivePendingStageDeliveryReceipt(entry.runId, payload.messageId, payload.delivery, payload.accounting);
|
|
6929
6975
|
} else if (event.type === RUN_EVENT_TYPES.promptAsked || event.type === RUN_EVENT_TYPES.promptReleased) {
|
|
6930
|
-
this.projection.applyWorkflowSnapshot(event.payload.handle);
|
|
6976
|
+
this.projection.applyWorkflowSnapshot(decodeHandle(event.payload.handle, this.projection.getWorkflow(entry.runId)?.pendingStageMessages));
|
|
6931
6977
|
this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
|
|
6932
6978
|
} else {
|
|
6933
6979
|
const previous = this.projection.getWorkflow(entry.runId);
|
|
6934
|
-
const handle =
|
|
6935
|
-
...event.payload,
|
|
6936
|
-
pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
|
|
6937
|
-
} : event.payload;
|
|
6980
|
+
const handle = decodeHandle(event.payload, previous?.pendingStageMessages);
|
|
6938
6981
|
this.projection.applyWorkflowSnapshot(handle);
|
|
6939
6982
|
if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
|
|
6940
6983
|
this.prompts.delete(entry.runId);
|
|
@@ -7054,6 +7097,20 @@ class JournalDurableBackend {
|
|
|
7054
7097
|
}
|
|
7055
7098
|
*append(runId, event, options) {
|
|
7056
7099
|
const lease = yield* this.ownership(runId);
|
|
7100
|
+
const base = this.sequences.get(runId);
|
|
7101
|
+
const pinned = options?.expectSeq;
|
|
7102
|
+
const encoded = base !== undefined && (pinned === undefined || pinned === base) && event.type !== RUN_EVENT_TYPES.runStarted ? encodeHandleEvent(event, this.projection.getWorkflow(runId)?.pendingStageMessages) : undefined;
|
|
7103
|
+
if (encoded !== undefined) {
|
|
7104
|
+
try {
|
|
7105
|
+
return yield* this.write(runId, lease, encoded, { ...options, expectSeq: base });
|
|
7106
|
+
} catch (error) {
|
|
7107
|
+
if (!(error instanceof JournalCasConflictError) || pinned !== undefined)
|
|
7108
|
+
throw error;
|
|
7109
|
+
}
|
|
7110
|
+
}
|
|
7111
|
+
yield* this.write(runId, lease, event, options);
|
|
7112
|
+
}
|
|
7113
|
+
*write(runId, lease, event, options) {
|
|
7057
7114
|
try {
|
|
7058
7115
|
const outcome = yield* invoke(() => this.journal.append(lease, event, options));
|
|
7059
7116
|
const nextSeq = (this.sequences.get(runId) ?? 0) + 1;
|
|
@@ -7235,17 +7292,6 @@ class JournalDurableBackend {
|
|
|
7235
7292
|
pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
|
|
7236
7293
|
updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
|
|
7237
7294
|
};
|
|
7238
|
-
const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
|
|
7239
|
-
const base = this.sequences.get(runId);
|
|
7240
|
-
if (encoded !== undefined && base !== undefined) {
|
|
7241
|
-
try {
|
|
7242
|
-
yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
|
|
7243
|
-
return true;
|
|
7244
|
-
} catch (error) {
|
|
7245
|
-
if (!(error instanceof JournalCasConflictError))
|
|
7246
|
-
throw error;
|
|
7247
|
-
}
|
|
7248
|
-
}
|
|
7249
7295
|
yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
|
|
7250
7296
|
return true;
|
|
7251
7297
|
}
|
|
@@ -7585,31 +7631,143 @@ function uniqueByIdentity(messages) {
|
|
|
7585
7631
|
function isDeltaEncoded(message) {
|
|
7586
7632
|
return message.deliveryDelta !== undefined;
|
|
7587
7633
|
}
|
|
7588
|
-
function
|
|
7589
|
-
const
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7634
|
+
function deliveryDelta(base, deliveries) {
|
|
7635
|
+
const drop = [];
|
|
7636
|
+
let kept = 0;
|
|
7637
|
+
base.forEach((delivery, index) => {
|
|
7638
|
+
if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
|
|
7639
|
+
kept++;
|
|
7640
|
+
else
|
|
7641
|
+
drop.push(index);
|
|
7642
|
+
});
|
|
7643
|
+
const add = deliveries.slice(kept);
|
|
7644
|
+
return drop.length === 0 ? { add } : { drop, add };
|
|
7645
|
+
}
|
|
7646
|
+
function pendingStageFields(message) {
|
|
7647
|
+
return Object.keys(message);
|
|
7648
|
+
}
|
|
7649
|
+
function copyField(target, source, key) {
|
|
7650
|
+
target[key] = source[key];
|
|
7651
|
+
}
|
|
7652
|
+
function isRemovedField(message, key) {
|
|
7653
|
+
return message[key] === undefined;
|
|
7654
|
+
}
|
|
7655
|
+
function json(message) {
|
|
7656
|
+
let text = entryJson.get(message);
|
|
7657
|
+
if (text === undefined) {
|
|
7658
|
+
text = JSON.stringify(message);
|
|
7659
|
+
entryJson.set(message, text);
|
|
7660
|
+
}
|
|
7661
|
+
return text;
|
|
7662
|
+
}
|
|
7663
|
+
function patchEntry(index, base, message) {
|
|
7664
|
+
const set = {};
|
|
7665
|
+
const unset = pendingStageFields(base).filter((key) => base[key] !== undefined && isRemovedField(message, key));
|
|
7666
|
+
for (const key of pendingStageFields(message)) {
|
|
7667
|
+
if (message[key] === undefined || key === "deliveries")
|
|
7668
|
+
continue;
|
|
7669
|
+
if (JSON.stringify(message[key]) !== JSON.stringify(base[key]))
|
|
7670
|
+
copyField(set, message, key);
|
|
7671
|
+
}
|
|
7672
|
+
let delta;
|
|
7673
|
+
if (message.deliveries !== undefined) {
|
|
7674
|
+
if (base.deliveries === undefined)
|
|
7675
|
+
set.deliveries = message.deliveries;
|
|
7676
|
+
else if (JSON.stringify(base.deliveries) !== JSON.stringify(message.deliveries))
|
|
7677
|
+
delta = deliveryDelta(base.deliveries, message.deliveries);
|
|
7678
|
+
}
|
|
7679
|
+
return {
|
|
7680
|
+
patch: index,
|
|
7681
|
+
...Object.keys(set).length === 0 ? {} : { set },
|
|
7682
|
+
...unset.length === 0 ? {} : { unset },
|
|
7683
|
+
...delta === undefined ? {} : { deliveryDelta: delta }
|
|
7684
|
+
};
|
|
7685
|
+
}
|
|
7686
|
+
function encodePendingStageList(previous, next) {
|
|
7687
|
+
const positions = new Map;
|
|
7688
|
+
const repeated = new Set;
|
|
7689
|
+
previous.forEach((message, index) => {
|
|
7690
|
+
const key = identity(message);
|
|
7691
|
+
if (positions.has(key))
|
|
7692
|
+
repeated.add(key);
|
|
7693
|
+
positions.set(key, index);
|
|
7611
7694
|
});
|
|
7612
|
-
|
|
7695
|
+
for (const key of repeated)
|
|
7696
|
+
positions.delete(key);
|
|
7697
|
+
const items = [];
|
|
7698
|
+
let reused = 0;
|
|
7699
|
+
for (const message of next) {
|
|
7700
|
+
const index = positions.get(identity(message));
|
|
7701
|
+
const base = index === undefined ? undefined : previous[index];
|
|
7702
|
+
if (base === undefined) {
|
|
7703
|
+
items.push(message);
|
|
7704
|
+
continue;
|
|
7705
|
+
}
|
|
7706
|
+
reused++;
|
|
7707
|
+
const patched = base === message || json(base) === json(message) ? undefined : patchEntry(index, base, message);
|
|
7708
|
+
if (patched === undefined || Object.keys(patched).length === 1) {
|
|
7709
|
+
const last = items.at(-1);
|
|
7710
|
+
if (Array.isArray(last) && last[0] + last[1] === index)
|
|
7711
|
+
last[1]++;
|
|
7712
|
+
else
|
|
7713
|
+
items.push([index, 1]);
|
|
7714
|
+
continue;
|
|
7715
|
+
}
|
|
7716
|
+
items.push(JSON.stringify(patched).length < json(message).length ? patched : message);
|
|
7717
|
+
}
|
|
7718
|
+
return reused > 0 ? items : undefined;
|
|
7719
|
+
}
|
|
7720
|
+
function decodePendingStageList(previous, items) {
|
|
7721
|
+
const base = (index) => {
|
|
7722
|
+
if (!Number.isInteger(index) || index < 0 || index >= previous.length)
|
|
7723
|
+
throw new Error(`atomic-workflows: journal pending-stage change names missing entry ${index}`);
|
|
7724
|
+
return previous[index];
|
|
7725
|
+
};
|
|
7726
|
+
const result = [];
|
|
7727
|
+
for (const item of items) {
|
|
7728
|
+
if (Array.isArray(item)) {
|
|
7729
|
+
const [start, count] = item;
|
|
7730
|
+
for (let index = start;index < start + count; index++)
|
|
7731
|
+
result.push(base(index));
|
|
7732
|
+
} else if (typeof item.patch === "number") {
|
|
7733
|
+
const { patch, set, unset, deliveryDelta: delta } = item;
|
|
7734
|
+
const entry = { ...base(patch), ...set };
|
|
7735
|
+
for (const key of unset ?? [])
|
|
7736
|
+
delete entry[key];
|
|
7737
|
+
if (delta !== undefined) {
|
|
7738
|
+
const dropped = new Set(delta.drop ?? []);
|
|
7739
|
+
const deliveries = base(patch).deliveries ?? [];
|
|
7740
|
+
entry.deliveries = [...deliveries.filter((_, index) => !dropped.has(index)), ...delta.add];
|
|
7741
|
+
}
|
|
7742
|
+
result.push(entry);
|
|
7743
|
+
} else
|
|
7744
|
+
result.push(item);
|
|
7745
|
+
}
|
|
7746
|
+
return result;
|
|
7747
|
+
}
|
|
7748
|
+
function encodeHandleEvent(event, previous) {
|
|
7749
|
+
const handle = writtenHandle(event);
|
|
7750
|
+
if (handle?.pendingStageMessages === undefined || previous === undefined)
|
|
7751
|
+
return;
|
|
7752
|
+
const change = encodePendingStageList(previous, handle.pendingStageMessages);
|
|
7753
|
+
if (change === undefined)
|
|
7754
|
+
return;
|
|
7755
|
+
const { pendingStageMessages: _full, ...rest } = handle;
|
|
7756
|
+
const encoded = { ...rest, pendingStageMessageChange: change };
|
|
7757
|
+
if (event.type === RUN_EVENT_TYPES.promptAsked || event.type === RUN_EVENT_TYPES.promptReleased)
|
|
7758
|
+
return { ...event, payload: { ...event.payload, handle: encoded } };
|
|
7759
|
+
return { ...event, payload: encoded };
|
|
7760
|
+
}
|
|
7761
|
+
function decodeHandle(handle, previous) {
|
|
7762
|
+
const { pendingStageMessageChange: change, ...rest } = handle;
|
|
7763
|
+
if (change !== undefined)
|
|
7764
|
+
return { ...rest, pendingStageMessages: decodePendingStageList(previous ?? [], change) };
|
|
7765
|
+
if (!hasPendingStageDeliveryDelta(handle.pendingStageMessages))
|
|
7766
|
+
return handle;
|
|
7767
|
+
return {
|
|
7768
|
+
...handle,
|
|
7769
|
+
pendingStageMessages: decodePendingStageDeliveries(previous ?? [], handle.pendingStageMessages ?? [])
|
|
7770
|
+
};
|
|
7613
7771
|
}
|
|
7614
7772
|
function decodePendingStageDeliveries(previous, messages) {
|
|
7615
7773
|
if (!messages.some(isDeltaEncoded))
|
|
@@ -7629,7 +7787,7 @@ function decodePendingStageDeliveries(previous, messages) {
|
|
|
7629
7787
|
function hasPendingStageDeliveryDelta(messages) {
|
|
7630
7788
|
return messages?.some(isDeltaEncoded) === true;
|
|
7631
7789
|
}
|
|
7632
|
-
var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
|
|
7790
|
+
var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError, entryJson;
|
|
7633
7791
|
var init_journal_backend = __esm(() => {
|
|
7634
7792
|
init_workflow_instance_ownership_error();
|
|
7635
7793
|
init_backend();
|
|
@@ -7687,6 +7845,7 @@ var init_journal_backend = __esm(() => {
|
|
|
7687
7845
|
this.name = "JournalMessageScopeError";
|
|
7688
7846
|
}
|
|
7689
7847
|
};
|
|
7848
|
+
entryJson = new WeakMap;
|
|
7690
7849
|
});
|
|
7691
7850
|
|
|
7692
7851
|
// dist/builtin/workflows/src/durable/journal/legacy-journal-tables.ts
|
|
@@ -60812,24 +60971,63 @@ function stickyStageDeliveryStatuses(runs, rootRunId, availableBackend) {
|
|
|
60812
60971
|
const root = runs.find((run) => run.id === rootRunId);
|
|
60813
60972
|
const entries = (root?.pendingStageMessages ?? []).filter((entry) => entry.sticky === true);
|
|
60814
60973
|
const result = [];
|
|
60974
|
+
const patternByTarget = new Map;
|
|
60975
|
+
const patterns = [];
|
|
60976
|
+
const entriesByPattern = [];
|
|
60977
|
+
const ownEntries = [];
|
|
60978
|
+
entries.forEach((entry, index) => {
|
|
60979
|
+
const target = entry.targetPath ?? entry.stageKey;
|
|
60980
|
+
let pattern = patternByTarget.get(target);
|
|
60981
|
+
if (pattern === undefined) {
|
|
60982
|
+
const parsed = parseWorkflowStageTarget(target);
|
|
60983
|
+
pattern = parsed?.rootRunId === rootRunId ? patterns.push(parsed.segments) - 1 : -1;
|
|
60984
|
+
if (pattern >= 0)
|
|
60985
|
+
entriesByPattern.push([]);
|
|
60986
|
+
patternByTarget.set(target, pattern);
|
|
60987
|
+
}
|
|
60988
|
+
if (pattern < 0)
|
|
60989
|
+
return;
|
|
60990
|
+
entriesByPattern[pattern].push(index);
|
|
60991
|
+
ownEntries.push(index);
|
|
60992
|
+
});
|
|
60993
|
+
if (ownEntries.length === 0)
|
|
60994
|
+
return result;
|
|
60995
|
+
const patternIndex = createStagePathPatternIndex(patterns);
|
|
60996
|
+
const deliveriesByEntry = new Map;
|
|
60997
|
+
const entryReceipts = (index, runId, stageId) => {
|
|
60998
|
+
let byStage = deliveriesByEntry.get(index);
|
|
60999
|
+
if (byStage === undefined) {
|
|
61000
|
+
byStage = new Map;
|
|
61001
|
+
for (const delivery of entries[index].deliveries ?? []) {
|
|
61002
|
+
const key = `${delivery.runId}\x00${delivery.stageId}`;
|
|
61003
|
+
const list = byStage.get(key);
|
|
61004
|
+
if (list === undefined)
|
|
61005
|
+
byStage.set(key, [delivery]);
|
|
61006
|
+
else
|
|
61007
|
+
list.push(delivery);
|
|
61008
|
+
}
|
|
61009
|
+
deliveriesByEntry.set(index, byStage);
|
|
61010
|
+
}
|
|
61011
|
+
return byStage.get(`${runId}\x00${stageId}`) ?? [];
|
|
61012
|
+
};
|
|
60815
61013
|
availableBackend = availableRunDurableBackend(runs, rootRunId) ?? availableBackend;
|
|
60816
61014
|
const backend = availableBackend === undefined ? undefined : durableBackendForRun(availableBackend, runs, rootRunId);
|
|
61015
|
+
const runById = new Map(runs.map((run) => [run.id, run]));
|
|
61016
|
+
const hopsFor = createWorkflowBoundaryHopsResolver(runs);
|
|
60817
61017
|
for (const run of runs) {
|
|
60818
|
-
|
|
61018
|
+
const durableRootRunId = reciprocalWorkflowRootRunId(runById, run.id);
|
|
61019
|
+
if (run.id !== rootRunId && run.rootRunId !== rootRunId && durableRootRunId !== rootRunId)
|
|
60819
61020
|
continue;
|
|
60820
|
-
const resolved =
|
|
60821
|
-
const hops =
|
|
61021
|
+
const resolved = durableRootRunId === rootRunId;
|
|
61022
|
+
const hops = hopsFor(run.id);
|
|
60822
61023
|
for (const stage of run.stages) {
|
|
60823
61024
|
if (stage.nodeKind === "tool" || stage.replayKey?.startsWith("prompt:") === true || stage.replayKey?.startsWith("workflow:") === true || stage.workflowChildRun !== undefined || stage.workflowChild !== undefined)
|
|
60824
61025
|
continue;
|
|
60825
|
-
|
|
61026
|
+
const matchedEntries = resolved && hops ? patternIndex.matching(hops, [stage.id, stage.name]).flatMap((pattern) => entriesByPattern[pattern]).sort((left, right) => left - right) : ownEntries;
|
|
61027
|
+
for (const index of matchedEntries) {
|
|
61028
|
+
const entry = entries[index];
|
|
60826
61029
|
const target = entry.targetPath ?? entry.stageKey;
|
|
60827
|
-
const
|
|
60828
|
-
if (!parsed || parsed.rootRunId !== rootRunId)
|
|
60829
|
-
continue;
|
|
60830
|
-
if (resolved && hops && !stageMatchesPathPattern(parsed.segments, hops, [stage.id, stage.name]))
|
|
60831
|
-
continue;
|
|
60832
|
-
const receipts = (entry.deliveries ?? []).filter((delivery) => delivery.runId === run.id && delivery.stageId === stage.id);
|
|
61030
|
+
const receipts = entryReceipts(index, run.id, stage.id);
|
|
60833
61031
|
const delivered = backend?.hasCachedPendingStageDeliveryReceipt?.(rootRunId, entry.id, {
|
|
60834
61032
|
runId: run.id,
|
|
60835
61033
|
stageId: stage.id,
|
|
@@ -61147,20 +61345,27 @@ function possibleStageRows(runs, rootRunId, rootRun, backend) {
|
|
|
61147
61345
|
if (isTerminalRunStatus(rootRun.status))
|
|
61148
61346
|
return [];
|
|
61149
61347
|
const stickyQueued = (rootRun.pendingStageMessages ?? []).filter((entry) => entry.sticky === true && entry.status === "queued");
|
|
61348
|
+
const segmentsByTarget = new Map;
|
|
61150
61349
|
const stickySegments = (entry) => {
|
|
61151
|
-
const
|
|
61152
|
-
|
|
61350
|
+
const target = entry.targetPath ?? entry.stageKey;
|
|
61351
|
+
if (segmentsByTarget.has(target))
|
|
61352
|
+
return segmentsByTarget.get(target);
|
|
61353
|
+
const parsed = parseWorkflowStageTarget(target);
|
|
61354
|
+
const segments = parsed?.rootRunId === rootRunId ? parsed.segments : undefined;
|
|
61355
|
+
segmentsByTarget.set(target, segments);
|
|
61356
|
+
return segments;
|
|
61153
61357
|
};
|
|
61154
61358
|
const scanEntries = stickyQueued.filter((entry) => {
|
|
61155
61359
|
const segments = stickySegments(entry);
|
|
61156
61360
|
return !(segments !== undefined && segments.length === 1 && segments[0] === "**");
|
|
61157
61361
|
});
|
|
61362
|
+
const liveStageMatches = materializedStageMatcher(runs, rootRunId);
|
|
61158
61363
|
const rows = [];
|
|
61159
61364
|
for (const scanEntry of rootRun.possibleStages ?? []) {
|
|
61160
61365
|
const rowSegments = splitStagePathSegments(scanEntry);
|
|
61161
61366
|
if (rowSegments.length === 0 || rowSegments.some((segment) => segment.length === 0))
|
|
61162
61367
|
continue;
|
|
61163
|
-
if (!rowSegments.some((segment) => segment.includes("*")) &&
|
|
61368
|
+
if (!rowSegments.some((segment) => segment.includes("*")) && liveStageMatches(rowSegments)) {
|
|
61164
61369
|
continue;
|
|
61165
61370
|
}
|
|
61166
61371
|
rows.push({
|
|
@@ -61181,28 +61386,43 @@ function possibleStageRows(runs, rootRunId, rootRun, backend) {
|
|
|
61181
61386
|
}).length
|
|
61182
61387
|
});
|
|
61183
61388
|
const statuses = stickyStageDeliveryStatuses(runs, rootRunId, backend);
|
|
61184
|
-
|
|
61185
|
-
|
|
61389
|
+
const statusesByTarget = new Map;
|
|
61390
|
+
for (const status of statuses) {
|
|
61391
|
+
const forTarget = statusesByTarget.get(status.target);
|
|
61392
|
+
if (forTarget === undefined)
|
|
61393
|
+
statusesByTarget.set(status.target, [status]);
|
|
61394
|
+
else
|
|
61395
|
+
forTarget.push(status);
|
|
61396
|
+
}
|
|
61397
|
+
const queuedByTarget = new Map;
|
|
61398
|
+
for (const entry of stickyQueued) {
|
|
61399
|
+
const target = entry.targetPath ?? entry.stageKey;
|
|
61400
|
+
queuedByTarget.set(target, (queuedByTarget.get(target) ?? 0) + 1);
|
|
61401
|
+
}
|
|
61402
|
+
const rowTargets = new Set(rows.map((row) => row.target));
|
|
61403
|
+
for (const target of statusesByTarget.keys()) {
|
|
61404
|
+
if (rowTargets.has(target))
|
|
61186
61405
|
continue;
|
|
61187
|
-
|
|
61188
|
-
|
|
61189
|
-
queuedCount: stickyQueued.filter((entry) => (entry.targetPath ?? entry.stageKey) === target).length,
|
|
61190
|
-
deliveryOnly: true
|
|
61191
|
-
});
|
|
61406
|
+
rowTargets.add(target);
|
|
61407
|
+
rows.push({ target, queuedCount: queuedByTarget.get(target) ?? 0, deliveryOnly: true });
|
|
61192
61408
|
}
|
|
61193
61409
|
for (let index = 0;index < rows.length; index++) {
|
|
61194
61410
|
const row = rows[index];
|
|
61195
|
-
const deliveryStates =
|
|
61196
|
-
if (deliveryStates
|
|
61411
|
+
const deliveryStates = statusesByTarget.get(row.target);
|
|
61412
|
+
if (deliveryStates !== undefined)
|
|
61197
61413
|
rows[index] = { ...row, deliveryStates };
|
|
61198
61414
|
}
|
|
61199
61415
|
return rows;
|
|
61200
61416
|
}
|
|
61201
|
-
function
|
|
61202
|
-
|
|
61203
|
-
|
|
61204
|
-
|
|
61205
|
-
|
|
61417
|
+
function materializedStageMatcher(runs, rootRunId) {
|
|
61418
|
+
const runById = new Map(runs.map((run) => [run.id, run]));
|
|
61419
|
+
const hopsFor = createWorkflowBoundaryHopsResolver(runs);
|
|
61420
|
+
const invocationRuns = runs.filter((run) => reciprocalWorkflowRootRunId(runById, run.id) === rootRunId);
|
|
61421
|
+
return (entrySegments) => materializedStageMatches(invocationRuns, hopsFor, entrySegments);
|
|
61422
|
+
}
|
|
61423
|
+
function materializedStageMatches(invocationRuns, hopsFor, entrySegments) {
|
|
61424
|
+
for (const run of invocationRuns) {
|
|
61425
|
+
const hops = hopsFor(run.id);
|
|
61206
61426
|
if (hops === undefined)
|
|
61207
61427
|
continue;
|
|
61208
61428
|
for (const stage of run.stages) {
|
|
@@ -61477,10 +61697,12 @@ async function recordConfirmedStickyDeliveries(activeStore, event) {
|
|
|
61477
61697
|
}
|
|
61478
61698
|
function matchingLiveStages(runs, rootRunId, patternSegments) {
|
|
61479
61699
|
const matches = [];
|
|
61700
|
+
const runById = new Map(runs.map((run) => [run.id, run]));
|
|
61701
|
+
const hopsFor = createWorkflowBoundaryHopsResolver(runs);
|
|
61480
61702
|
for (const run of runs) {
|
|
61481
|
-
if (
|
|
61703
|
+
if (reciprocalWorkflowRootRunId(runById, run.id) !== rootRunId)
|
|
61482
61704
|
continue;
|
|
61483
|
-
const hops =
|
|
61705
|
+
const hops = hopsFor(run.id);
|
|
61484
61706
|
if (hops === undefined)
|
|
61485
61707
|
continue;
|
|
61486
61708
|
for (const stage of run.stages) {
|
|
@@ -61616,11 +61838,26 @@ function writeMessage(socket, msg, callback) {
|
|
|
61616
61838
|
}
|
|
61617
61839
|
function createMessageReader(onMessage, onError) {
|
|
61618
61840
|
let buffer = Buffer.alloc(0);
|
|
61841
|
+
let pending = [];
|
|
61842
|
+
let pendingLength = 0;
|
|
61843
|
+
let awaitedLength = 0;
|
|
61619
61844
|
return (data) => {
|
|
61620
|
-
|
|
61845
|
+
if (pendingLength + data.length < awaitedLength) {
|
|
61846
|
+
pending.push(data);
|
|
61847
|
+
pendingLength += data.length;
|
|
61848
|
+
return;
|
|
61849
|
+
}
|
|
61850
|
+
buffer = pending.length === 0 ? Buffer.concat([buffer, data]) : Buffer.concat([...pending, data]);
|
|
61851
|
+
pending = [];
|
|
61852
|
+
pendingLength = 0;
|
|
61853
|
+
awaitedLength = 0;
|
|
61621
61854
|
while (buffer.length >= 4) {
|
|
61622
61855
|
const length = buffer.readUInt32BE(0);
|
|
61623
61856
|
if (buffer.length < 4 + length) {
|
|
61857
|
+
pending = [buffer];
|
|
61858
|
+
pendingLength = buffer.length;
|
|
61859
|
+
awaitedLength = 4 + length;
|
|
61860
|
+
buffer = Buffer.alloc(0);
|
|
61624
61861
|
break;
|
|
61625
61862
|
}
|
|
61626
61863
|
const payload = buffer.subarray(4, 4 + length);
|