@yielded/agent 0.1.0-beta.175 → 0.1.0-beta.176
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/{Admin-Qa3I1oA5.d.mts → Admin-DTDihBra.d.mts} +13 -7
- package/dist/{DurableAgentRuntime-CPlw5_7e.mjs → DurableAgentRuntime-BJbuIqQq.mjs} +84 -37
- package/dist/DurableAgentRuntime-BJbuIqQq.mjs.map +1 -0
- package/dist/{EventSource-BTu68yDW.d.mts → EventSource-Cc58uEso.d.mts} +2 -2
- package/dist/{MessageDelivery-DjAZJ4Fd.d.mts → MessageDelivery-ClHg7c0O.d.mts} +42 -42
- package/dist/{PreparedInputAdmission-wgY4JMZY.d.mts → PreparedInputAdmission-CjcX1OP0.d.mts} +3 -3
- package/dist/{RunContinuation-BiyKJK32.mjs → RunContinuation-CWHuciup.mjs} +3 -3
- package/dist/RunContinuation-CWHuciup.mjs.map +1 -0
- package/dist/{RunContinuation-3jB5pULC.d.mts → RunContinuation-DSnUTSSb.d.mts} +4 -4
- package/dist/{RunJournal-CPMKcfd0.mjs → RunJournal-DGblbFas.mjs} +5 -5
- package/dist/{RunJournal-CPMKcfd0.mjs.map → RunJournal-DGblbFas.mjs.map} +1 -1
- package/dist/{RunStorage-CzutHJBE.mjs → RunStorage-BF2YgWkg.mjs} +43 -33
- package/dist/RunStorage-BF2YgWkg.mjs.map +1 -0
- package/dist/{Subscription-DmIHlOJK.d.mts → Subscription-DuC_PcGx.d.mts} +2 -2
- package/dist/{SubscriptionInput-DgcQhvbe.d.mts → SubscriptionInput-B1IeAEDi.d.mts} +2 -2
- package/dist/{ThreadImport-DP-vOk2F.d.mts → ThreadImport-D_Biid6w.d.mts} +42 -42
- package/dist/{ThreadImport-CjlG57vH.mjs → ThreadImport-ZgWxf7bB.mjs} +5 -5
- package/dist/{ThreadImport-CjlG57vH.mjs.map → ThreadImport-ZgWxf7bB.mjs.map} +1 -1
- package/dist/{ThreadStore-EuUilthA.mjs → ThreadStore-BC4V-mY-.mjs} +39 -18
- package/dist/ThreadStore-BC4V-mY-.mjs.map +1 -0
- package/dist/{ThreadTransfer-DB_zsrtJ.mjs → ThreadTransfer-S7FnTRHt.mjs} +2 -2
- package/dist/{ThreadTransfer-DB_zsrtJ.mjs.map → ThreadTransfer-S7FnTRHt.mjs.map} +1 -1
- package/dist/{WorkerAdmission-eBeXt_mu.mjs → WorkerAdmission-C47Vy_cK.mjs} +3 -3
- package/dist/{WorkerAdmission-eBeXt_mu.mjs.map → WorkerAdmission-C47Vy_cK.mjs.map} +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/AgentRegistration.d.mts +1 -1
- package/dist/durable/Certification.d.mts +1 -1
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/CommittedActivity.mjs +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +2 -2
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/LifecyclePublication.d.mts +1 -1
- package/dist/durable/LifecyclePublication.mjs +1 -1
- package/dist/durable/MessageDelivery.d.mts +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +2 -2
- package/dist/durable/PersistentHistory.d.mts +1 -1
- package/dist/durable/PersistentHistory.mjs +3 -3
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/RunContinuation.d.mts +2 -2
- package/dist/durable/RunContinuation.mjs +2 -2
- package/dist/durable/RunJournal.d.mts +1 -1
- package/dist/durable/RunJournal.mjs +1 -1
- package/dist/durable/RunStorage.d.mts +1 -1
- package/dist/durable/RunStorage.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +1 -1
- package/dist/durable/SettlementPublisher.d.mts +1 -1
- package/dist/durable/SettlementPublisher.mjs +2 -2
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs.map +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +3 -3
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionTools.d.mts +2 -2
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/Subscriptions.d.mts +5 -5
- package/dist/durable/ThreadArchiveRange.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.mjs +1 -1
- package/dist/durable/ThreadImport.d.mts +1 -1
- package/dist/durable/ThreadImport.mjs +1 -1
- package/dist/durable/ThreadInvariants.d.mts +3 -3
- package/dist/durable/ThreadInvariants.mjs +3 -3
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadReader.d.mts +1 -1
- package/dist/durable/ThreadReader.mjs +1 -1
- package/dist/durable/ThreadStore.d.mts +1 -1
- package/dist/durable/ThreadStore.mjs +1 -1
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/ThreadStoreConformance.mjs +3 -3
- package/dist/durable/ThreadTransfer.d.mts +1 -1
- package/dist/durable/ThreadTransfer.mjs +1 -1
- package/dist/durable/ThreadWork.d.mts +1 -1
- package/dist/durable/ThreadWork.mjs +3 -3
- package/dist/durable/WorkerAdmission.d.mts +68 -68
- package/dist/durable/WorkerAdmission.mjs +1 -1
- package/dist/durable/WorkerHost.d.mts +1 -1
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +7 -7
- package/dist/{run-context-reader-DVhJZ1Qe.d.mts → run-context-reader-DvTEq2RC.d.mts} +2 -2
- package/package.json +1 -1
- package/src/durable/DurableAgentRuntime.ts +169 -55
- package/src/durable/RunContinuation.ts +2 -2
- package/src/durable/RunJournal.ts +9 -5
- package/src/durable/RunStorage.ts +83 -61
- package/src/durable/SubmissionLedger.ts +8 -0
- package/src/durable/ThreadStore.ts +39 -19
- package/src/durable/internal/initial-context.ts +17 -2
- package/src/durable/internal/prompt-history-cache.ts +13 -8
- package/dist/DurableAgentRuntime-CPlw5_7e.mjs.map +0 -1
- package/dist/RunContinuation-BiyKJK32.mjs.map +0 -1
- package/dist/RunStorage-CzutHJBE.mjs.map +0 -1
- package/dist/ThreadStore-EuUilthA.mjs.map +0 -1
|
@@ -158,7 +158,7 @@ import {
|
|
|
158
158
|
} from "./internal/agent-registration.ts";
|
|
159
159
|
import { makeAgentUpdateRuntime } from "./internal/agent-updates.ts";
|
|
160
160
|
import { inspectForeignDiagnostic, safeUnknownString } from "./internal/foreign-diagnostic.ts";
|
|
161
|
-
import { initialContext } from "./internal/initial-context.ts";
|
|
161
|
+
import { initialContext, type InitialContextInput } from "./internal/initial-context.ts";
|
|
162
162
|
import {
|
|
163
163
|
makeJournalMetadata,
|
|
164
164
|
type JournalMetadata,
|
|
@@ -403,6 +403,9 @@ import {
|
|
|
403
403
|
getRunInput,
|
|
404
404
|
ThreadReader,
|
|
405
405
|
MAX_THREAD_EXPORT_PAGE_BYTES,
|
|
406
|
+
PreparedAppend,
|
|
407
|
+
type CommitBatch,
|
|
408
|
+
type ExecutionUpdate,
|
|
406
409
|
} from "./ThreadStore.ts";
|
|
407
410
|
import {
|
|
408
411
|
type WorkIndexRebuildRequest,
|
|
@@ -594,6 +597,11 @@ export { threadCreatedBatchId, threadCreatedRecordId } from "./internal/thread-i
|
|
|
594
597
|
export const recoveryRepairBatchId = (submissionId: SubmissionId, decisionTag: string): BatchId =>
|
|
595
598
|
decodeBatchId(`repair:${submissionId}:${decisionTag}`);
|
|
596
599
|
|
|
600
|
+
const isOriginalInputRecord = (
|
|
601
|
+
record: RecordEnvelope,
|
|
602
|
+
): record is RecordEnvelope & { readonly payload: UserInputRecorded } =>
|
|
603
|
+
record.payload._tag === "UserInputRecorded";
|
|
604
|
+
|
|
597
605
|
/** Deterministic record identity of one executed recovery decision's `RepairAnnotated` record. */
|
|
598
606
|
export const recoveryRepairRecordId = (submissionId: SubmissionId, decisionTag: string): RecordId =>
|
|
599
607
|
decodeRecordId(`repair:${submissionId}:${decisionTag}`);
|
|
@@ -1364,7 +1372,7 @@ interface OpenCallReview {
|
|
|
1364
1372
|
|
|
1365
1373
|
interface InitialJournalSource {
|
|
1366
1374
|
records: ReadonlyArray<JournalRecordEnvelope> | undefined;
|
|
1367
|
-
readonly original:
|
|
1375
|
+
readonly original: InitialContextInput;
|
|
1368
1376
|
readonly suffix: ReadonlyArray<CanonicalRecordEnvelope>;
|
|
1369
1377
|
cacheable: boolean;
|
|
1370
1378
|
readonly prefix?: CachedPromptHistory;
|
|
@@ -1472,7 +1480,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
1472
1480
|
|
|
1473
1481
|
// Rebuild the pinned admission and captured suffix using this runtime's host dependencies.
|
|
1474
1482
|
const readInitialJournal = (
|
|
1475
|
-
original:
|
|
1483
|
+
original: InitialContextInput,
|
|
1476
1484
|
suffix: ReadonlyArray<CanonicalRecordEnvelope>,
|
|
1477
1485
|
) =>
|
|
1478
1486
|
initialContext(original).pipe(
|
|
@@ -2617,7 +2625,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
2617
2625
|
Effect.map(({ owned }) => owned),
|
|
2618
2626
|
);
|
|
2619
2627
|
|
|
2620
|
-
const withProgressSession = Effect.fnUntraced(function* (
|
|
2628
|
+
const withProgressSession = Effect.fnUntraced(function* (
|
|
2629
|
+
session: RunStorageSession,
|
|
2630
|
+
originalInput?: () => InitialContextInput | undefined,
|
|
2631
|
+
) {
|
|
2621
2632
|
const { owned, progress } = yield* withProgress(session);
|
|
2622
2633
|
|
|
2623
2634
|
activeProgress.set(session.threadId, { writer: progress });
|
|
@@ -2628,9 +2639,44 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
2628
2639
|
}),
|
|
2629
2640
|
);
|
|
2630
2641
|
|
|
2642
|
+
const inputWriter =
|
|
2643
|
+
session.appendInput === undefined
|
|
2644
|
+
? undefined
|
|
2645
|
+
: {
|
|
2646
|
+
...session,
|
|
2647
|
+
append: session.appendInput,
|
|
2648
|
+
};
|
|
2649
|
+
|
|
2631
2650
|
const wrapped: RunStorageSession = {
|
|
2632
2651
|
...session,
|
|
2633
2652
|
append: owned.append,
|
|
2653
|
+
...(inputWriter === undefined
|
|
2654
|
+
? {}
|
|
2655
|
+
: {
|
|
2656
|
+
appendInput: (batch: CommitBatch) =>
|
|
2657
|
+
storageOwner.withProgress(
|
|
2658
|
+
session.threadId,
|
|
2659
|
+
storageOwner.withMutation(
|
|
2660
|
+
Effect.gen(function* () {
|
|
2661
|
+
yield* session.checkActive;
|
|
2662
|
+
// The captured empty canonical prefix is enforced again by the append tail and
|
|
2663
|
+
// checkpoint revision-zero CAS. It is not a cache entry across preparation IO.
|
|
2664
|
+
const original = originalInput?.();
|
|
2665
|
+
const commit = progress.commit(batch);
|
|
2666
|
+
|
|
2667
|
+
return yield* (
|
|
2668
|
+
original === undefined
|
|
2669
|
+
? commit
|
|
2670
|
+
: progress.withVerifiedEmptyRun(
|
|
2671
|
+
runIdForSubmission(session.claim.submissionId),
|
|
2672
|
+
CanonicalSequence.make(original.sequence - 1),
|
|
2673
|
+
commit,
|
|
2674
|
+
)
|
|
2675
|
+
).pipe(Effect.provideService(CurrentRunWriter, inputWriter));
|
|
2676
|
+
}),
|
|
2677
|
+
),
|
|
2678
|
+
),
|
|
2679
|
+
}),
|
|
2634
2680
|
publishSettlement: (batch) =>
|
|
2635
2681
|
progress.publish(batch).pipe(Effect.provideService(CurrentRunSettlement, session)),
|
|
2636
2682
|
};
|
|
@@ -3167,6 +3213,20 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
3167
3213
|
);
|
|
3168
3214
|
});
|
|
3169
3215
|
|
|
3216
|
+
const prepareOriginalInput = (submission: SubmissionSnapshot) =>
|
|
3217
|
+
makeEnvelope(
|
|
3218
|
+
submissionInputRecordId(submission.submissionId),
|
|
3219
|
+
UserInputRecorded.make({
|
|
3220
|
+
submissionId: submission.submissionId,
|
|
3221
|
+
kind: "user",
|
|
3222
|
+
runId: runIdForSubmission(submission.submissionId),
|
|
3223
|
+
input: submission.inputPayload,
|
|
3224
|
+
...(submission.messageAdmission === undefined
|
|
3225
|
+
? {}
|
|
3226
|
+
: { messageAdmission: submission.messageAdmission }),
|
|
3227
|
+
}),
|
|
3228
|
+
);
|
|
3229
|
+
|
|
3170
3230
|
/**
|
|
3171
3231
|
* Plan step 2: append the deterministic `UserInputRecorded` record (batch idempotency makes it
|
|
3172
3232
|
* exactly-once canonical, DUR-007) and mark it applied. When canonical history already carries
|
|
@@ -3192,18 +3252,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
3192
3252
|
return;
|
|
3193
3253
|
}
|
|
3194
3254
|
|
|
3195
|
-
const envelope = yield*
|
|
3196
|
-
recordId,
|
|
3197
|
-
UserInputRecorded.make({
|
|
3198
|
-
submissionId,
|
|
3199
|
-
kind: "user",
|
|
3200
|
-
runId: runIdForSubmission(submissionId),
|
|
3201
|
-
input: submission.inputPayload,
|
|
3202
|
-
...(submission.messageAdmission === undefined
|
|
3203
|
-
? {}
|
|
3204
|
-
: { messageAdmission: submission.messageAdmission }),
|
|
3205
|
-
}),
|
|
3206
|
-
);
|
|
3255
|
+
const envelope = yield* prepareOriginalInput(submission);
|
|
3207
3256
|
|
|
3208
3257
|
const result = yield* appendBatch(
|
|
3209
3258
|
ctx,
|
|
@@ -3254,8 +3303,13 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
3254
3303
|
maxDurationMillis: number,
|
|
3255
3304
|
records: ReadonlyArray<CanonicalRecordEnvelope>,
|
|
3256
3305
|
operational: ReadonlyArray<RecordEnvelope>,
|
|
3306
|
+
input?: RecordEnvelope,
|
|
3257
3307
|
) {
|
|
3258
3308
|
const writer = yield* CurrentRunWriter;
|
|
3309
|
+
const append = input === undefined ? writer.append : writer.appendInput;
|
|
3310
|
+
|
|
3311
|
+
if (append === undefined)
|
|
3312
|
+
return yield* RunJournalError.make({ message: "Fresh startup requires an input publisher" });
|
|
3259
3313
|
const runId = runIdForSubmission(submission.submissionId);
|
|
3260
3314
|
const recordId = runStartedRecordId(runId);
|
|
3261
3315
|
const existing = yield* readRunStart(submission.threadId, runId, operational);
|
|
@@ -3304,13 +3358,21 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
3304
3358
|
}
|
|
3305
3359
|
yield* hit("run:before-start-append");
|
|
3306
3360
|
yield* appendBatch(
|
|
3307
|
-
writer,
|
|
3361
|
+
{ threadId: writer.threadId, append },
|
|
3308
3362
|
CanonicalBatch.make({
|
|
3309
|
-
batchId:
|
|
3363
|
+
batchId:
|
|
3364
|
+
input === undefined
|
|
3365
|
+
? runStartedBatchId(runId)
|
|
3366
|
+
: submissionInputBatchId(submission.submissionId),
|
|
3310
3367
|
producerId: config.producerId,
|
|
3311
|
-
records: [
|
|
3368
|
+
records: [
|
|
3369
|
+
input ?? start,
|
|
3370
|
+
...(input === undefined ? [] : [start]),
|
|
3371
|
+
...(context === undefined ? [] : [context]),
|
|
3372
|
+
],
|
|
3312
3373
|
}),
|
|
3313
3374
|
);
|
|
3375
|
+
if (input !== undefined) yield* hit("input:after-canonical-append");
|
|
3314
3376
|
committed = true;
|
|
3315
3377
|
yield* hit("run:after-start-append");
|
|
3316
3378
|
});
|
|
@@ -4856,12 +4918,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
4856
4918
|
// Unrelated Thread traffic does not invalidate an already validated owner projection.
|
|
4857
4919
|
const projectionThrough = Math.max(progressThrough ?? 0, records.at(-1)?.sequence ?? 0);
|
|
4858
4920
|
|
|
4859
|
-
const originalInput =
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
payload
|
|
4863
|
-
|
|
4864
|
-
|
|
4921
|
+
const originalInput =
|
|
4922
|
+
initialJournal?.original ??
|
|
4923
|
+
records.find(
|
|
4924
|
+
({ record: { payload } }) =>
|
|
4925
|
+
payload._tag === "UserInputRecorded" &&
|
|
4926
|
+
payload.kind === "user" &&
|
|
4927
|
+
payload.submissionId === submissionId,
|
|
4928
|
+
);
|
|
4865
4929
|
|
|
4866
4930
|
if (originalInput === undefined)
|
|
4867
4931
|
return yield* RunJournalError.make({ message: "Run has no original context boundary" });
|
|
@@ -5447,18 +5511,6 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5447
5511
|
usageSummary: yield* currentUsageSummary(),
|
|
5448
5512
|
};
|
|
5449
5513
|
}
|
|
5450
|
-
// Summaries cover only prior Runs. Pruning and rollover also map fully settled
|
|
5451
|
-
// current-Run records and preserves the canonical instruction/input block during replay.
|
|
5452
|
-
let ownerFirstSequence: CanonicalSequence | undefined;
|
|
5453
|
-
|
|
5454
|
-
for (const envelope of records) {
|
|
5455
|
-
const payload = envelope.record.payload;
|
|
5456
|
-
|
|
5457
|
-
if ("runId" in payload && payload.runId === runId) {
|
|
5458
|
-
ownerFirstSequence = envelope.sequence;
|
|
5459
|
-
break;
|
|
5460
|
-
}
|
|
5461
|
-
}
|
|
5462
5514
|
|
|
5463
5515
|
/** Only canonical joined inputs can require restoration from an earlier Attempt. */
|
|
5464
5516
|
let joinBacklog: ReadonlyArray<JoinSnapshot> | undefined =
|
|
@@ -5839,6 +5891,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
5839
5891
|
const start = yield* CurrentRunStart;
|
|
5840
5892
|
|
|
5841
5893
|
yield* start.commit(yield* makeEnvelope(recordId, payload));
|
|
5894
|
+
knownIds.add(originalInput.record.recordId);
|
|
5842
5895
|
knownIds.add(runStartedRecordId(runId));
|
|
5843
5896
|
knownIds.add(recordId);
|
|
5844
5897
|
historyEvidence.clear();
|
|
@@ -6400,13 +6453,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
6400
6453
|
);
|
|
6401
6454
|
}
|
|
6402
6455
|
|
|
6403
|
-
//
|
|
6404
|
-
// complete current-Run
|
|
6456
|
+
// The validated original input bounds summaries even when startup deferred its append.
|
|
6457
|
+
// Pruning and rollover also admit complete current-Run canonical boundaries.
|
|
6405
6458
|
const coverable = sourceBoundaries.filter(
|
|
6406
|
-
(boundary) =>
|
|
6407
|
-
commit.kind !== "summarize" ||
|
|
6408
|
-
ownerFirstSequence === undefined ||
|
|
6409
|
-
boundary.sequence < ownerFirstSequence,
|
|
6459
|
+
(boundary) => commit.kind !== "summarize" || boundary.sequence < originalInput.sequence,
|
|
6410
6460
|
);
|
|
6411
6461
|
|
|
6412
6462
|
if (coverable.length === 0) {
|
|
@@ -8164,6 +8214,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8164
8214
|
// before suspension; only acknowledged ranges enter this Attempt's disposable suffix.
|
|
8165
8215
|
let committedPromptSuffix: CommittedPromptSuffix | undefined;
|
|
8166
8216
|
const storageSession = session;
|
|
8217
|
+
let pendingOriginal: InitialContextInput | undefined;
|
|
8167
8218
|
|
|
8168
8219
|
yield* Effect.addFinalizer(() =>
|
|
8169
8220
|
Effect.sync(() => {
|
|
@@ -8174,6 +8225,27 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8174
8225
|
|
|
8175
8226
|
const promptSession: RunStorageSession = {
|
|
8176
8227
|
...storageSession,
|
|
8228
|
+
...(storageSession.appendInput === undefined
|
|
8229
|
+
? {}
|
|
8230
|
+
: {
|
|
8231
|
+
appendInput: (batch: CommitBatch, executions?: ReadonlyArray<ExecutionUpdate>) =>
|
|
8232
|
+
storageSession.appendInput!(batch, executions).pipe(
|
|
8233
|
+
Effect.tap((result) =>
|
|
8234
|
+
Effect.sync(() => {
|
|
8235
|
+
if (
|
|
8236
|
+
pendingOriginal !== undefined &&
|
|
8237
|
+
!result.replayed &&
|
|
8238
|
+
result.firstSequence === pendingOriginal.sequence
|
|
8239
|
+
)
|
|
8240
|
+
committedPromptSuffix = makeCommittedPromptSuffix({
|
|
8241
|
+
threadId: storageSession.threadId,
|
|
8242
|
+
sequence: result.firstSequence,
|
|
8243
|
+
record: batch.records[0]!,
|
|
8244
|
+
});
|
|
8245
|
+
}),
|
|
8246
|
+
),
|
|
8247
|
+
),
|
|
8248
|
+
}),
|
|
8177
8249
|
append: (batch, executions) => {
|
|
8178
8250
|
const suffix = committedPromptSuffix;
|
|
8179
8251
|
const append = storageSession.append(batch, executions);
|
|
@@ -8209,7 +8281,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8209
8281
|
},
|
|
8210
8282
|
};
|
|
8211
8283
|
|
|
8212
|
-
session = yield* withProgressSession(promptSession);
|
|
8284
|
+
session = yield* withProgressSession(promptSession, () => pendingOriginal);
|
|
8213
8285
|
const { claim, threadId } = session;
|
|
8214
8286
|
const submissionId = claim.submissionId;
|
|
8215
8287
|
|
|
@@ -8254,6 +8326,41 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8254
8326
|
view.records.some(({ record }) => record.payload._tag !== "ThreadCreated")
|
|
8255
8327
|
)
|
|
8256
8328
|
return undefined;
|
|
8329
|
+
if (session.appendInput !== undefined) {
|
|
8330
|
+
const captured = yield* PreparedAppend.capture(
|
|
8331
|
+
AppendRequest.make({
|
|
8332
|
+
threadId,
|
|
8333
|
+
expectedTailSequence: before,
|
|
8334
|
+
batch: CanonicalBatch.make({
|
|
8335
|
+
batchId: submissionInputBatchId(submissionId),
|
|
8336
|
+
producerId: config.producerId,
|
|
8337
|
+
records: [yield* prepareOriginalInput(submission)],
|
|
8338
|
+
}),
|
|
8339
|
+
}),
|
|
8340
|
+
);
|
|
8341
|
+
|
|
8342
|
+
const record = captured.records[0]!.canonical;
|
|
8343
|
+
|
|
8344
|
+
if (!isOriginalInputRecord(record))
|
|
8345
|
+
return yield* RunJournalError.make({
|
|
8346
|
+
message: "Captured startup has no original input",
|
|
8347
|
+
});
|
|
8348
|
+
pendingOriginal = {
|
|
8349
|
+
threadId,
|
|
8350
|
+
sequence: CanonicalSequence.make(before + 1),
|
|
8351
|
+
record,
|
|
8352
|
+
};
|
|
8353
|
+
|
|
8354
|
+
const start = yield* makeRunStart(
|
|
8355
|
+
submission,
|
|
8356
|
+
Duration.toMillis(registeredAgent.definition.policy.maxDuration),
|
|
8357
|
+
view.records,
|
|
8358
|
+
view.operational,
|
|
8359
|
+
record,
|
|
8360
|
+
).pipe(Effect.provideService(CurrentRunWriter, session));
|
|
8361
|
+
|
|
8362
|
+
return { snapshot, through: before, view, start, input: pendingOriginal };
|
|
8363
|
+
}
|
|
8257
8364
|
const progress = progressWriters.get(session);
|
|
8258
8365
|
|
|
8259
8366
|
const applyInput = applyCanonicalInput(
|
|
@@ -8303,6 +8410,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8303
8410
|
operational: accepted.operational,
|
|
8304
8411
|
},
|
|
8305
8412
|
start,
|
|
8413
|
+
input: undefined,
|
|
8306
8414
|
};
|
|
8307
8415
|
}),
|
|
8308
8416
|
)
|
|
@@ -8737,12 +8845,14 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8737
8845
|
let initialJournal: InitialJournalSource | undefined;
|
|
8738
8846
|
|
|
8739
8847
|
if (view.context === undefined) {
|
|
8740
|
-
const original =
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
record
|
|
8744
|
-
|
|
8745
|
-
|
|
8848
|
+
const original =
|
|
8849
|
+
initialization?.input ??
|
|
8850
|
+
currentRecords.find(
|
|
8851
|
+
({ record }) =>
|
|
8852
|
+
record.payload._tag === "UserInputRecorded" &&
|
|
8853
|
+
record.payload.kind === "user" &&
|
|
8854
|
+
record.payload.submissionId === submissionId,
|
|
8855
|
+
);
|
|
8746
8856
|
|
|
8747
8857
|
if (original === undefined)
|
|
8748
8858
|
return yield* RunJournalError.make({
|
|
@@ -8752,7 +8862,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8752
8862
|
const suffix = currentRecords.filter((entry) => entry.sequence > original.sequence);
|
|
8753
8863
|
|
|
8754
8864
|
const fresh =
|
|
8755
|
-
|
|
8865
|
+
initialization?.input !== undefined ||
|
|
8866
|
+
(!lineage.inputWasRecorded &&
|
|
8867
|
+
tail.sequence === original.sequence &&
|
|
8868
|
+
suffix.length === 0);
|
|
8756
8869
|
|
|
8757
8870
|
// Every replay stays bounded at the ORIGINAL admission, never the later Thread tail.
|
|
8758
8871
|
const source: InitialJournalSource = yield* Effect.gen(function* () {
|
|
@@ -8795,9 +8908,10 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8795
8908
|
}
|
|
8796
8909
|
|
|
8797
8910
|
committedPromptSuffix?.discard();
|
|
8798
|
-
committedPromptSuffix =
|
|
8799
|
-
|
|
8800
|
-
|
|
8911
|
+
committedPromptSuffix =
|
|
8912
|
+
initialJournal?.cacheable && initialization?.input === undefined
|
|
8913
|
+
? makeCommittedPromptSuffix(initialJournal.original)
|
|
8914
|
+
: undefined;
|
|
8801
8915
|
|
|
8802
8916
|
const outcome = yield* runModel(
|
|
8803
8917
|
agent,
|
|
@@ -8807,7 +8921,7 @@ const make = Effect.fnUntraced(function* (bindings: ReadonlyArray<ResolvedBindin
|
|
|
8807
8921
|
currentRecords,
|
|
8808
8922
|
canonical,
|
|
8809
8923
|
initialJournal,
|
|
8810
|
-
tail.sequence,
|
|
8924
|
+
initialization?.input?.sequence ?? tail.sequence,
|
|
8811
8925
|
view.progressThrough,
|
|
8812
8926
|
view.progress,
|
|
8813
8927
|
view.context,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
subagentRequestedRecordId,
|
|
53
53
|
subagentStartedRecordId,
|
|
54
54
|
} from "./RunJournal.ts";
|
|
55
|
-
import type { RunStorageSession
|
|
55
|
+
import type { RunStorageSession } from "./RunStorage.ts";
|
|
56
56
|
import {
|
|
57
57
|
submissionAbortRecordId,
|
|
58
58
|
submissionInputRecordId,
|
|
@@ -74,7 +74,7 @@ export { RunContinuation, RunContextRecorded, EvidenceReference } from "./Record
|
|
|
74
74
|
/** Writer and progress sequencer selected at the local owner's resource boundary. */
|
|
75
75
|
export class CurrentRunWriter extends Context.Service<
|
|
76
76
|
CurrentRunWriter,
|
|
77
|
-
Pick<
|
|
77
|
+
Pick<RunStorageSession, "threadId" | "tail" | "append" | "appendInput" | "withProgress">
|
|
78
78
|
>()("@effect-agent/thread/CurrentRunWriter") {}
|
|
79
79
|
|
|
80
80
|
/** Settlement publication keeps its original atomic authority and claim-scoped implementation. */
|
|
@@ -1736,12 +1736,12 @@ const modelResponseRecord = Effect.fnUntraced(function* (
|
|
|
1736
1736
|
}
|
|
1737
1737
|
|
|
1738
1738
|
const encodedMessages = yield* encodePrompt(Prompt.fromMessages([...promptMessages])).pipe(
|
|
1739
|
-
Effect.
|
|
1739
|
+
Effect.mapErrorEager((cause) => journalError("Turn Prompt messages failed to encode", cause)),
|
|
1740
1740
|
);
|
|
1741
1741
|
|
|
1742
1742
|
const messages = yield* decodePersistedJson(encodedMessages).pipe(
|
|
1743
|
-
Effect.
|
|
1744
|
-
Effect.
|
|
1743
|
+
Effect.mapEager(copyJson),
|
|
1744
|
+
Effect.mapErrorEager((cause) =>
|
|
1745
1745
|
journalError("Turn Prompt messages exceed canonical persistence bounds", cause),
|
|
1746
1746
|
),
|
|
1747
1747
|
);
|
|
@@ -1783,7 +1783,9 @@ const modelResponseRecord = Effect.fnUntraced(function* (
|
|
|
1783
1783
|
}),
|
|
1784
1784
|
}),
|
|
1785
1785
|
}).pipe(
|
|
1786
|
-
Effect.
|
|
1786
|
+
Effect.mapErrorEager((cause) =>
|
|
1787
|
+
journalError("Invalid model response operation evidence", cause),
|
|
1788
|
+
),
|
|
1787
1789
|
);
|
|
1788
1790
|
|
|
1789
1791
|
if (payload.modelUsage !== undefined) {
|
|
@@ -1791,7 +1793,9 @@ const modelResponseRecord = Effect.fnUntraced(function* (
|
|
|
1791
1793
|
return yield* journalError("Turn model usage must contain at least one completed call");
|
|
1792
1794
|
|
|
1793
1795
|
const summary = yield* continuationUsage(payload.modelUsage).pipe(
|
|
1794
|
-
Effect.
|
|
1796
|
+
Effect.mapErrorEager((cause) =>
|
|
1797
|
+
journalError("Turn model usage exceeds accounting bounds", cause),
|
|
1798
|
+
),
|
|
1795
1799
|
);
|
|
1796
1800
|
|
|
1797
1801
|
if (
|
|
@@ -113,6 +113,8 @@ export interface RunWriter {
|
|
|
113
113
|
|
|
114
114
|
export interface RunStorageSession extends RunWriter, RunOwnership {
|
|
115
115
|
readonly claim: Claim;
|
|
116
|
+
/** Atomic original input/context plus the ledger marker, when the adapter supports it. */
|
|
117
|
+
readonly appendInput?: RunWriter["append"];
|
|
116
118
|
/** Library-only initialization, before application/model IO. Hold progress before mutation
|
|
117
119
|
* so the captured controls and this owner's acknowledged input remain one read view. */
|
|
118
120
|
readonly withInitialization?: <A, E, R>(
|
|
@@ -176,69 +178,75 @@ const makeWriter = Effect.fnUntraced(function* (threadId: ThreadId) {
|
|
|
176
178
|
}),
|
|
177
179
|
);
|
|
178
180
|
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
ThreadStoreError.make({
|
|
190
|
-
operation: "capture append",
|
|
191
|
-
message: cause.message,
|
|
192
|
-
cause,
|
|
181
|
+
const makeAppend = Effect.gen(function* () {
|
|
182
|
+
const publisher = yield* ThreadStore;
|
|
183
|
+
|
|
184
|
+
const append: RunWriter["append"] = Effect.fnUntraced(function* (batch, executions) {
|
|
185
|
+
const captured = yield* PreparedAppend.capture(
|
|
186
|
+
AppendRequest.make({
|
|
187
|
+
threadId,
|
|
188
|
+
expectedTailSequence: tail.sequence,
|
|
189
|
+
batch,
|
|
190
|
+
...(executions === undefined ? {} : { executions }),
|
|
193
191
|
}),
|
|
194
|
-
)
|
|
195
|
-
|
|
192
|
+
).pipe(
|
|
193
|
+
Effect.mapError((cause) =>
|
|
194
|
+
ThreadStoreError.make({
|
|
195
|
+
operation: "capture append",
|
|
196
|
+
message: cause.message,
|
|
197
|
+
cause,
|
|
198
|
+
}),
|
|
199
|
+
),
|
|
200
|
+
);
|
|
196
201
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
202
|
+
return yield* owner.withProgress(
|
|
203
|
+
threadId,
|
|
204
|
+
owner.withMutation(
|
|
205
|
+
Effect.gen(function* () {
|
|
206
|
+
yield* checkActive;
|
|
207
|
+
for (let retries = 0; ; retries++) {
|
|
208
|
+
const result = yield* publisher
|
|
209
|
+
.append(
|
|
210
|
+
captured.expectedTailSequence === tail.sequence
|
|
211
|
+
? captured
|
|
212
|
+
: AppendRequest.make({
|
|
213
|
+
...captured,
|
|
214
|
+
expectedTailSequence: tail.sequence,
|
|
215
|
+
}),
|
|
216
|
+
)
|
|
217
|
+
.pipe(
|
|
218
|
+
Effect.catchTag("AppendConflict", (conflict) => {
|
|
219
|
+
if (
|
|
220
|
+
conflict.reason !== "tail" ||
|
|
221
|
+
conflict.actualTailSequence === undefined ||
|
|
222
|
+
retries >= 8
|
|
223
|
+
)
|
|
224
|
+
return Effect.fail(conflict);
|
|
225
|
+
tail = { sequence: conflict.actualTailSequence };
|
|
226
|
+
if (captured.executions !== undefined) return Effect.fail(conflict);
|
|
227
|
+
|
|
228
|
+
return Effect.succeed(undefined);
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
if (result === undefined) continue;
|
|
233
|
+
// A historical replay returns its original coordinates, not today's canonical tail.
|
|
234
|
+
if (result.replayed) yield* refresh;
|
|
235
|
+
else tail = { sequence: result.lastSequence };
|
|
236
|
+
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
}).pipe(
|
|
240
|
+
// Revoke uncertain writes before releasing the writer's mutation gate.
|
|
241
|
+
Effect.onError((cause) =>
|
|
242
|
+
isDefiniteWriteFailure(cause) ? Effect.void : ownership.release,
|
|
243
|
+
),
|
|
238
244
|
),
|
|
239
245
|
),
|
|
240
|
-
)
|
|
241
|
-
);
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return append;
|
|
242
250
|
});
|
|
243
251
|
|
|
244
252
|
const writer = {
|
|
@@ -247,13 +255,14 @@ const makeWriter = Effect.fnUntraced(function* (threadId: ThreadId) {
|
|
|
247
255
|
withProgress: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
248
256
|
owner.withProgress(threadId, Effect.andThen(checkActive, effect)),
|
|
249
257
|
tail: Effect.sync(() => tail),
|
|
250
|
-
append,
|
|
258
|
+
append: yield* makeAppend,
|
|
251
259
|
checkActive: owner.withMutation(checkActive),
|
|
252
260
|
refresh,
|
|
253
261
|
};
|
|
254
262
|
|
|
255
263
|
return {
|
|
256
264
|
writer,
|
|
265
|
+
makeAppend,
|
|
257
266
|
adoptTail: (sequence: CanonicalSequence) => {
|
|
258
267
|
tail = { sequence };
|
|
259
268
|
},
|
|
@@ -310,7 +319,7 @@ export const make = Effect.gen(function* () {
|
|
|
310
319
|
});
|
|
311
320
|
yield* store.materialize(ThreadMaterialization.make({ threadId: request.threadId }));
|
|
312
321
|
|
|
313
|
-
const { writer, adoptTail } = yield* makeWriter(request.threadId).pipe(
|
|
322
|
+
const { writer, makeAppend, adoptTail } = yield* makeWriter(request.threadId).pipe(
|
|
314
323
|
Effect.provideService(WriterOwnership, generation),
|
|
315
324
|
Effect.provideService(ThreadStore, store),
|
|
316
325
|
Effect.provideService(StorageOwner, owner),
|
|
@@ -318,9 +327,22 @@ export const make = Effect.gen(function* () {
|
|
|
318
327
|
|
|
319
328
|
const owned = <A, E>(effect: Effect.Effect<A, E>) => generation.use(effect);
|
|
320
329
|
|
|
330
|
+
const inputPublisher = ledger.appendInput;
|
|
331
|
+
|
|
332
|
+
const appendInput =
|
|
333
|
+
inputPublisher === undefined
|
|
334
|
+
? undefined
|
|
335
|
+
: yield* makeAppend.pipe(
|
|
336
|
+
Effect.provideService(ThreadStore, {
|
|
337
|
+
...store,
|
|
338
|
+
append: (request) => inputPublisher(claimed.submissionId, request),
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
341
|
+
|
|
321
342
|
const session: RunStorageSession = {
|
|
322
343
|
...writer,
|
|
323
344
|
claim: claimed,
|
|
345
|
+
...(appendInput === undefined ? {} : { appendInput }),
|
|
324
346
|
withInitialization: (effect) =>
|
|
325
347
|
owner.withProgress(request.threadId, generation.use(effect)),
|
|
326
348
|
release: generation.release,
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
ToolCallUnknown,
|
|
37
37
|
WorkerAdmission,
|
|
38
38
|
} from "./Records.ts";
|
|
39
|
+
import type { AppendRequest, AppendResult, ThreadStoreFailure } from "./ThreadStore.ts";
|
|
39
40
|
|
|
40
41
|
export { IdempotencyKey, JoinedToHost, QueueSequence, Principal } from "../core/Receipt.ts";
|
|
41
42
|
|
|
@@ -1113,6 +1114,13 @@ export class SubmissionLedger extends Context.Service<
|
|
|
1113
1114
|
readonly selectHead: (
|
|
1114
1115
|
request: ClaimRequest,
|
|
1115
1116
|
) => Effect.Effect<Option.Option<Claim>, LedgerError>;
|
|
1117
|
+
/** Atomically publish a captured original input, active context and applied marker.
|
|
1118
|
+
* Optional adapters retain the ordinary append-then-repair protocol. The captured input
|
|
1119
|
+
* must be this owner's deterministic admission; a moved context boundary is refused. */
|
|
1120
|
+
readonly appendInput?: (
|
|
1121
|
+
submissionId: SubmissionId,
|
|
1122
|
+
request: AppendRequest,
|
|
1123
|
+
) => Effect.Effect<AppendResult, ThreadStoreFailure>;
|
|
1116
1124
|
readonly markInputApplied: (
|
|
1117
1125
|
request: MarkInputAppliedRequest,
|
|
1118
1126
|
) => Effect.Effect<void, OwnershipLost | LedgerError>;
|