@yielded/agent 0.1.0-beta.174 → 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-BHmp2V9p.d.mts → Admin-DTDihBra.d.mts} +50 -13
- package/dist/{AgentRuntime-BD3ZHAMq.d.mts → AgentRuntime-CHDyRbSf.d.mts} +3 -3
- package/dist/{AgentRuntime-B6qZlD3f.mjs → AgentRuntime-DaDKMasp.mjs} +123 -114
- package/dist/AgentRuntime-DaDKMasp.mjs.map +1 -0
- package/dist/{Digest-EpeqWEdv.mjs → Digest-C3EODOee.mjs} +2 -2
- package/dist/{Digest-EpeqWEdv.mjs.map → Digest-C3EODOee.mjs.map} +1 -1
- package/dist/{DurableAgentRuntime-CBBjRNJN.mjs → DurableAgentRuntime-BJbuIqQq.mjs} +237 -114
- package/dist/DurableAgentRuntime-BJbuIqQq.mjs.map +1 -0
- package/dist/{EventSource-C9aaJaXT.d.mts → EventSource-Cc58uEso.d.mts} +2 -2
- package/dist/InMemory.d.mts +2 -2
- package/dist/{MessageDelivery-BGDm-c46.d.mts → MessageDelivery-ClHg7c0O.d.mts} +42 -42
- package/dist/{MessagingHost-CRUK4JBi.mjs → MessagingHost-CZCyrnvJ.mjs} +27 -26
- package/dist/MessagingHost-CZCyrnvJ.mjs.map +1 -0
- package/dist/{PreparedInputAdmission-BvDqlgiP.d.mts → PreparedInputAdmission-CjcX1OP0.d.mts} +3 -3
- package/dist/{Records-BiuJDWNe.mjs → Records-DzL4Kbtw.mjs} +2 -2
- package/dist/{Records-BiuJDWNe.mjs.map → Records-DzL4Kbtw.mjs.map} +1 -1
- package/dist/{RunContinuation-4ZATcVLB.mjs → RunContinuation-CWHuciup.mjs} +74 -40
- package/dist/RunContinuation-CWHuciup.mjs.map +1 -0
- package/dist/{RunContinuation-CTJ1jGrO.d.mts → RunContinuation-DSnUTSSb.d.mts} +11 -5
- package/dist/{RunJournal-CNZqA3Ge.mjs → RunJournal-DGblbFas.mjs} +86 -81
- package/dist/RunJournal-DGblbFas.mjs.map +1 -0
- package/dist/{RunStorage-BHjuuERI.mjs → RunStorage-BF2YgWkg.mjs} +44 -33
- package/dist/RunStorage-BF2YgWkg.mjs.map +1 -0
- package/dist/{Scheduling-NG8slKdI.mjs → Scheduling-07_dsGED.mjs} +3 -3
- package/dist/{Scheduling-NG8slKdI.mjs.map → Scheduling-07_dsGED.mjs.map} +1 -1
- package/dist/{Subagent-CTIpnzlB.mjs → Subagent-CexndZSp.mjs} +2 -2
- package/dist/{Subagent-CTIpnzlB.mjs.map → Subagent-CexndZSp.mjs.map} +1 -1
- package/dist/{Subagent-DKhZA72u.d.mts → Subagent-xhN_aD1r.d.mts} +3 -3
- package/dist/{Subscription-BNg9VCRl.d.mts → Subscription-DuC_PcGx.d.mts} +2 -2
- package/dist/{SubscriptionInput-BUoXv-_K.d.mts → SubscriptionInput-B1IeAEDi.d.mts} +2 -2
- package/dist/{Thread-dk-QiNml.d.mts → Thread-DzdJAY4J.d.mts} +4 -3
- package/dist/{ThreadHistory-ByxS8HU1.d.mts → ThreadHistory-KPPBJLJb.d.mts} +12 -2
- package/dist/{ThreadImport-CMgzttvr.d.mts → ThreadImport-D_Biid6w.d.mts} +42 -42
- package/dist/{ThreadImport-CodYoJoa.mjs → ThreadImport-ZgWxf7bB.mjs} +8 -8
- package/dist/{ThreadImport-CodYoJoa.mjs.map → ThreadImport-ZgWxf7bB.mjs.map} +1 -1
- package/dist/{ThreadStore-59uDGXul.mjs → ThreadStore-BC4V-mY-.mjs} +42 -23
- package/dist/ThreadStore-BC4V-mY-.mjs.map +1 -0
- package/dist/{ThreadTransfer-3jigUl3g.mjs → ThreadTransfer-S7FnTRHt.mjs} +4 -4
- package/dist/{ThreadTransfer-3jigUl3g.mjs.map → ThreadTransfer-S7FnTRHt.mjs.map} +1 -1
- package/dist/{WorkerAdmission-DDYJdOsU.mjs → WorkerAdmission-C47Vy_cK.mjs} +5 -5
- package/dist/{WorkerAdmission-DDYJdOsU.mjs.map → WorkerAdmission-C47Vy_cK.mjs.map} +1 -1
- package/dist/{agent-registration-DRbyhAu-.mjs → agent-registration-a6QYaG3h.mjs} +4 -4
- package/dist/{agent-registration-DRbyhAu-.mjs.map → agent-registration-a6QYaG3h.mjs.map} +1 -1
- package/dist/capabilities/Messaging.mjs +1 -1
- package/dist/capabilities/ModelContext.d.mts +1 -1
- package/dist/capabilities/RunHooks.d.mts +1 -1
- package/dist/capabilities/Subagent.d.mts +1 -1
- package/dist/capabilities/Subagent.mjs +1 -1
- package/dist/core/Thread.d.mts +2 -2
- package/dist/core/Thread.mjs +5 -3
- package/dist/core/Thread.mjs.map +1 -1
- package/dist/durable/ActivityStore.mjs +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/Admin.mjs +1 -1
- package/dist/durable/AgentRegistration.d.mts +1 -1
- package/dist/durable/AgentRegistration.mjs +1 -1
- package/dist/durable/Certification.d.mts +1 -1
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/CommittedActivity.mjs +3 -3
- package/dist/durable/Digest.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/EventSource.mjs +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/LifecyclePublication.d.mts +1 -1
- package/dist/durable/LifecyclePublication.mjs +2 -2
- package/dist/durable/MessageDelivery.d.mts +1 -1
- package/dist/durable/MessageDelivery.mjs +2 -2
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +40 -40
- package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
- package/dist/durable/PersistentHistory.d.mts +2 -2
- package/dist/durable/PersistentHistory.mjs +4 -4
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/RecordFormat.mjs +1 -1
- package/dist/durable/Records.mjs +1 -1
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/Recovery.mjs +1 -1
- package/dist/durable/RunContinuation.d.mts +2 -2
- package/dist/durable/RunContinuation.mjs +3 -3
- package/dist/durable/RunJournal.d.mts +2 -2
- package/dist/durable/RunJournal.mjs +2 -2
- package/dist/durable/RunStorage.d.mts +1 -1
- package/dist/durable/RunStorage.mjs +1 -1
- package/dist/durable/Schedule.mjs +1 -1
- package/dist/durable/ScheduleStoreConformance.mjs +1 -1
- package/dist/durable/ScheduleTransition.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +1 -1
- package/dist/durable/Scheduling.mjs +1 -1
- package/dist/durable/SettlementPublisher.d.mts +1 -1
- package/dist/durable/SettlementPublisher.mjs +3 -3
- package/dist/durable/SqlMemoryStore.mjs +5 -3
- package/dist/durable/SqlMemoryStore.mjs.map +1 -1
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs +7 -3
- package/dist/durable/SubmissionLedger.mjs.map +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +6 -6
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/Subscription.mjs +2 -2
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionInput.mjs +2 -2
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
- package/dist/durable/SubscriptionTools.d.mts +2 -2
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/SubscriptionTransition.mjs +1 -1
- package/dist/durable/Subscriptions.d.mts +5 -5
- package/dist/durable/Subscriptions.mjs +2 -2
- package/dist/durable/ThreadArchiveRange.d.mts +1 -1
- package/dist/durable/ThreadArchiveRange.mjs +1 -1
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.mjs +2 -2
- package/dist/durable/ThreadContextHistoryProjection.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 +4 -4
- package/dist/durable/ThreadProjection.mjs +1 -1
- 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 +2 -2
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/ThreadStoreConformance.mjs +4 -4
- package/dist/durable/ThreadTransfer.d.mts +1 -1
- package/dist/durable/ThreadTransfer.mjs +1 -1
- package/dist/durable/ThreadWork.d.mts +2 -2
- package/dist/durable/ThreadWork.mjs +12 -6
- package/dist/durable/ThreadWork.mjs.map +1 -1
- package/dist/durable/ToolReconciler.mjs +1 -1
- package/dist/durable/WakeScheduler.mjs +23 -39
- package/dist/durable/WakeScheduler.mjs.map +1 -1
- 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/engine/AgentRuntime.d.mts +1 -1
- package/dist/engine/AgentRuntime.mjs +1 -1
- package/dist/engine/MessagingHost.mjs +1 -1
- package/dist/engine/Output.mjs +1 -1
- package/dist/engine/ThreadHistory.d.mts +1 -1
- package/dist/engine/ThreadHistory.mjs +3 -2
- package/dist/engine/ThreadHistory.mjs.map +1 -1
- package/dist/index.d.mts +12 -12
- package/dist/index.mjs +13 -13
- package/dist/{output-contract-CMIhu4Cw.mjs → output-contract-DCnLYCdp.mjs} +12 -15
- package/dist/{output-contract-CMIhu4Cw.mjs.map → output-contract-DCnLYCdp.mjs.map} +1 -1
- package/dist/{run-context-reader-CC7ZD95F.d.mts → run-context-reader-DvTEq2RC.d.mts} +2 -2
- package/package.json +1 -1
- package/src/core/Thread.ts +143 -146
- package/src/durable/DurableAgentRuntime.ts +549 -260
- package/src/durable/RunContinuation.ts +115 -64
- package/src/durable/RunJournal.ts +107 -112
- package/src/durable/RunStorage.ts +90 -61
- package/src/durable/SqlMemoryStore.ts +8 -6
- package/src/durable/SubmissionLedger.ts +15 -0
- package/src/durable/ThreadStore.ts +48 -26
- package/src/durable/ThreadWork.ts +10 -0
- package/src/durable/WakeScheduler.ts +34 -66
- package/src/durable/internal/initial-context.ts +17 -2
- package/src/durable/internal/prompt-history-cache.ts +13 -8
- package/src/durable/internal/usage-totals.ts +28 -31
- package/src/engine/MessagingHost.ts +4 -2
- package/src/engine/ThreadHistory.ts +47 -48
- package/src/engine/internal/agent-runtime.ts +234 -202
- package/src/engine/internal/output-contract.ts +15 -15
- package/src/engine/internal/primitive-delta.ts +2 -3
- package/src/engine/internal/tool-telemetry.ts +3 -5
- package/dist/AgentRuntime-B6qZlD3f.mjs.map +0 -1
- package/dist/DurableAgentRuntime-CBBjRNJN.mjs.map +0 -1
- package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
- package/dist/RunContinuation-4ZATcVLB.mjs.map +0 -1
- package/dist/RunJournal-CNZqA3Ge.mjs.map +0 -1
- package/dist/RunStorage-BHjuuERI.mjs.map +0 -1
- package/dist/ThreadStore-59uDGXul.mjs.map +0 -1
|
@@ -343,19 +343,20 @@ export class AppendRequest extends Schema.Class<AppendRequest>(
|
|
|
343
343
|
expectedTailSequence: CanonicalSequence,
|
|
344
344
|
}) {}
|
|
345
345
|
|
|
346
|
+
const RecordCount = Schema.Array(Schema.Unknown).check(Schema.isMaxLength(256));
|
|
347
|
+
const ExecutionRecordCount = Schema.Array(Schema.Unknown).check(Schema.isMaxLength(8));
|
|
348
|
+
|
|
346
349
|
const AppendHeader = Schema.Struct({
|
|
347
350
|
threadId: ThreadId,
|
|
348
351
|
expectedTailSequence: CanonicalSequence,
|
|
349
352
|
batchId: BatchId,
|
|
350
353
|
producerId: ProducerId,
|
|
354
|
+
records: RecordCount,
|
|
355
|
+
executions: RecordCount,
|
|
351
356
|
});
|
|
352
357
|
|
|
353
358
|
const validateHeader = Schema.decodeUnknownSync(AppendHeader);
|
|
354
359
|
|
|
355
|
-
const validateRecordCount = Schema.decodeUnknownSync(
|
|
356
|
-
Schema.Array(Schema.Unknown).check(Schema.isMaxLength(256)),
|
|
357
|
-
);
|
|
358
|
-
|
|
359
360
|
const validateExecutionHeader = Schema.decodeUnknownSync(
|
|
360
361
|
Schema.Struct({
|
|
361
362
|
previousRevision: ExecutionUpdate.fields.previousRevision,
|
|
@@ -366,24 +367,36 @@ const validateExecutionHeader = Schema.decodeUnknownSync(
|
|
|
366
367
|
originalSequence: ExecutionCheckpoint.fields.originalSequence,
|
|
367
368
|
throughSequence: ExecutionCheckpoint.fields.throughSequence,
|
|
368
369
|
batchId: ExecutionCheckpoint.fields.batchId,
|
|
370
|
+
operational: ExecutionRecordCount,
|
|
369
371
|
}),
|
|
372
|
+
facts: ExecutionRecordCount,
|
|
370
373
|
}),
|
|
371
374
|
);
|
|
372
375
|
|
|
373
|
-
const validateExecutionRecordCount = Schema.decodeUnknownSync(
|
|
374
|
-
Schema.Array(Schema.Unknown).check(Schema.isMaxLength(8)),
|
|
375
|
-
);
|
|
376
|
-
|
|
377
376
|
/** Capture records once; wrapper fields retain their owning Schema's constraints. */
|
|
378
377
|
const captureExecution = (input: ExecutionUpdate) => {
|
|
379
|
-
const
|
|
380
|
-
const operationalInput =
|
|
378
|
+
const checkpointInput = input.checkpoint;
|
|
379
|
+
const operationalInput = checkpointInput.operational;
|
|
381
380
|
const factsInput = input.facts;
|
|
382
381
|
|
|
383
|
-
const
|
|
384
|
-
|
|
382
|
+
const header = validateExecutionHeader({
|
|
383
|
+
previousRevision: input.previousRevision,
|
|
384
|
+
retire: input.retire,
|
|
385
|
+
checkpoint: {
|
|
386
|
+
version: checkpointInput.version,
|
|
387
|
+
canonicalRecords: checkpointInput.canonicalRecords,
|
|
388
|
+
originalSequence: checkpointInput.originalSequence,
|
|
389
|
+
throughSequence: checkpointInput.throughSequence,
|
|
390
|
+
batchId: checkpointInput.batchId,
|
|
391
|
+
operational: operationalInput,
|
|
392
|
+
},
|
|
393
|
+
facts: factsInput,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const operationalLength = header.checkpoint.operational.length;
|
|
397
|
+
const factsLength = header.facts.length;
|
|
385
398
|
|
|
386
|
-
const progress = captureRecord(
|
|
399
|
+
const progress = captureRecord(checkpointInput.progressRecord);
|
|
387
400
|
|
|
388
401
|
if (Result.isFailure(progress)) return Result.fail(progress.failure);
|
|
389
402
|
const progressRecord = progress.success.canonical;
|
|
@@ -415,10 +428,6 @@ const captureExecution = (input: ExecutionUpdate) => {
|
|
|
415
428
|
facts.push(record.success);
|
|
416
429
|
}
|
|
417
430
|
|
|
418
|
-
// Keep the actual owned arrays within the persisted bounds as well.
|
|
419
|
-
validateExecutionRecordCount(operational);
|
|
420
|
-
validateExecutionRecordCount(facts);
|
|
421
|
-
|
|
422
431
|
const checkpoint = Object.freeze(
|
|
423
432
|
ExecutionCheckpoint.make(
|
|
424
433
|
{
|
|
@@ -446,7 +455,13 @@ const captureExecution = (input: ExecutionUpdate) => {
|
|
|
446
455
|
const json = `{"previousRevision":${update.previousRevision},"checkpoint":${checkpointJson},"facts":[${facts.map(({ json }) => json).join(",")}],"retire":${update.retire}}`;
|
|
447
456
|
|
|
448
457
|
return Result.succeed(
|
|
449
|
-
Object.freeze({
|
|
458
|
+
Object.freeze({
|
|
459
|
+
update,
|
|
460
|
+
json,
|
|
461
|
+
checkpointJson,
|
|
462
|
+
factsJson: JSON.stringify(facts.map(({ wire }) => wire)),
|
|
463
|
+
bytes: utf8ByteLength(json),
|
|
464
|
+
}),
|
|
450
465
|
);
|
|
451
466
|
};
|
|
452
467
|
|
|
@@ -497,6 +512,8 @@ export interface PreparedAppend extends AppendRequest {
|
|
|
497
512
|
readonly json: string;
|
|
498
513
|
/** Checkpoint wire from the same detached, validated execution capture. */
|
|
499
514
|
readonly checkpointJson: string;
|
|
515
|
+
/** Schema-encoded fact array from this same capture, for the stable retry digest. */
|
|
516
|
+
readonly factsJson: string;
|
|
500
517
|
readonly bytes: number;
|
|
501
518
|
}>;
|
|
502
519
|
readonly records: ReadonlyArray<{
|
|
@@ -526,15 +543,19 @@ export const PreparedAppend = {
|
|
|
526
543
|
return Effect.try({
|
|
527
544
|
try: () =>
|
|
528
545
|
Result.gen(function* () {
|
|
546
|
+
const executions = input.executions ?? [];
|
|
547
|
+
const recordInputs = input.batch.records;
|
|
548
|
+
|
|
529
549
|
const header = validateHeader({
|
|
530
550
|
...input,
|
|
531
551
|
batchId: input.batch.batchId,
|
|
532
552
|
producerId: input.batch.producerId,
|
|
553
|
+
records: recordInputs,
|
|
554
|
+
executions,
|
|
533
555
|
});
|
|
534
556
|
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
const executionLength = validateRecordCount(executions).length;
|
|
557
|
+
const recordLength = header.records.length;
|
|
558
|
+
const executionLength = header.executions.length;
|
|
538
559
|
|
|
539
560
|
const executionWrites: Array<PreparedAppend["executionWrites"][number]> = [];
|
|
540
561
|
|
|
@@ -542,9 +563,7 @@ export const PreparedAppend = {
|
|
|
542
563
|
executionWrites.push(yield* captureExecution(executions[index]!));
|
|
543
564
|
Object.freeze(executionWrites);
|
|
544
565
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
if (input.batch.records.length === 0 && executionWrites.length === 0)
|
|
566
|
+
if (recordLength === 0 && executionWrites.length === 0)
|
|
548
567
|
return yield* Result.fail(
|
|
549
568
|
ThreadStoreError.make({
|
|
550
569
|
operation: "prepare durable commit",
|
|
@@ -558,8 +577,11 @@ export const PreparedAppend = {
|
|
|
558
577
|
message: "Active state exceeds its bounded transfer representation",
|
|
559
578
|
}),
|
|
560
579
|
);
|
|
561
|
-
// Visit
|
|
562
|
-
const encodings =
|
|
580
|
+
// Visit Schema-validated indexed slots, including sparse ones, never a caller's iterator.
|
|
581
|
+
const encodings = [];
|
|
582
|
+
|
|
583
|
+
for (let index = 0; index < recordLength; index++)
|
|
584
|
+
encodings.push(yield* captureRecord(recordInputs[index]!));
|
|
563
585
|
const prefix = `{"batchId":${JSON.stringify(header.batchId)},"producerId":${JSON.stringify(header.producerId)},"records":[`;
|
|
564
586
|
const batchJson = `${prefix}${encodings.map(({ json }) => json).join(",")}]}`;
|
|
565
587
|
|
|
@@ -144,6 +144,10 @@ export const ThreadWorkCursor = Schema.Struct({
|
|
|
144
144
|
|
|
145
145
|
export type ThreadWorkCursor = typeof ThreadWorkCursor.Type;
|
|
146
146
|
|
|
147
|
+
/** Local snapshot request; it grants no claim or dispatch authority. */
|
|
148
|
+
export const FreshAdmissionWork = Schema.Struct({ threadId: ThreadId, submissionId: SubmissionId });
|
|
149
|
+
export type FreshAdmissionWork = typeof FreshAdmissionWork.Type;
|
|
150
|
+
|
|
147
151
|
export const WorkThreadsRequest = Schema.Struct({
|
|
148
152
|
afterThreadId: Schema.optionalKey(ThreadId),
|
|
149
153
|
limit: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 32 })),
|
|
@@ -192,6 +196,12 @@ export class WorkDiscoveryUnavailable extends Schema.TaggedError<WorkDiscoveryUn
|
|
|
192
196
|
|
|
193
197
|
/** Native owner pages and canonical-index maintenance, implemented at the storage owner. */
|
|
194
198
|
export interface ThreadWorkStorage {
|
|
199
|
+
/** Native proof that this untouched root admission has no other recovery obligations.
|
|
200
|
+
* A caller must still claim and validate current controls before dispatch. */
|
|
201
|
+
readonly isOnlyFreshAdmission?: (
|
|
202
|
+
request: FreshAdmissionWork,
|
|
203
|
+
) => Effect.Effect<boolean, ThreadStoreError>;
|
|
204
|
+
|
|
195
205
|
readonly threads: (
|
|
196
206
|
request: WorkThreadsRequest,
|
|
197
207
|
) => Effect.Effect<WorkThreadsPage, ThreadStoreError>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Scope } from "effect";
|
|
2
|
-
import { Context, Deferred, Effect, Layer,
|
|
2
|
+
import { Context, Deferred, Effect, Layer, Stream } from "effect";
|
|
3
3
|
|
|
4
4
|
import { type ThreadId } from "../core/Identifiers.ts";
|
|
5
5
|
|
|
@@ -17,79 +17,47 @@ export interface WakeSubscriptionHub {
|
|
|
17
17
|
readonly notify: (threadId: ThreadId) => Effect.Effect<void>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
interface WakeRegistration {
|
|
21
|
-
readonly id: number;
|
|
22
|
-
readonly deferred: Deferred.Deferred<void>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type WakeRegistrations = ReadonlyMap<ThreadId, ReadonlyMap<number, Deferred.Deferred<void>>>;
|
|
26
|
-
|
|
27
20
|
/**
|
|
28
21
|
* Build the shared per-incarnation registration primitive used by host WakeSchedulers. The hub
|
|
29
22
|
* contains hints only: eviction may discard it, while a reconnecting caller re-subscribes and
|
|
30
23
|
* rechecks canonical storage. Scope finalizers remove cancelled waiters, and notify broadcasts
|
|
31
24
|
* instead of consuming a signal that belongs to another waiter.
|
|
32
25
|
*/
|
|
33
|
-
export const makeWakeSubscriptionHub: Effect.Effect<WakeSubscriptionHub> = Effect.
|
|
34
|
-
const registrations =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
26
|
+
export const makeWakeSubscriptionHub: Effect.Effect<WakeSubscriptionHub> = Effect.sync(() => {
|
|
27
|
+
const registrations = new Map<ThreadId, Set<Deferred.Deferred<void>>>();
|
|
28
|
+
|
|
29
|
+
const subscribe = (threadId: ThreadId) =>
|
|
30
|
+
Effect.acquireRelease(
|
|
31
|
+
Effect.sync(() => {
|
|
32
|
+
const deferred = Deferred.makeUnsafe<void>();
|
|
33
|
+
let waiters = registrations.get(threadId);
|
|
34
|
+
|
|
35
|
+
if (waiters === undefined) {
|
|
36
|
+
waiters = new Set();
|
|
37
|
+
registrations.set(threadId, waiters);
|
|
38
|
+
}
|
|
39
|
+
waiters.add(deferred);
|
|
40
|
+
|
|
41
|
+
return deferred;
|
|
42
|
+
}),
|
|
43
|
+
(deferred) =>
|
|
44
|
+
Effect.sync(() => {
|
|
45
|
+
const waiters = registrations.get(threadId);
|
|
46
|
+
|
|
47
|
+
if (waiters?.delete(deferred) && waiters.size === 0) registrations.delete(threadId);
|
|
48
|
+
}),
|
|
49
|
+
).pipe(Effect.map(Deferred.await));
|
|
50
|
+
|
|
51
|
+
const notify = (threadId: ThreadId) =>
|
|
52
|
+
Effect.sync(() => {
|
|
53
|
+
const waiters = registrations.get(threadId);
|
|
54
|
+
|
|
55
|
+
if (waiters === undefined) return;
|
|
56
|
+
// Detach before resuming: a resumed waiter may register its next wait immediately.
|
|
57
|
+
registrations.delete(threadId);
|
|
58
|
+
for (const waiter of waiters) Deferred.doneUnsafe(waiter, Effect.void);
|
|
53
59
|
});
|
|
54
60
|
|
|
55
|
-
const subscribe = Effect.fnUntraced(function* (
|
|
56
|
-
threadId: ThreadId,
|
|
57
|
-
): Effect.fn.Return<Effect.Effect<void>, never, Scope.Scope> {
|
|
58
|
-
const deferred = yield* Deferred.make<void>();
|
|
59
|
-
const id = yield* Ref.getAndUpdate(nextId, (current) => current + 1);
|
|
60
|
-
const registration: WakeRegistration = { id, deferred };
|
|
61
|
-
|
|
62
|
-
yield* Effect.addFinalizer(() => remove(threadId, registration.id));
|
|
63
|
-
yield* Ref.update(registrations, (current) => {
|
|
64
|
-
const next = new Map(current);
|
|
65
|
-
const thread = new Map(current.get(threadId) ?? []);
|
|
66
|
-
|
|
67
|
-
thread.set(registration.id, registration.deferred);
|
|
68
|
-
next.set(threadId, thread);
|
|
69
|
-
|
|
70
|
-
return next;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
return Deferred.await(registration.deferred);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const notify = Effect.fnUntraced(function* (threadId: ThreadId) {
|
|
77
|
-
const waiters = yield* Ref.modify(registrations, (current) => {
|
|
78
|
-
const thread = current.get(threadId);
|
|
79
|
-
|
|
80
|
-
if (thread === undefined) return [[], current] as const;
|
|
81
|
-
const next = new Map(current);
|
|
82
|
-
|
|
83
|
-
next.delete(threadId);
|
|
84
|
-
|
|
85
|
-
return [[...thread.values()], next] as const;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
yield* Effect.forEach(waiters, (waiter) => Deferred.succeed(waiter, undefined), {
|
|
89
|
-
discard: true,
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
61
|
return { subscribe, notify };
|
|
94
62
|
});
|
|
95
63
|
|
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
import { Effect, Option, Schema, Stream } from "effect";
|
|
2
2
|
import { Prompt } from "effect/ai";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CanonicalSequence,
|
|
6
|
+
type CanonicalRecordEnvelope,
|
|
7
|
+
type RecordEnvelope,
|
|
8
|
+
type UserInputRecorded,
|
|
9
|
+
} from "../Records.ts";
|
|
5
10
|
import { RunJournalError, toolCallSettledRecordId } from "../RunJournal.ts";
|
|
6
11
|
import { submissionSettlementRecordId } from "../SubmissionLedger.ts";
|
|
7
12
|
import { getRecord, getRunInput, PROMPT_EVIDENCE_TAGS, ThreadReader } from "../ThreadStore.ts";
|
|
8
13
|
import type { JournalRecordEnvelope } from "./journal-metadata.ts";
|
|
9
14
|
|
|
15
|
+
/** A captured original input can prepare context before its atomic publication. It carries
|
|
16
|
+
* an expected position, never a committed observation cursor or dispatch authority. */
|
|
17
|
+
export type InitialContextInput =
|
|
18
|
+
| CanonicalRecordEnvelope
|
|
19
|
+
| {
|
|
20
|
+
readonly threadId: CanonicalRecordEnvelope["threadId"];
|
|
21
|
+
readonly sequence: CanonicalRecordEnvelope["sequence"];
|
|
22
|
+
readonly record: RecordEnvelope & { readonly payload: UserInputRecorded };
|
|
23
|
+
};
|
|
24
|
+
|
|
10
25
|
const invalid = (message: string) => RunJournalError.make({ message });
|
|
11
26
|
const promptTag = new Set<string>(PROMPT_EVIDENCE_TAGS);
|
|
12
27
|
|
|
13
28
|
/** Fresh admission trusts validated immutable history; recovery verifies its saved Prompt digest. */
|
|
14
|
-
export const initialContext = Effect.fnUntraced(function* (original:
|
|
29
|
+
export const initialContext = Effect.fnUntraced(function* (original: InitialContextInput) {
|
|
15
30
|
const reader = yield* ThreadReader;
|
|
16
31
|
const threadId = original.threadId;
|
|
17
32
|
const through = CanonicalSequence.make(original.sequence - 1);
|
|
@@ -6,9 +6,9 @@ import { boundedValueFootprint } from "../../engine/internal/bounded-value.ts";
|
|
|
6
6
|
import { canonicalJson, digestCanonicalJson, utf8ByteLength } from "../Digest.ts";
|
|
7
7
|
import {
|
|
8
8
|
CanonicalSequence,
|
|
9
|
+
type CanonicalRecordEnvelope,
|
|
9
10
|
CURRENT_RECORD_FORMAT,
|
|
10
11
|
PromptRecord,
|
|
11
|
-
type CanonicalRecordEnvelope,
|
|
12
12
|
type Digest,
|
|
13
13
|
type PromptRecordEnvelope,
|
|
14
14
|
type RecordEnvelope,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ThreadStore,
|
|
24
24
|
ThreadTailRequest,
|
|
25
25
|
} from "../ThreadStore.ts";
|
|
26
|
+
import type { InitialContextInput } from "./initial-context.ts";
|
|
26
27
|
import type { JournalRecordEnvelope } from "./journal-metadata.ts";
|
|
27
28
|
import { promptJsonFootprint } from "./prompt-footprint.ts";
|
|
28
29
|
import { captureRecord } from "./record-encoding.ts";
|
|
@@ -149,12 +150,14 @@ const retainedBytes = (value: unknown, json: string, allowance: number) => {
|
|
|
149
150
|
return footprint === undefined ? undefined : encodedBytes + 2 * footprint;
|
|
150
151
|
};
|
|
151
152
|
|
|
152
|
-
const encodeHistory =
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
const encodeHistory = (prompt: Prompt.Prompt, allowance: number) =>
|
|
154
|
+
Effect.suspend(() =>
|
|
155
|
+
Effect.mapEager(encodePrompt(prompt), (value) => {
|
|
156
|
+
const encoded = canonicalJson(value);
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
})
|
|
158
|
+
return { encoded, bytes: retainedBytes(value, encoded, allowance) };
|
|
159
|
+
}),
|
|
160
|
+
);
|
|
158
161
|
|
|
159
162
|
// Finish parsing/decoding before suspending for suffix reads. Only the native prefix and its
|
|
160
163
|
// canonical string leave this Effect; it does not keep a second parsed root in the outer Run.
|
|
@@ -252,7 +255,9 @@ const suffixRecordFootprint = (entry: PromptRecordEnvelope, allowance: number) =
|
|
|
252
255
|
* acknowledged, consecutive appends. Never authority for writes or recovery; any uncertainty
|
|
253
256
|
* discards this derivative and completion rereads canonical history.
|
|
254
257
|
*/
|
|
255
|
-
export const makeCommittedPromptSuffix = (
|
|
258
|
+
export const makeCommittedPromptSuffix = (
|
|
259
|
+
original: Pick<CanonicalRecordEnvelope, "threadId" | "sequence" | "record">,
|
|
260
|
+
) => {
|
|
256
261
|
const records: Array<PromptRecordEnvelope> = [];
|
|
257
262
|
|
|
258
263
|
const runId =
|
|
@@ -558,7 +563,7 @@ export const makePromptHistoryCache = Effect.fnUntraced(function* () {
|
|
|
558
563
|
const complete = Effect.fnUntraced(
|
|
559
564
|
function* (
|
|
560
565
|
writer: WriterIdentity,
|
|
561
|
-
original:
|
|
566
|
+
original: InitialContextInput,
|
|
562
567
|
history: PromptHistory,
|
|
563
568
|
committed?: CommittedPromptSuffix,
|
|
564
569
|
) {
|
|
@@ -3,36 +3,33 @@ import { Effect } from "effect";
|
|
|
3
3
|
import { summarizeModelUsage, type ModelCallUsage } from "../../core/Usage.ts";
|
|
4
4
|
|
|
5
5
|
/** Captured calls are additive and nonnegative; progress does not need attribution groups. */
|
|
6
|
-
export const continuationUsage =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let knownWebSearchCalls = 0;
|
|
6
|
+
export const continuationUsage = (calls: ReadonlyArray<ModelCallUsage>) =>
|
|
7
|
+
Effect.suspend(() => {
|
|
8
|
+
let inputTokens = 0;
|
|
9
|
+
let outputTokens = 0;
|
|
10
|
+
let costMicrousd = 0;
|
|
11
|
+
let knownWebSearchCalls = 0;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
for (const call of calls) {
|
|
14
|
+
inputTokens += call.inputTokens.total;
|
|
15
|
+
outputTokens += call.outputTokens.total;
|
|
16
|
+
costMicrousd += call.costMicrousd;
|
|
17
|
+
knownWebSearchCalls += call.webSearchCalls ?? 0;
|
|
18
|
+
}
|
|
19
|
+
if (
|
|
20
|
+
Number.isSafeInteger(inputTokens) &&
|
|
21
|
+
Number.isSafeInteger(outputTokens) &&
|
|
22
|
+
Number.isSafeInteger(costMicrousd) &&
|
|
23
|
+
Number.isSafeInteger(knownWebSearchCalls)
|
|
24
|
+
)
|
|
25
|
+
return Effect.succeed({ modelCalls: calls.length, inputTokens, outputTokens, costMicrousd });
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
});
|
|
27
|
+
// Safe global sums bound every component/group. On overflow retain the full validator,
|
|
28
|
+
// including groups whose hosted-search coverage differs from the aggregate's coverage.
|
|
29
|
+
return Effect.map(summarizeModelUsage(calls), (summary) => ({
|
|
30
|
+
modelCalls: summary.modelCalls,
|
|
31
|
+
inputTokens: summary.inputTokens.total,
|
|
32
|
+
outputTokens: summary.outputTokens.total,
|
|
33
|
+
costMicrousd: summary.costMicrousd,
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Effect } from "effect";
|
|
2
2
|
import * as Context from "effect/Context";
|
|
3
3
|
|
|
4
4
|
import type { AnyDefinition } from "../core/Agent.ts";
|
|
@@ -47,7 +47,9 @@ export class MessagingHost extends Context.Service<
|
|
|
47
47
|
}
|
|
48
48
|
>()("@effect-agent/engine/MessagingHost") {
|
|
49
49
|
static readonly unavailable: MessagingHost["Service"] = {
|
|
50
|
-
context:
|
|
50
|
+
context: Effect.suspend(() =>
|
|
51
|
+
MessagingError.make({ operation: "context", reason: "unavailable" }),
|
|
52
|
+
),
|
|
51
53
|
send: () => MessagingError.make({ operation: "send", reason: "unavailable" }),
|
|
52
54
|
reply: () => MessagingError.make({ operation: "reply", reason: "unavailable" }),
|
|
53
55
|
inbox: () => MessagingError.make({ operation: "inbox", reason: "unavailable" }),
|
|
@@ -75,57 +75,56 @@ export class ThreadHistory extends Context.Service<
|
|
|
75
75
|
}
|
|
76
76
|
>()("@effect-agent/engine/ThreadHistory") {
|
|
77
77
|
/** Bind incremental history to the supplied Thread.Store, including scoped ownership views. */
|
|
78
|
-
static readonly
|
|
79
|
-
|
|
80
|
-
Effect.gen(function* () {
|
|
81
|
-
const threads = yield* ConversationStore;
|
|
78
|
+
static readonly makeFromStore = Effect.gen(function* () {
|
|
79
|
+
const threads = yield* ConversationStore;
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
81
|
+
const historyError = (cause: ThreadError): ThreadHistoryError =>
|
|
82
|
+
ThreadHistoryError.make({
|
|
83
|
+
threadId: cause.threadId,
|
|
84
|
+
reason:
|
|
85
|
+
cause._tag === "ThreadNotFound"
|
|
86
|
+
? "not-found"
|
|
87
|
+
: cause._tag === "ThreadLimitExceeded"
|
|
88
|
+
? "limit"
|
|
89
|
+
: cause._tag === "ThreadHistoryDiverged"
|
|
90
|
+
? "conflict"
|
|
91
|
+
: cause._tag === "ThreadOwnershipError"
|
|
92
|
+
? cause.reason === "closed"
|
|
93
|
+
? "fenced"
|
|
94
|
+
: "conflict"
|
|
95
|
+
: "encoding",
|
|
96
|
+
message:
|
|
97
|
+
cause._tag === "ThreadNotFound"
|
|
98
|
+
? "Thread history is not present in this application Scope"
|
|
99
|
+
: cause._tag === "ThreadLimitExceeded"
|
|
100
|
+
? `In-memory history exceeds the ${cause.limit} limit of ${cause.limitValue}`
|
|
101
|
+
: cause.message,
|
|
102
|
+
cause,
|
|
103
|
+
});
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
return ThreadHistory.of({
|
|
106
|
+
retention: "incremental",
|
|
107
|
+
memoryStore: threads,
|
|
108
|
+
load: (threadId) =>
|
|
109
|
+
threads.snapshot(threadId).pipe(Effect.map(toPrompt), Effect.mapError(historyError)),
|
|
110
|
+
open: Effect.fnUntraced(function* ({ threadId, runId }) {
|
|
111
|
+
const snapshot = yield* threads.create(threadId).pipe(Effect.mapError(historyError));
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
return {
|
|
114
|
+
prompt: toPrompt(snapshot),
|
|
115
|
+
stageInput: () => Effect.void,
|
|
116
|
+
stageHistory: ({ source }) =>
|
|
117
|
+
threads
|
|
118
|
+
.recordHistory(threadId, runId, source)
|
|
119
|
+
.pipe(Effect.asVoid, Effect.mapError(historyError)),
|
|
120
|
+
commit: () =>
|
|
121
|
+
threads.snapshot(threadId).pipe(Effect.asVoid, Effect.mapError(historyError)),
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
static readonly layerFromStore = Layer.effect(ThreadHistory, ThreadHistory.makeFromStore);
|
|
129
128
|
|
|
130
129
|
/** Retain history for the application Scope, within message and host capacity. Separate Layer builds own separate stores. */
|
|
131
130
|
static readonly layer = ThreadHistory.layerFromStore.pipe(Layer.provideMerge(layerMemory));
|