@bastani/atomic 0.9.44-alpha.1 → 0.9.44-alpha.2
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 +16 -0
- package/dist/builtin/intercom/CHANGELOG.md +10 -0
- package/dist/builtin/intercom/broker/client-message-validation.ts +6 -0
- package/dist/builtin/intercom/broker/client.ts +4 -0
- package/dist/builtin/intercom/broker/send-signature.ts +4 -1
- package/dist/builtin/intercom/index.bundle.mjs +82 -14
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/skills/intercom/SKILL.md +1 -0
- package/dist/builtin/intercom/types.ts +8 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +6 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +94 -5
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +94 -5
- package/dist/builtin/workflows/src/index.js +94 -5
- package/dist/builtin/workflows/src/local-worker-entry.mjs +94 -5
- package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
- package/dist/core/agent-session-custom-message-commit.js +59 -0
- package/dist/core/agent-session-custom-message-commit.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +1 -0
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts +4 -0
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +10 -2
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +9 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/builtin-packages.d.ts.map +1 -1
- package/dist/core/builtin-packages.js +13 -13
- package/dist/core/builtin-packages.js.map +1 -1
- package/docs/intercom/operations.md +1 -1
- package/docs/intercom.md +11 -1
- package/docs/subagents.md +9 -1
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
|
@@ -1661,6 +1661,13 @@ function removeProcessExitCleanup(cleanup) {
|
|
|
1661
1661
|
if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
|
|
1662
1662
|
process.off("exit", runProcessExitCleanups);
|
|
1663
1663
|
}
|
|
1664
|
+
function snapshotNeedsHistory(tail) {
|
|
1665
|
+
const event = tail.snapshot.event;
|
|
1666
|
+
if (event.type !== RUN_EVENT_TYPES.messageQueued)
|
|
1667
|
+
return false;
|
|
1668
|
+
const payload = event.payload;
|
|
1669
|
+
return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
|
|
1670
|
+
}
|
|
1664
1671
|
function* invoke(action) {
|
|
1665
1672
|
try {
|
|
1666
1673
|
const result = action();
|
|
@@ -1776,8 +1783,11 @@ class JournalDurableBackend {
|
|
|
1776
1783
|
this.projection.applyWorkflowSnapshot(event.payload.handle);
|
|
1777
1784
|
this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
|
|
1778
1785
|
} else {
|
|
1779
|
-
const handle = event.payload;
|
|
1780
1786
|
const previous = this.projection.getWorkflow(entry.runId);
|
|
1787
|
+
const handle = event.type === RUN_EVENT_TYPES.messageQueued && hasPendingStageDeliveryDelta(event.payload.pendingStageMessages) ? {
|
|
1788
|
+
...event.payload,
|
|
1789
|
+
pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
|
|
1790
|
+
} : event.payload;
|
|
1781
1791
|
this.projection.applyWorkflowSnapshot(handle);
|
|
1782
1792
|
if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
|
|
1783
1793
|
this.prompts.delete(entry.runId);
|
|
@@ -2073,11 +2083,23 @@ class JournalDurableBackend {
|
|
|
2073
2083
|
for (const message of messages)
|
|
2074
2084
|
if (message.runId !== logicalRunId)
|
|
2075
2085
|
throw new JournalMessageScopeError(logicalRunId, message.runId);
|
|
2076
|
-
|
|
2086
|
+
const next = {
|
|
2077
2087
|
...handle,
|
|
2078
2088
|
pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
|
|
2079
2089
|
updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
|
|
2080
|
-
}
|
|
2090
|
+
};
|
|
2091
|
+
const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
|
|
2092
|
+
const base = this.sequences.get(runId);
|
|
2093
|
+
if (encoded !== undefined && base !== undefined) {
|
|
2094
|
+
try {
|
|
2095
|
+
yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
|
|
2096
|
+
return true;
|
|
2097
|
+
} catch (error) {
|
|
2098
|
+
if (!(error instanceof JournalCasConflictError))
|
|
2099
|
+
throw error;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
|
|
2081
2103
|
return true;
|
|
2082
2104
|
}
|
|
2083
2105
|
async archivePendingStageMessage(runId, message) {
|
|
@@ -2171,7 +2193,7 @@ class JournalDurableBackend {
|
|
|
2171
2193
|
if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
|
|
2172
2194
|
return;
|
|
2173
2195
|
const tail = yield* invoke(() => journal.snapshotTail(runId));
|
|
2174
|
-
if (tail !== undefined)
|
|
2196
|
+
if (tail !== undefined && !snapshotNeedsHistory(tail))
|
|
2175
2197
|
return this.summarize(runId, tail);
|
|
2176
2198
|
if (summarized === undefined && version.seq === 0)
|
|
2177
2199
|
return;
|
|
@@ -2397,6 +2419,69 @@ class JournalDurableBackend {
|
|
|
2397
2419
|
}
|
|
2398
2420
|
}
|
|
2399
2421
|
}
|
|
2422
|
+
function identity(message) {
|
|
2423
|
+
return JSON.stringify([message.runId, message.id]);
|
|
2424
|
+
}
|
|
2425
|
+
function uniqueByIdentity(messages) {
|
|
2426
|
+
const result = new Map;
|
|
2427
|
+
const repeated = new Set;
|
|
2428
|
+
for (const message of messages) {
|
|
2429
|
+
const key = identity(message);
|
|
2430
|
+
if (result.has(key))
|
|
2431
|
+
repeated.add(key);
|
|
2432
|
+
result.set(key, message);
|
|
2433
|
+
}
|
|
2434
|
+
for (const key of repeated)
|
|
2435
|
+
result.delete(key);
|
|
2436
|
+
return result;
|
|
2437
|
+
}
|
|
2438
|
+
function isDeltaEncoded(message) {
|
|
2439
|
+
return message.deliveryDelta !== undefined;
|
|
2440
|
+
}
|
|
2441
|
+
function encodePendingStageDeliveries(previous, next) {
|
|
2442
|
+
const bases = uniqueByIdentity(previous);
|
|
2443
|
+
const nextIdentities = uniqueByIdentity(next);
|
|
2444
|
+
let encodedAny = false;
|
|
2445
|
+
const encoded = next.map((message) => {
|
|
2446
|
+
const base = bases.get(identity(message))?.deliveries;
|
|
2447
|
+
const deliveries = message.deliveries;
|
|
2448
|
+
if (base === undefined || deliveries === undefined || !nextIdentities.has(identity(message)))
|
|
2449
|
+
return message;
|
|
2450
|
+
const drop = [];
|
|
2451
|
+
let kept = 0;
|
|
2452
|
+
base.forEach((delivery, index) => {
|
|
2453
|
+
if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
|
|
2454
|
+
kept++;
|
|
2455
|
+
else
|
|
2456
|
+
drop.push(index);
|
|
2457
|
+
});
|
|
2458
|
+
const add = deliveries.slice(kept);
|
|
2459
|
+
if (drop.length + add.length >= deliveries.length)
|
|
2460
|
+
return message;
|
|
2461
|
+
encodedAny = true;
|
|
2462
|
+
const { deliveries: _full, ...rest } = message;
|
|
2463
|
+
return { ...rest, deliveryDelta: drop.length === 0 ? { add } : { drop, add } };
|
|
2464
|
+
});
|
|
2465
|
+
return encodedAny ? encoded : undefined;
|
|
2466
|
+
}
|
|
2467
|
+
function decodePendingStageDeliveries(previous, messages) {
|
|
2468
|
+
if (!messages.some(isDeltaEncoded))
|
|
2469
|
+
return messages;
|
|
2470
|
+
const bases = uniqueByIdentity(previous);
|
|
2471
|
+
return messages.map((message) => {
|
|
2472
|
+
if (!isDeltaEncoded(message))
|
|
2473
|
+
return message;
|
|
2474
|
+
const base = bases.get(identity(message))?.deliveries;
|
|
2475
|
+
if (base === undefined)
|
|
2476
|
+
throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
|
|
2477
|
+
const { deliveryDelta, ...rest } = message;
|
|
2478
|
+
const dropped = new Set(deliveryDelta.drop ?? []);
|
|
2479
|
+
return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
function hasPendingStageDeliveryDelta(messages) {
|
|
2483
|
+
return messages?.some(isDeltaEncoded) === true;
|
|
2484
|
+
}
|
|
2400
2485
|
var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
|
|
2401
2486
|
var init_journal_backend = __esm(() => {
|
|
2402
2487
|
init_workflow_instance_ownership_error();
|
|
@@ -58403,7 +58488,8 @@ function buildSendSignature(to, options) {
|
|
|
58403
58488
|
attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
|
|
58404
58489
|
replyTo: options.replyTo ?? null,
|
|
58405
58490
|
expectsReply: options.expectsReply ?? false,
|
|
58406
|
-
replyError: options.replyError ?? null
|
|
58491
|
+
replyError: options.replyError ?? null,
|
|
58492
|
+
...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
|
|
58407
58493
|
});
|
|
58408
58494
|
}
|
|
58409
58495
|
|
|
@@ -58589,6 +58675,8 @@ function isMessage(value2) {
|
|
|
58589
58675
|
return false;
|
|
58590
58676
|
if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
|
|
58591
58677
|
return false;
|
|
58678
|
+
if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
|
|
58679
|
+
return false;
|
|
58592
58680
|
if (message.replyError !== undefined && typeof message.replyError !== "string")
|
|
58593
58681
|
return false;
|
|
58594
58682
|
if (message.source !== undefined && !isMessageSource(message.source))
|
|
@@ -59798,6 +59886,7 @@ var init_client = __esm(() => {
|
|
|
59798
59886
|
timestamp: Date.now(),
|
|
59799
59887
|
replyTo: options.replyTo,
|
|
59800
59888
|
expectsReply: options.expectsReply,
|
|
59889
|
+
...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
|
|
59801
59890
|
replyError: options.replyError,
|
|
59802
59891
|
source: this._messageSource,
|
|
59803
59892
|
content: { text: options.text, attachments: options.attachments }
|
|
@@ -9870,6 +9870,13 @@ function removeProcessExitCleanup(cleanup) {
|
|
|
9870
9870
|
if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
|
|
9871
9871
|
process.off("exit", runProcessExitCleanups);
|
|
9872
9872
|
}
|
|
9873
|
+
function snapshotNeedsHistory(tail) {
|
|
9874
|
+
const event = tail.snapshot.event;
|
|
9875
|
+
if (event.type !== RUN_EVENT_TYPES.messageQueued)
|
|
9876
|
+
return false;
|
|
9877
|
+
const payload = event.payload;
|
|
9878
|
+
return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
|
|
9879
|
+
}
|
|
9873
9880
|
function* invoke(action) {
|
|
9874
9881
|
try {
|
|
9875
9882
|
const result = action();
|
|
@@ -9985,8 +9992,11 @@ class JournalDurableBackend {
|
|
|
9985
9992
|
this.projection.applyWorkflowSnapshot(event.payload.handle);
|
|
9986
9993
|
this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
|
|
9987
9994
|
} else {
|
|
9988
|
-
const handle = event.payload;
|
|
9989
9995
|
const previous = this.projection.getWorkflow(entry.runId);
|
|
9996
|
+
const handle = event.type === RUN_EVENT_TYPES.messageQueued && hasPendingStageDeliveryDelta(event.payload.pendingStageMessages) ? {
|
|
9997
|
+
...event.payload,
|
|
9998
|
+
pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
|
|
9999
|
+
} : event.payload;
|
|
9990
10000
|
this.projection.applyWorkflowSnapshot(handle);
|
|
9991
10001
|
if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
|
|
9992
10002
|
this.prompts.delete(entry.runId);
|
|
@@ -10282,11 +10292,23 @@ class JournalDurableBackend {
|
|
|
10282
10292
|
for (const message of messages)
|
|
10283
10293
|
if (message.runId !== logicalRunId)
|
|
10284
10294
|
throw new JournalMessageScopeError(logicalRunId, message.runId);
|
|
10285
|
-
|
|
10295
|
+
const next = {
|
|
10286
10296
|
...handle,
|
|
10287
10297
|
pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
|
|
10288
10298
|
updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
|
|
10289
|
-
}
|
|
10299
|
+
};
|
|
10300
|
+
const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
|
|
10301
|
+
const base = this.sequences.get(runId);
|
|
10302
|
+
if (encoded !== undefined && base !== undefined) {
|
|
10303
|
+
try {
|
|
10304
|
+
yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
|
|
10305
|
+
return true;
|
|
10306
|
+
} catch (error) {
|
|
10307
|
+
if (!(error instanceof JournalCasConflictError))
|
|
10308
|
+
throw error;
|
|
10309
|
+
}
|
|
10310
|
+
}
|
|
10311
|
+
yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
|
|
10290
10312
|
return true;
|
|
10291
10313
|
}
|
|
10292
10314
|
async archivePendingStageMessage(runId, message) {
|
|
@@ -10380,7 +10402,7 @@ class JournalDurableBackend {
|
|
|
10380
10402
|
if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
|
|
10381
10403
|
return;
|
|
10382
10404
|
const tail = yield* invoke(() => journal.snapshotTail(runId));
|
|
10383
|
-
if (tail !== undefined)
|
|
10405
|
+
if (tail !== undefined && !snapshotNeedsHistory(tail))
|
|
10384
10406
|
return this.summarize(runId, tail);
|
|
10385
10407
|
if (summarized === undefined && version.seq === 0)
|
|
10386
10408
|
return;
|
|
@@ -10606,6 +10628,69 @@ class JournalDurableBackend {
|
|
|
10606
10628
|
}
|
|
10607
10629
|
}
|
|
10608
10630
|
}
|
|
10631
|
+
function identity(message) {
|
|
10632
|
+
return JSON.stringify([message.runId, message.id]);
|
|
10633
|
+
}
|
|
10634
|
+
function uniqueByIdentity(messages) {
|
|
10635
|
+
const result = new Map;
|
|
10636
|
+
const repeated = new Set;
|
|
10637
|
+
for (const message of messages) {
|
|
10638
|
+
const key = identity(message);
|
|
10639
|
+
if (result.has(key))
|
|
10640
|
+
repeated.add(key);
|
|
10641
|
+
result.set(key, message);
|
|
10642
|
+
}
|
|
10643
|
+
for (const key of repeated)
|
|
10644
|
+
result.delete(key);
|
|
10645
|
+
return result;
|
|
10646
|
+
}
|
|
10647
|
+
function isDeltaEncoded(message) {
|
|
10648
|
+
return message.deliveryDelta !== undefined;
|
|
10649
|
+
}
|
|
10650
|
+
function encodePendingStageDeliveries(previous, next) {
|
|
10651
|
+
const bases = uniqueByIdentity(previous);
|
|
10652
|
+
const nextIdentities = uniqueByIdentity(next);
|
|
10653
|
+
let encodedAny = false;
|
|
10654
|
+
const encoded = next.map((message) => {
|
|
10655
|
+
const base = bases.get(identity(message))?.deliveries;
|
|
10656
|
+
const deliveries = message.deliveries;
|
|
10657
|
+
if (base === undefined || deliveries === undefined || !nextIdentities.has(identity(message)))
|
|
10658
|
+
return message;
|
|
10659
|
+
const drop = [];
|
|
10660
|
+
let kept = 0;
|
|
10661
|
+
base.forEach((delivery, index) => {
|
|
10662
|
+
if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
|
|
10663
|
+
kept++;
|
|
10664
|
+
else
|
|
10665
|
+
drop.push(index);
|
|
10666
|
+
});
|
|
10667
|
+
const add = deliveries.slice(kept);
|
|
10668
|
+
if (drop.length + add.length >= deliveries.length)
|
|
10669
|
+
return message;
|
|
10670
|
+
encodedAny = true;
|
|
10671
|
+
const { deliveries: _full, ...rest } = message;
|
|
10672
|
+
return { ...rest, deliveryDelta: drop.length === 0 ? { add } : { drop, add } };
|
|
10673
|
+
});
|
|
10674
|
+
return encodedAny ? encoded : undefined;
|
|
10675
|
+
}
|
|
10676
|
+
function decodePendingStageDeliveries(previous, messages) {
|
|
10677
|
+
if (!messages.some(isDeltaEncoded))
|
|
10678
|
+
return messages;
|
|
10679
|
+
const bases = uniqueByIdentity(previous);
|
|
10680
|
+
return messages.map((message) => {
|
|
10681
|
+
if (!isDeltaEncoded(message))
|
|
10682
|
+
return message;
|
|
10683
|
+
const base = bases.get(identity(message))?.deliveries;
|
|
10684
|
+
if (base === undefined)
|
|
10685
|
+
throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
|
|
10686
|
+
const { deliveryDelta, ...rest } = message;
|
|
10687
|
+
const dropped = new Set(deliveryDelta.drop ?? []);
|
|
10688
|
+
return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
|
|
10689
|
+
});
|
|
10690
|
+
}
|
|
10691
|
+
function hasPendingStageDeliveryDelta(messages) {
|
|
10692
|
+
return messages?.some(isDeltaEncoded) === true;
|
|
10693
|
+
}
|
|
10609
10694
|
var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
|
|
10610
10695
|
var init_journal_backend = __esm(() => {
|
|
10611
10696
|
init_workflow_instance_ownership_error();
|
|
@@ -59086,7 +59171,8 @@ function buildSendSignature(to, options) {
|
|
|
59086
59171
|
attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
|
|
59087
59172
|
replyTo: options.replyTo ?? null,
|
|
59088
59173
|
expectsReply: options.expectsReply ?? false,
|
|
59089
|
-
replyError: options.replyError ?? null
|
|
59174
|
+
replyError: options.replyError ?? null,
|
|
59175
|
+
...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
|
|
59090
59176
|
});
|
|
59091
59177
|
}
|
|
59092
59178
|
|
|
@@ -59272,6 +59358,8 @@ function isMessage(value2) {
|
|
|
59272
59358
|
return false;
|
|
59273
59359
|
if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
|
|
59274
59360
|
return false;
|
|
59361
|
+
if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
|
|
59362
|
+
return false;
|
|
59275
59363
|
if (message.replyError !== undefined && typeof message.replyError !== "string")
|
|
59276
59364
|
return false;
|
|
59277
59365
|
if (message.source !== undefined && !isMessageSource(message.source))
|
|
@@ -60481,6 +60569,7 @@ var init_client = __esm(() => {
|
|
|
60481
60569
|
timestamp: Date.now(),
|
|
60482
60570
|
replyTo: options.replyTo,
|
|
60483
60571
|
expectsReply: options.expectsReply,
|
|
60572
|
+
...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
|
|
60484
60573
|
replyError: options.replyError,
|
|
60485
60574
|
source: this._messageSource,
|
|
60486
60575
|
content: { text: options.text, attachments: options.attachments }
|
|
@@ -1661,6 +1661,13 @@ function removeProcessExitCleanup(cleanup) {
|
|
|
1661
1661
|
if (processExitCleanups.delete(cleanup) && processExitCleanups.size === 0)
|
|
1662
1662
|
process.off("exit", runProcessExitCleanups);
|
|
1663
1663
|
}
|
|
1664
|
+
function snapshotNeedsHistory(tail) {
|
|
1665
|
+
const event = tail.snapshot.event;
|
|
1666
|
+
if (event.type !== RUN_EVENT_TYPES.messageQueued)
|
|
1667
|
+
return false;
|
|
1668
|
+
const payload = event.payload;
|
|
1669
|
+
return hasPendingStageDeliveryDelta(payload.pendingStageMessages);
|
|
1670
|
+
}
|
|
1664
1671
|
function* invoke(action) {
|
|
1665
1672
|
try {
|
|
1666
1673
|
const result = action();
|
|
@@ -1776,8 +1783,11 @@ class JournalDurableBackend {
|
|
|
1776
1783
|
this.projection.applyWorkflowSnapshot(event.payload.handle);
|
|
1777
1784
|
this.prompts.set(entry.runId, new PromptReservationState(event.payload.handle.pendingPrompts, event.payload.prompts));
|
|
1778
1785
|
} else {
|
|
1779
|
-
const handle = event.payload;
|
|
1780
1786
|
const previous = this.projection.getWorkflow(entry.runId);
|
|
1787
|
+
const handle = event.type === RUN_EVENT_TYPES.messageQueued && hasPendingStageDeliveryDelta(event.payload.pendingStageMessages) ? {
|
|
1788
|
+
...event.payload,
|
|
1789
|
+
pendingStageMessages: decodePendingStageDeliveries(previous?.pendingStageMessages ?? [], event.payload.pendingStageMessages ?? [])
|
|
1790
|
+
} : event.payload;
|
|
1781
1791
|
this.projection.applyWorkflowSnapshot(handle);
|
|
1782
1792
|
if (event.type === RUN_EVENT_TYPES.runStarted || previous?.pendingPrompts !== handle.pendingPrompts)
|
|
1783
1793
|
this.prompts.delete(entry.runId);
|
|
@@ -2073,11 +2083,23 @@ class JournalDurableBackend {
|
|
|
2073
2083
|
for (const message of messages)
|
|
2074
2084
|
if (message.runId !== logicalRunId)
|
|
2075
2085
|
throw new JournalMessageScopeError(logicalRunId, message.runId);
|
|
2076
|
-
|
|
2086
|
+
const next = {
|
|
2077
2087
|
...handle,
|
|
2078
2088
|
pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages),
|
|
2079
2089
|
updatedAt: Math.max(Date.now(), handle.updatedAt + 1)
|
|
2080
|
-
}
|
|
2090
|
+
};
|
|
2091
|
+
const encoded = encodePendingStageDeliveries(handle.pendingStageMessages ?? [], next.pendingStageMessages ?? []);
|
|
2092
|
+
const base = this.sequences.get(runId);
|
|
2093
|
+
if (encoded !== undefined && base !== undefined) {
|
|
2094
|
+
try {
|
|
2095
|
+
yield* this.append(runId, { type: RUN_EVENT_TYPES.messageQueued, payload: { ...next, pendingStageMessages: encoded } }, { expectSeq: base });
|
|
2096
|
+
return true;
|
|
2097
|
+
} catch (error) {
|
|
2098
|
+
if (!(error instanceof JournalCasConflictError))
|
|
2099
|
+
throw error;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
yield* this.snapshot(next, RUN_EVENT_TYPES.messageQueued);
|
|
2081
2103
|
return true;
|
|
2082
2104
|
}
|
|
2083
2105
|
async archivePendingStageMessage(runId, message) {
|
|
@@ -2171,7 +2193,7 @@ class JournalDurableBackend {
|
|
|
2171
2193
|
if (summarized !== undefined && summarized.seq === version.seq && summarized.at === version.at)
|
|
2172
2194
|
return;
|
|
2173
2195
|
const tail = yield* invoke(() => journal.snapshotTail(runId));
|
|
2174
|
-
if (tail !== undefined)
|
|
2196
|
+
if (tail !== undefined && !snapshotNeedsHistory(tail))
|
|
2175
2197
|
return this.summarize(runId, tail);
|
|
2176
2198
|
if (summarized === undefined && version.seq === 0)
|
|
2177
2199
|
return;
|
|
@@ -2397,6 +2419,69 @@ class JournalDurableBackend {
|
|
|
2397
2419
|
}
|
|
2398
2420
|
}
|
|
2399
2421
|
}
|
|
2422
|
+
function identity(message) {
|
|
2423
|
+
return JSON.stringify([message.runId, message.id]);
|
|
2424
|
+
}
|
|
2425
|
+
function uniqueByIdentity(messages) {
|
|
2426
|
+
const result = new Map;
|
|
2427
|
+
const repeated = new Set;
|
|
2428
|
+
for (const message of messages) {
|
|
2429
|
+
const key = identity(message);
|
|
2430
|
+
if (result.has(key))
|
|
2431
|
+
repeated.add(key);
|
|
2432
|
+
result.set(key, message);
|
|
2433
|
+
}
|
|
2434
|
+
for (const key of repeated)
|
|
2435
|
+
result.delete(key);
|
|
2436
|
+
return result;
|
|
2437
|
+
}
|
|
2438
|
+
function isDeltaEncoded(message) {
|
|
2439
|
+
return message.deliveryDelta !== undefined;
|
|
2440
|
+
}
|
|
2441
|
+
function encodePendingStageDeliveries(previous, next) {
|
|
2442
|
+
const bases = uniqueByIdentity(previous);
|
|
2443
|
+
const nextIdentities = uniqueByIdentity(next);
|
|
2444
|
+
let encodedAny = false;
|
|
2445
|
+
const encoded = next.map((message) => {
|
|
2446
|
+
const base = bases.get(identity(message))?.deliveries;
|
|
2447
|
+
const deliveries = message.deliveries;
|
|
2448
|
+
if (base === undefined || deliveries === undefined || !nextIdentities.has(identity(message)))
|
|
2449
|
+
return message;
|
|
2450
|
+
const drop = [];
|
|
2451
|
+
let kept = 0;
|
|
2452
|
+
base.forEach((delivery, index) => {
|
|
2453
|
+
if (kept < deliveries.length && JSON.stringify(delivery) === JSON.stringify(deliveries[kept]))
|
|
2454
|
+
kept++;
|
|
2455
|
+
else
|
|
2456
|
+
drop.push(index);
|
|
2457
|
+
});
|
|
2458
|
+
const add = deliveries.slice(kept);
|
|
2459
|
+
if (drop.length + add.length >= deliveries.length)
|
|
2460
|
+
return message;
|
|
2461
|
+
encodedAny = true;
|
|
2462
|
+
const { deliveries: _full, ...rest } = message;
|
|
2463
|
+
return { ...rest, deliveryDelta: drop.length === 0 ? { add } : { drop, add } };
|
|
2464
|
+
});
|
|
2465
|
+
return encodedAny ? encoded : undefined;
|
|
2466
|
+
}
|
|
2467
|
+
function decodePendingStageDeliveries(previous, messages) {
|
|
2468
|
+
if (!messages.some(isDeltaEncoded))
|
|
2469
|
+
return messages;
|
|
2470
|
+
const bases = uniqueByIdentity(previous);
|
|
2471
|
+
return messages.map((message) => {
|
|
2472
|
+
if (!isDeltaEncoded(message))
|
|
2473
|
+
return message;
|
|
2474
|
+
const base = bases.get(identity(message))?.deliveries;
|
|
2475
|
+
if (base === undefined)
|
|
2476
|
+
throw new Error(`atomic-workflows: journal delivery change for ${identity(message)} has no base entry`);
|
|
2477
|
+
const { deliveryDelta, ...rest } = message;
|
|
2478
|
+
const dropped = new Set(deliveryDelta.drop ?? []);
|
|
2479
|
+
return { ...rest, deliveries: [...base.filter((_, index) => !dropped.has(index)), ...deliveryDelta.add] };
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
function hasPendingStageDeliveryDelta(messages) {
|
|
2483
|
+
return messages?.some(isDeltaEncoded) === true;
|
|
2484
|
+
}
|
|
2400
2485
|
var PROCESS_HOLDER_ID, processExitCleanups, JournalOwnershipError, JournalBackendClosedError, JournalOperationAbortedError, JournalExecutionRetiredError, JournalMessageScopeError;
|
|
2401
2486
|
var init_journal_backend = __esm(() => {
|
|
2402
2487
|
init_workflow_instance_ownership_error();
|
|
@@ -58403,7 +58488,8 @@ function buildSendSignature(to, options) {
|
|
|
58403
58488
|
attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
|
|
58404
58489
|
replyTo: options.replyTo ?? null,
|
|
58405
58490
|
expectsReply: options.expectsReply ?? false,
|
|
58406
|
-
replyError: options.replyError ?? null
|
|
58491
|
+
replyError: options.replyError ?? null,
|
|
58492
|
+
...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery }
|
|
58407
58493
|
});
|
|
58408
58494
|
}
|
|
58409
58495
|
|
|
@@ -58589,6 +58675,8 @@ function isMessage(value2) {
|
|
|
58589
58675
|
return false;
|
|
58590
58676
|
if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean")
|
|
58591
58677
|
return false;
|
|
58678
|
+
if (message.delivery !== undefined && message.delivery !== "steer" && message.delivery !== "followUp" && message.delivery !== "interrupt")
|
|
58679
|
+
return false;
|
|
58592
58680
|
if (message.replyError !== undefined && typeof message.replyError !== "string")
|
|
58593
58681
|
return false;
|
|
58594
58682
|
if (message.source !== undefined && !isMessageSource(message.source))
|
|
@@ -59798,6 +59886,7 @@ var init_client = __esm(() => {
|
|
|
59798
59886
|
timestamp: Date.now(),
|
|
59799
59887
|
replyTo: options.replyTo,
|
|
59800
59888
|
expectsReply: options.expectsReply,
|
|
59889
|
+
...options.delivery === undefined || options.delivery === "steer" ? {} : { delivery: options.delivery },
|
|
59801
59890
|
replyError: options.replyError,
|
|
59802
59891
|
source: this._messageSource,
|
|
59803
59892
|
content: { text: options.text, attachments: options.attachments }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-session-custom-message-commit.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-custom-message-commit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-session-custom-message-commit.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-custom-message-commit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAQ9F,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAmEnD,6EAA6E;AAC7E,wBAAsB,2BAA2B,CAAC,CAAC,EAClD,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA+Gf;AAED,wBAAgB,4BAA4B,CAAC,CAAC,EAC7C,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,EAC5B,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,+EAA+E;AAC/E,wBAAsB,4BAA4B,CAAC,CAAC,EACnD,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Df;AAED,wBAAgB,6BAA6B,CAAC,CAAC,EAC9C,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAC/B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,eAAO,MAAM,sCAAsC;;;CAGlD,CAAC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { resolveWorkflowStageDeliveryTarget } from "./agent-session-delivery-forwarding.js";
|
|
2
2
|
import { admitProtectedStreamingCustomMessage, queueProtectedStreamingCustomMessage, queueProtectedStreamingCustomMessages, triggerProtectedStreamingCustomMessages, } from "./agent-session-persistent-custom-messages.js";
|
|
3
|
+
import { deliverQueuedInputIfIdle } from "./agent-session-prompt.js";
|
|
4
|
+
import { TASK_COMPLETION_MESSAGE_TYPE } from "./tasks/completion.js";
|
|
3
5
|
async function appendDurableStageAdmission(session, message) {
|
|
4
6
|
await session._appendCustomMessage(message);
|
|
5
7
|
}
|
|
@@ -23,15 +25,54 @@ async function commitPriorityMessage(session, message) {
|
|
|
23
25
|
void turn.catch(() => { });
|
|
24
26
|
}
|
|
25
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* #3919: before a fresh stage's own prompt is submitted, turn-starting input is that prompt's startup context.
|
|
30
|
+
* Once the prompt is in preflight, a run has started, or queued input is paused, the ordinary admission paths
|
|
31
|
+
* own ordering. The stage's own task completions follow its own work, so they never precede its prompt.
|
|
32
|
+
*/
|
|
33
|
+
function holdsForFirstStagePrompt(session, message, options) {
|
|
34
|
+
return (session._workflowStageAwaitingFirstPrompt &&
|
|
35
|
+
session._activePromptCount === 0 &&
|
|
36
|
+
!session._queuedMessagesPaused &&
|
|
37
|
+
message.customType !== TASK_COMPLETION_MESSAGE_TYPE &&
|
|
38
|
+
options?.triggerTurn === true &&
|
|
39
|
+
options.excludeFromContext !== true &&
|
|
40
|
+
options.deliverAs !== "nextTurn");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* #3919: once the stage prompt is submitted but before it owns a run (its input hooks), a turn-starting
|
|
44
|
+
* message must not start a competing turn. It queues behind the pending prompt; interrupts keep their own path.
|
|
45
|
+
*/
|
|
46
|
+
function queuesBehindFirstStagePrompt(session, options) {
|
|
47
|
+
return (session._workflowStageAwaitingFirstPrompt &&
|
|
48
|
+
session._activePromptCount > 0 &&
|
|
49
|
+
!session._queuedMessagesPaused &&
|
|
50
|
+
options?.triggerTurn === true &&
|
|
51
|
+
options.excludeFromContext !== true &&
|
|
52
|
+
options.deliverAs !== "nextTurn" &&
|
|
53
|
+
options.deliverAs !== "interrupt");
|
|
54
|
+
}
|
|
26
55
|
/** Commit one delivery whose source generation already granted admission. */
|
|
27
56
|
export async function commitAdmittedCustomMessage(session, appMessage, options) {
|
|
28
57
|
const owner = resolveWorkflowStageDeliveryTarget(session);
|
|
29
58
|
if (owner !== session)
|
|
30
59
|
return commitAdmittedCustomMessage(owner, appMessage, options);
|
|
31
60
|
const self = session;
|
|
61
|
+
if (holdsForFirstStagePrompt(self, appMessage, options)) {
|
|
62
|
+
await appendDurableStageAdmission(self, appMessage);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
32
65
|
const useProtectedReconciliation = options?.persistWhenStreaming === true &&
|
|
33
66
|
options.excludeFromContext !== true &&
|
|
34
67
|
(options.triggerTurn === true || options.stageAdmissionKey !== undefined);
|
|
68
|
+
if (queuesBehindFirstStagePrompt(self, options)) {
|
|
69
|
+
const delivery = options?.deliverAs === "followUp" ? "followUp" : "steer";
|
|
70
|
+
if (useProtectedReconciliation)
|
|
71
|
+
await queueProtectedStreamingCustomMessage(self, appMessage, delivery);
|
|
72
|
+
else
|
|
73
|
+
self._queueAgentMessage(appMessage, delivery);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
35
76
|
if (useProtectedReconciliation &&
|
|
36
77
|
options?.triggerTurn &&
|
|
37
78
|
options.deliverAs === "interrupt" &&
|
|
@@ -78,6 +119,9 @@ export async function commitAdmittedCustomMessage(session, appMessage, options)
|
|
|
78
119
|
}
|
|
79
120
|
else if ((self.isStreaming || self._isPreparingRun) && useProtectedReconciliation) {
|
|
80
121
|
await queueProtectedStreamingCustomMessage(self, appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
|
|
122
|
+
// The durable card append can yield past the run's last queue drain.
|
|
123
|
+
if (!self.isStreaming)
|
|
124
|
+
deliverQueuedInputIfIdle(self);
|
|
81
125
|
}
|
|
82
126
|
else if ((self.isStreaming || self._isPreparingRun) && options?.persistWhenStreaming === true) {
|
|
83
127
|
await appendDurableStageAdmission(self, appMessage);
|
|
@@ -130,8 +174,21 @@ export async function commitAdmittedCustomMessages(session, appMessages, options
|
|
|
130
174
|
if (owner !== session)
|
|
131
175
|
return commitAdmittedCustomMessages(owner, appMessages, options);
|
|
132
176
|
const self = session;
|
|
177
|
+
if (appMessages.every((item) => holdsForFirstStagePrompt(self, item, options))) {
|
|
178
|
+
for (const item of appMessages)
|
|
179
|
+
await appendDurableStageAdmission(self, item);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
133
182
|
const useProtectedReconciliation = options?.persistWhenStreaming === true && options.triggerTurn === true && options.excludeFromContext !== true;
|
|
134
183
|
const delivery = options?.deliverAs === "followUp" ? "followUp" : "steer";
|
|
184
|
+
if (queuesBehindFirstStagePrompt(self, options)) {
|
|
185
|
+
if (useProtectedReconciliation)
|
|
186
|
+
await queueProtectedStreamingCustomMessages(self, appMessages, delivery);
|
|
187
|
+
else
|
|
188
|
+
for (const item of appMessages)
|
|
189
|
+
self._queueAgentMessage(item, delivery);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
135
192
|
if (options?.deliverAs === "nextTurn") {
|
|
136
193
|
self._pendingNextTurnMessages.push(...appMessages);
|
|
137
194
|
}
|
|
@@ -166,6 +223,8 @@ export async function commitAdmittedCustomMessages(session, appMessages, options
|
|
|
166
223
|
}
|
|
167
224
|
else if ((self.isStreaming || self._isPreparingRun) && useProtectedReconciliation) {
|
|
168
225
|
await queueProtectedStreamingCustomMessages(self, appMessages, delivery);
|
|
226
|
+
if (!self.isStreaming)
|
|
227
|
+
deliverQueuedInputIfIdle(self);
|
|
169
228
|
}
|
|
170
229
|
else if ((self.isStreaming || self._isPreparingRun) && options?.persistWhenStreaming === true) {
|
|
171
230
|
for (const item of appMessages)
|