@modelprofile.com/flexharness 10.0.0 → 10.2.0
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.flexharness.d.ts +13 -1
- package/dist_ts/classes.flexharness.js +302 -188
- package/dist_ts/classes.stores.js +11 -119
- package/dist_ts/errors.d.ts +7 -2
- package/dist_ts/errors.js +1 -1
- package/dist_ts/interfaces.d.ts +18 -0
- package/dist_ts/interfaces.js +1 -1
- package/dist_ts/utils.storerules.d.ts +28 -0
- package/dist_ts/utils.storerules.js +107 -0
- package/dist_ts_chat_harness/classes.transcript.d.ts +103 -0
- package/dist_ts_chat_harness/classes.transcript.js +296 -0
- package/dist_ts_chat_harness/functions.intents.d.ts +62 -0
- package/dist_ts_chat_harness/functions.intents.js +96 -0
- package/dist_ts_chat_harness/functions.mapping.d.ts +33 -0
- package/dist_ts_chat_harness/functions.mapping.js +220 -0
- package/dist_ts_chat_harness/index.d.ts +3 -0
- package/dist_ts_chat_harness/index.js +4 -0
- package/dist_ts_chat_harness/plugins.d.ts +1 -0
- package/dist_ts_chat_harness/plugins.js +2 -0
- package/dist_ts_stores_nosqldb/classes.nosqldbstores.d.ts +31 -0
- package/dist_ts_stores_nosqldb/classes.nosqldbstores.js +312 -0
- package/dist_ts_stores_nosqldb/classes.recordstore.d.ts +110 -0
- package/dist_ts_stores_nosqldb/classes.recordstore.js +437 -0
- package/dist_ts_stores_nosqldb/index.d.ts +2 -0
- package/dist_ts_stores_nosqldb/index.js +3 -0
- package/dist_ts_stores_nosqldb/plugins.d.ts +7 -0
- package/dist_ts_stores_nosqldb/plugins.js +10 -0
- package/package.json +24 -4
- package/readme.md +6 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.flexharness.ts +324 -205
- package/ts/classes.stores.ts +28 -172
- package/ts/errors.ts +8 -2
- package/ts/interfaces.ts +19 -0
- package/ts/readme.md +6 -0
- package/ts/utils.storerules.ts +177 -0
- package/ts_chat_harness/classes.transcript.ts +399 -0
- package/ts_chat_harness/functions.intents.ts +169 -0
- package/ts_chat_harness/functions.mapping.ts +267 -0
- package/ts_chat_harness/index.ts +33 -0
- package/ts_chat_harness/plugins.ts +18 -0
- package/ts_chat_harness/readme.md +117 -0
- package/ts_stores_nosqldb/classes.nosqldbstores.ts +465 -0
- package/ts_stores_nosqldb/classes.recordstore.ts +560 -0
- package/ts_stores_nosqldb/index.ts +9 -0
- package/ts_stores_nosqldb/plugins.ts +38 -0
- package/ts_stores_nosqldb/readme.md +53 -0
|
@@ -90,6 +90,7 @@ import type {
|
|
|
90
90
|
IFlexSchedulePromptOptions,
|
|
91
91
|
IFlexScopeSnapshot,
|
|
92
92
|
IFlexSession,
|
|
93
|
+
IFlexSessionView,
|
|
93
94
|
IFlexSessionPermissionPolicyContext,
|
|
94
95
|
IFlexSessionActivity,
|
|
95
96
|
IFlexSessionGeneration,
|
|
@@ -360,6 +361,11 @@ interface IStorageState {
|
|
|
360
361
|
activeRuns: Map<string, IActiveRun>;
|
|
361
362
|
delegatedRunAdmissionOwners: Set<IDelegatedRunAdmissionOwner>;
|
|
362
363
|
pendingPermissions: Map<string, IPendingPermission>;
|
|
364
|
+
/**
|
|
365
|
+
* Per session, the open sections that change what a session view shows and emit the events
|
|
366
|
+
* describing it. A view waits until none is open, so it never shows a change without its event.
|
|
367
|
+
*/
|
|
368
|
+
publications: Map<string, Set<Promise<void>>>;
|
|
363
369
|
scopeQueue: Promise<void>;
|
|
364
370
|
lifecycle: 'active' | 'retiring' | 'fenced' | 'retired';
|
|
365
371
|
fenceInProgress: boolean;
|
|
@@ -652,6 +658,12 @@ type TEventDetails = Record<string, unknown> & {
|
|
|
652
658
|
|
|
653
659
|
const maxScheduleDebounceMs = 24 * 60 * 60 * 1000;
|
|
654
660
|
const maxBackgroundExecutions = 100;
|
|
661
|
+
/**
|
|
662
|
+
* Consecutive waits a session view makes for queued writes and open sections to settle. Each wait
|
|
663
|
+
* ends only when real work finishes, so the bound limits starvation by a session that never stops
|
|
664
|
+
* changing, not the time one slow save may take.
|
|
665
|
+
*/
|
|
666
|
+
const maxSessionViewPasses = 64;
|
|
655
667
|
const maxSubagentTaskDescriptionBytes = 256;
|
|
656
668
|
const maxSubagentPromptBytes = 64 * 1024;
|
|
657
669
|
const maxSubagentTaskIdBytes = 512;
|
|
@@ -1924,6 +1936,75 @@ export class FlexHarness<TScope = unknown> {
|
|
|
1924
1936
|
}
|
|
1925
1937
|
}
|
|
1926
1938
|
|
|
1939
|
+
/**
|
|
1940
|
+
* Reads the session's metadata, visible messages and pending permissions in one step, together
|
|
1941
|
+
* with the instance's event sequence they include. It waits until no queued projection or scope
|
|
1942
|
+
* write and no change that emits events for the session is in progress.
|
|
1943
|
+
*/
|
|
1944
|
+
public async getSessionView(scopeId: string, sessionId: string): Promise<IFlexSessionView> {
|
|
1945
|
+
const { state } = await this.resolveState(scopeId);
|
|
1946
|
+
let stored = this.requireSession(state, sessionId);
|
|
1947
|
+
for (let pass = 0; ; pass++) {
|
|
1948
|
+
if (pass === maxSessionViewPasses) {
|
|
1949
|
+
throw new FlexHarnessSessionBusyError(
|
|
1950
|
+
sessionId,
|
|
1951
|
+
`kept changing through ${maxSessionViewPasses} consecutive waits for a settled view`,
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
const scopeQueue = state.scopeQueue;
|
|
1955
|
+
const projectionQueue = stored.projectionQueue;
|
|
1956
|
+
const publications = [...(state.publications.get(sessionId) ?? [])];
|
|
1957
|
+
await Promise.all([scopeQueue, projectionQueue, ...publications]);
|
|
1958
|
+
this.assertStateAcceptingWork(state);
|
|
1959
|
+
stored = this.requireSession(state, sessionId);
|
|
1960
|
+
if (
|
|
1961
|
+
state.scopeQueue === scopeQueue
|
|
1962
|
+
&& stored.projectionQueue === projectionQueue
|
|
1963
|
+
&& !state.publications.get(sessionId)?.size
|
|
1964
|
+
) break;
|
|
1965
|
+
}
|
|
1966
|
+
const run = state.activeRuns.get(sessionId);
|
|
1967
|
+
const messages = this.visibleMessages(stored).map((message) => (
|
|
1968
|
+
run && run.stored === stored && message.messageId === run.assistantMessageId
|
|
1969
|
+
&& message.status === 'streaming'
|
|
1970
|
+
? { ...message, parts: run.callbackParts }
|
|
1971
|
+
: message
|
|
1972
|
+
));
|
|
1973
|
+
return publicSnapshot({
|
|
1974
|
+
session: stored.session,
|
|
1975
|
+
messages,
|
|
1976
|
+
pendingPermissions: [...state.pendingPermissions.values()]
|
|
1977
|
+
.map((pending) => pending.request)
|
|
1978
|
+
.filter((request) => request.sessionId === sessionId)
|
|
1979
|
+
.sort((left, right) => left.createdAt.localeCompare(right.createdAt)),
|
|
1980
|
+
sequence: this.sequence,
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* Opens a section that changes what a view of the session shows and emits the events that
|
|
1986
|
+
* describe the change. Closing is idempotent; close it once the events are emitted or the
|
|
1987
|
+
* change is rolled back.
|
|
1988
|
+
*/
|
|
1989
|
+
private openPublication(state: IStorageState, sessionId: string): () => void {
|
|
1990
|
+
let release!: () => void;
|
|
1991
|
+
const section = new Promise<void>((resolve) => {
|
|
1992
|
+
release = resolve;
|
|
1993
|
+
});
|
|
1994
|
+
let sections = state.publications.get(sessionId);
|
|
1995
|
+
if (!sections) {
|
|
1996
|
+
sections = new Set();
|
|
1997
|
+
state.publications.set(sessionId, sections);
|
|
1998
|
+
}
|
|
1999
|
+
sections.add(section);
|
|
2000
|
+
return () => {
|
|
2001
|
+
const current = state.publications.get(sessionId);
|
|
2002
|
+
if (!current?.delete(section)) return;
|
|
2003
|
+
if (current.size === 0) state.publications.delete(sessionId);
|
|
2004
|
+
release();
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
|
|
1927
2008
|
public async getMessages(scopeId: string, sessionId: string): Promise<IFlexMessage[]> {
|
|
1928
2009
|
const { state } = await this.resolveState(scopeId);
|
|
1929
2010
|
const stored = this.requireSession(state, sessionId);
|
|
@@ -3397,12 +3478,17 @@ export class FlexHarness<TScope = unknown> {
|
|
|
3397
3478
|
.map((runId) => stored.reversionSegments.find((segment) => segment.runId === runId))
|
|
3398
3479
|
.find((segment) => segment?.status === 'completed');
|
|
3399
3480
|
if (!target) throw new FlexHarnessValidationError('Pending reversion has no target candidate.');
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3481
|
+
const closePublication = this.openPublication(state, sessionId);
|
|
3482
|
+
try {
|
|
3483
|
+
await this.resumePendingApply(state, stored, scopeId, scope, operationSignal);
|
|
3484
|
+
this.emitEvent(scopeId, stored.session, {
|
|
3485
|
+
type: 'session.history.changed',
|
|
3486
|
+
direction,
|
|
3487
|
+
runId: target.runId,
|
|
3488
|
+
});
|
|
3489
|
+
} finally {
|
|
3490
|
+
closePublication();
|
|
3491
|
+
}
|
|
3406
3492
|
return target.runId;
|
|
3407
3493
|
}
|
|
3408
3494
|
const unit = this.reversionUnit(stored, direction);
|
|
@@ -3442,18 +3528,23 @@ export class FlexHarness<TScope = unknown> {
|
|
|
3442
3528
|
appliedRunIds: [],
|
|
3443
3529
|
};
|
|
3444
3530
|
}, true);
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3531
|
+
const closePublication = this.openPublication(state, sessionId);
|
|
3532
|
+
try {
|
|
3533
|
+
await this.resumePendingApply(
|
|
3534
|
+
state,
|
|
3535
|
+
stored,
|
|
3536
|
+
scopeId,
|
|
3537
|
+
scope,
|
|
3538
|
+
operationSignal,
|
|
3539
|
+
);
|
|
3540
|
+
this.emitEvent(scopeId, stored.session, {
|
|
3541
|
+
type: 'session.history.changed',
|
|
3542
|
+
direction,
|
|
3543
|
+
runId: unit.target.runId,
|
|
3544
|
+
});
|
|
3545
|
+
} finally {
|
|
3546
|
+
closePublication();
|
|
3547
|
+
}
|
|
3457
3548
|
return unit.target.runId;
|
|
3458
3549
|
},
|
|
3459
3550
|
);
|
|
@@ -4405,123 +4496,130 @@ export class FlexHarness<TScope = unknown> {
|
|
|
4405
4496
|
const prompt = queued.prompt!;
|
|
4406
4497
|
const options = queued.options!;
|
|
4407
4498
|
let projectionReserved = false;
|
|
4499
|
+
// Open from the reservation until its messages are announced or rolled back.
|
|
4500
|
+
let closeReservationPublication = (): void => undefined;
|
|
4408
4501
|
try {
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
run
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
status: 'capturing',
|
|
4439
|
-
contextAvailable: true,
|
|
4440
|
-
eventIds: [],
|
|
4441
|
-
workspaceCaptured: captureId !== undefined,
|
|
4442
|
-
protocolVersion,
|
|
4443
|
-
provenance: captureId === undefined ? 'transcript' : 'workspace',
|
|
4444
|
-
...(captureId === undefined ? {} : { disposition: 'pending' }),
|
|
4445
|
-
...(captureId === undefined ? {} : { captureId }),
|
|
4446
|
-
});
|
|
4447
|
-
if (captureId) {
|
|
4448
|
-
run.stored.pendingReversion = {
|
|
4449
|
-
kind: 'capture',
|
|
4502
|
+
try {
|
|
4503
|
+
this.assertPromptPromotion(queued, run);
|
|
4504
|
+
await this.acquireDelegatedRunAdmission(queued, run);
|
|
4505
|
+
this.assertPromptPromotion(queued, run);
|
|
4506
|
+
await this.commitRevertedBranch(run.state, run.stored, run.scopeId, run.scope as TScope);
|
|
4507
|
+
this.assertPromptPromotion(queued, run);
|
|
4508
|
+
const generationPrompt = cloneSerializable(prompt.modelMessage.content) as Parameters<
|
|
4509
|
+
plugins.IAgentSession['beginGeneration']
|
|
4510
|
+
>[0];
|
|
4511
|
+
this.assertPromptPromotion(queued, run);
|
|
4512
|
+
run.transaction = await this.withRunCompactorContext(
|
|
4513
|
+
run,
|
|
4514
|
+
() => run.stored.agentSession.beginGeneration(
|
|
4515
|
+
generationPrompt,
|
|
4516
|
+
{ generationId: run.runId },
|
|
4517
|
+
),
|
|
4518
|
+
);
|
|
4519
|
+
this.assertPromptPromotion(queued, run);
|
|
4520
|
+
const reservation = this.createReservation(run, prompt);
|
|
4521
|
+
closeReservationPublication = this.openPublication(run.state, run.sessionId);
|
|
4522
|
+
await this.mutateProjection(run.state, run.stored, () => {
|
|
4523
|
+
run.stored.messages.push(reservation.userMessage, reservation.assistantMessage);
|
|
4524
|
+
if (run.stored.session.agent === undefined) {
|
|
4525
|
+
this.pruneReversionState(run.stored, true);
|
|
4526
|
+
const captureId = this.turnReversionProvider
|
|
4527
|
+
? this.reversionId('capture', run.state.storageKey, run.sessionId, run.runId)
|
|
4528
|
+
: undefined;
|
|
4529
|
+
const protocolVersion = this.reversionProviderProtocolVersion() ?? 1;
|
|
4530
|
+
run.stored.reversionSegments.push({
|
|
4450
4531
|
runId: run.runId,
|
|
4451
|
-
|
|
4452
|
-
|
|
4532
|
+
userMessageId: reservation.userMessage.messageId,
|
|
4533
|
+
status: 'capturing',
|
|
4534
|
+
contextAvailable: true,
|
|
4535
|
+
eventIds: [],
|
|
4536
|
+
workspaceCaptured: captureId !== undefined,
|
|
4453
4537
|
protocolVersion,
|
|
4454
|
-
|
|
4538
|
+
provenance: captureId === undefined ? 'transcript' : 'workspace',
|
|
4539
|
+
...(captureId === undefined ? {} : { disposition: 'pending' }),
|
|
4540
|
+
...(captureId === undefined ? {} : { captureId }),
|
|
4541
|
+
});
|
|
4542
|
+
if (captureId) {
|
|
4543
|
+
run.stored.pendingReversion = {
|
|
4544
|
+
kind: 'capture',
|
|
4545
|
+
runId: run.runId,
|
|
4546
|
+
captureId,
|
|
4547
|
+
state: 'preparing',
|
|
4548
|
+
protocolVersion,
|
|
4549
|
+
};
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
});
|
|
4553
|
+
projectionReserved = true;
|
|
4554
|
+
run.reservedUserMessage = publicSnapshot(reservation.userMessage);
|
|
4555
|
+
run.reservedAssistantMessage = publicSnapshot(reservation.assistantMessage);
|
|
4556
|
+
this.assertPromptPromotion(queued, run);
|
|
4557
|
+
await this.mutateScope(run.state, () => {
|
|
4558
|
+
if (run.controller.signal.aborted) {
|
|
4559
|
+
throw run.controller.signal.reason ?? new FlexHarnessAbortError();
|
|
4560
|
+
}
|
|
4561
|
+
if (run.state.tombstones.has(run.sessionId) || run.state.sessions.get(run.sessionId) !== run.stored) {
|
|
4562
|
+
throw new FlexHarnessAbortError('The session was deleted during prompt admission.');
|
|
4455
4563
|
}
|
|
4564
|
+
run.stored.session.status = run.scheduleKey ? 'scheduled' : 'running';
|
|
4565
|
+
run.stored.session.activity = {
|
|
4566
|
+
runId: run.runId,
|
|
4567
|
+
status: run.scheduleKey ? 'scheduled' : 'running',
|
|
4568
|
+
startedAt: reservation.userMessage.createdAt,
|
|
4569
|
+
};
|
|
4570
|
+
run.stored.session.updatedAt = reservation.userMessage.createdAt;
|
|
4571
|
+
});
|
|
4572
|
+
} catch (error) {
|
|
4573
|
+
if (containsStoreCommitUncertainty(error)) run.state.lifecycle = 'fenced';
|
|
4574
|
+
const safeError = this.projectExternalError(run, error, projectionReserved ? 'persistence' : 'agentSession');
|
|
4575
|
+
let cleanupErrors: unknown[] = [];
|
|
4576
|
+
let cleanupFailure: unknown;
|
|
4577
|
+
try {
|
|
4578
|
+
await this.closeRunDelegatedRunAdmission(
|
|
4579
|
+
run,
|
|
4580
|
+
!run.delegatedRunAdmissionOwner?.acquisitionDetached,
|
|
4581
|
+
);
|
|
4582
|
+
} catch (closeError) {
|
|
4583
|
+
cleanupErrors.push(closeError);
|
|
4456
4584
|
}
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
this.assertPromptPromotion(queued, run);
|
|
4462
|
-
await this.mutateScope(run.state, () => {
|
|
4463
|
-
if (run.controller.signal.aborted) {
|
|
4464
|
-
throw run.controller.signal.reason ?? new FlexHarnessAbortError();
|
|
4585
|
+
try {
|
|
4586
|
+
cleanupErrors.push(...await this.rollbackAdmission(run, projectionReserved, safeError));
|
|
4587
|
+
} catch (rollbackError) {
|
|
4588
|
+
cleanupFailure = rollbackError;
|
|
4465
4589
|
}
|
|
4466
|
-
if (
|
|
4467
|
-
|
|
4590
|
+
if (cleanupFailure !== undefined && cleanupErrors.length > 0) {
|
|
4591
|
+
cleanupFailure = combineErrors([cleanupFailure, ...cleanupErrors]);
|
|
4468
4592
|
}
|
|
4469
|
-
run.
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
});
|
|
4477
|
-
} catch (error) {
|
|
4478
|
-
if (containsStoreCommitUncertainty(error)) run.state.lifecycle = 'fenced';
|
|
4479
|
-
const safeError = this.projectExternalError(run, error, projectionReserved ? 'persistence' : 'agentSession');
|
|
4480
|
-
let cleanupErrors: unknown[] = [];
|
|
4481
|
-
let cleanupFailure: unknown;
|
|
4482
|
-
try {
|
|
4483
|
-
await this.closeRunDelegatedRunAdmission(
|
|
4484
|
-
run,
|
|
4485
|
-
!run.delegatedRunAdmissionOwner?.acquisitionDetached,
|
|
4486
|
-
);
|
|
4487
|
-
} catch (closeError) {
|
|
4488
|
-
cleanupErrors.push(closeError);
|
|
4489
|
-
}
|
|
4490
|
-
try {
|
|
4491
|
-
cleanupErrors.push(...await this.rollbackAdmission(run, projectionReserved, safeError));
|
|
4492
|
-
} catch (rollbackError) {
|
|
4493
|
-
cleanupFailure = rollbackError;
|
|
4494
|
-
}
|
|
4495
|
-
if (cleanupFailure !== undefined && cleanupErrors.length > 0) {
|
|
4496
|
-
cleanupFailure = combineErrors([cleanupFailure, ...cleanupErrors]);
|
|
4497
|
-
}
|
|
4498
|
-
if (run.state.lifecycle === 'fenced' && cleanupFailure === undefined) {
|
|
4499
|
-
const combined = combineErrors([safeError, ...cleanupErrors]);
|
|
4500
|
-
try {
|
|
4501
|
-
await this.fenceNamespace(run.state, run, combined);
|
|
4502
|
-
} catch (fenceError) {
|
|
4503
|
-
cleanupFailure = fenceError;
|
|
4593
|
+
if (run.state.lifecycle === 'fenced' && cleanupFailure === undefined) {
|
|
4594
|
+
const combined = combineErrors([safeError, ...cleanupErrors]);
|
|
4595
|
+
try {
|
|
4596
|
+
await this.fenceNamespace(run.state, run, combined);
|
|
4597
|
+
} catch (fenceError) {
|
|
4598
|
+
cleanupFailure = fenceError;
|
|
4599
|
+
}
|
|
4504
4600
|
}
|
|
4601
|
+
const rejection = cleanupFailure
|
|
4602
|
+
?? (cleanupErrors.length > 0 ? combineErrors([safeError, ...cleanupErrors]) : safeError);
|
|
4603
|
+
throw rejection;
|
|
4505
4604
|
}
|
|
4506
|
-
const rejection = cleanupFailure
|
|
4507
|
-
?? (cleanupErrors.length > 0 ? combineErrors([safeError, ...cleanupErrors]) : safeError);
|
|
4508
|
-
throw rejection;
|
|
4509
|
-
}
|
|
4510
4605
|
|
|
4511
|
-
|
|
4512
|
-
this.emitEvent(run.scopeId, run.stored.session, {
|
|
4513
|
-
type: runType,
|
|
4514
|
-
runId: run.runId,
|
|
4515
|
-
messageId: run.userMessageId,
|
|
4516
|
-
session: publicSnapshot(run.stored.session),
|
|
4517
|
-
});
|
|
4518
|
-
for (const message of [run.reservedUserMessage!, run.reservedAssistantMessage!]) {
|
|
4606
|
+
const runType = run.scheduleKey ? 'run.scheduled' : 'run.started';
|
|
4519
4607
|
this.emitEvent(run.scopeId, run.stored.session, {
|
|
4520
|
-
type:
|
|
4608
|
+
type: runType,
|
|
4521
4609
|
runId: run.runId,
|
|
4522
|
-
messageId:
|
|
4523
|
-
|
|
4610
|
+
messageId: run.userMessageId,
|
|
4611
|
+
session: publicSnapshot(run.stored.session),
|
|
4524
4612
|
});
|
|
4613
|
+
for (const message of [run.reservedUserMessage!, run.reservedAssistantMessage!]) {
|
|
4614
|
+
this.emitEvent(run.scopeId, run.stored.session, {
|
|
4615
|
+
type: 'message.created',
|
|
4616
|
+
runId: run.runId,
|
|
4617
|
+
messageId: message.messageId,
|
|
4618
|
+
message,
|
|
4619
|
+
});
|
|
4620
|
+
}
|
|
4621
|
+
} finally {
|
|
4622
|
+
closeReservationPublication();
|
|
4525
4623
|
}
|
|
4526
4624
|
|
|
4527
4625
|
queued.status = run.scheduleKey ? 'scheduled' : 'starting';
|
|
@@ -4729,15 +4827,20 @@ export class FlexHarness<TScope = unknown> {
|
|
|
4729
4827
|
run.steering.keptInContext = true;
|
|
4730
4828
|
this.refreshAutomaticArchiveHorizon(run.stored);
|
|
4731
4829
|
run.phase = 'promoting';
|
|
4830
|
+
const closePublication = this.openPublication(run.state, run.sessionId);
|
|
4732
4831
|
try {
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4832
|
+
try {
|
|
4833
|
+
await this.promoteCompletedTerminal(run, terminal);
|
|
4834
|
+
} catch (error) {
|
|
4835
|
+
const projected = this.projectExternalError(run, error, 'persistence');
|
|
4836
|
+
await this.fenceNamespace(run.state, run, projected);
|
|
4837
|
+
throw projected;
|
|
4838
|
+
}
|
|
4839
|
+
this.emitTerminalProjection(run, terminal);
|
|
4840
|
+
this.emitFinalRunEvent(run, terminal.assistantMessage, undefined, false);
|
|
4841
|
+
} finally {
|
|
4842
|
+
closePublication();
|
|
4738
4843
|
}
|
|
4739
|
-
this.emitTerminalProjection(run, terminal);
|
|
4740
|
-
this.emitFinalRunEvent(run, terminal.assistantMessage, undefined, false);
|
|
4741
4844
|
await this.drainReversionReleases(
|
|
4742
4845
|
run.state,
|
|
4743
4846
|
run.stored,
|
|
@@ -4858,16 +4961,21 @@ export class FlexHarness<TScope = unknown> {
|
|
|
4858
4961
|
: safeError;
|
|
4859
4962
|
const terminal = this.buildTerminal(run, publicStatus, undefined, terminalError);
|
|
4860
4963
|
if (!this.isTombstoned(run)) {
|
|
4964
|
+
const closePublication = this.openPublication(run.state, run.sessionId);
|
|
4861
4965
|
try {
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4966
|
+
try {
|
|
4967
|
+
await this.publishFailedTerminal(run, terminal);
|
|
4968
|
+
} catch (persistenceError) {
|
|
4969
|
+
errors.push(this.projectExternalError(run, persistenceError, 'persistence'));
|
|
4970
|
+
const combined = combineErrors(errors);
|
|
4971
|
+
await this.fenceNamespace(run.state, run, combined);
|
|
4972
|
+
throw combined;
|
|
4973
|
+
}
|
|
4974
|
+
this.emitTerminalProjection(run, terminal);
|
|
4975
|
+
this.emitFinalRunEvent(run, terminal.assistantMessage, terminalError, publicStatus === 'cancelled');
|
|
4976
|
+
} finally {
|
|
4977
|
+
closePublication();
|
|
4868
4978
|
}
|
|
4869
|
-
this.emitTerminalProjection(run, terminal);
|
|
4870
|
-
this.emitFinalRunEvent(run, terminal.assistantMessage, terminalError, publicStatus === 'cancelled');
|
|
4871
4979
|
}
|
|
4872
4980
|
await this.drainReversionReleases(
|
|
4873
4981
|
run.state,
|
|
@@ -6561,61 +6669,66 @@ export class FlexHarness<TScope = unknown> {
|
|
|
6561
6669
|
parts: [],
|
|
6562
6670
|
}
|
|
6563
6671
|
: undefined;
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6672
|
+
const closePublication = this.openPublication(run.state, run.sessionId);
|
|
6673
|
+
try {
|
|
6674
|
+
await this.mutateProjection(run.state, run.stored, () => {
|
|
6675
|
+
const index = run.stored.messages.findIndex((message) => message.messageId === answeredAssistantId);
|
|
6676
|
+
if (index < 0) throw new Error('The steered run has no projected assistant message.');
|
|
6677
|
+
if (!nextAssistant) {
|
|
6678
|
+
run.stored.messages.splice(index, 0, cloneSerializable(steerMessage));
|
|
6679
|
+
return;
|
|
6680
|
+
}
|
|
6681
|
+
completedAssistant = {
|
|
6682
|
+
...cloneSerializable(run.stored.messages[index]),
|
|
6683
|
+
status: 'completed',
|
|
6684
|
+
completedAt: timestamp,
|
|
6685
|
+
parts: cloneSerializable(run.callbackParts),
|
|
6686
|
+
...(run.modelResolution ? { model: cloneSerializable(run.modelResolution.identity) } : {}),
|
|
6687
|
+
};
|
|
6688
|
+
run.stored.messages.splice(
|
|
6689
|
+
index,
|
|
6690
|
+
1,
|
|
6691
|
+
cloneSerializable(completedAssistant),
|
|
6692
|
+
cloneSerializable(steerMessage),
|
|
6693
|
+
cloneSerializable(nextAssistant),
|
|
6694
|
+
);
|
|
6695
|
+
});
|
|
6696
|
+
if (completedAssistant && nextAssistant) {
|
|
6697
|
+
run.assistantMessageId = nextAssistant.messageId;
|
|
6698
|
+
run.reservedAssistantMessage = publicSnapshot(nextAssistant);
|
|
6699
|
+
run.callbackParts = [];
|
|
6700
|
+
run.reasoningPartIds.clear();
|
|
6701
|
+
this.emitEvent(run.scopeId, run.stored.session, {
|
|
6702
|
+
type: 'message.updated',
|
|
6703
|
+
runId: run.runId,
|
|
6704
|
+
messageId: completedAssistant.messageId,
|
|
6705
|
+
message: publicSnapshot(completedAssistant),
|
|
6706
|
+
});
|
|
6570
6707
|
}
|
|
6571
|
-
completedAssistant = {
|
|
6572
|
-
...cloneSerializable(run.stored.messages[index]),
|
|
6573
|
-
status: 'completed',
|
|
6574
|
-
completedAt: timestamp,
|
|
6575
|
-
parts: cloneSerializable(run.callbackParts),
|
|
6576
|
-
...(run.modelResolution ? { model: cloneSerializable(run.modelResolution.identity) } : {}),
|
|
6577
|
-
};
|
|
6578
|
-
run.stored.messages.splice(
|
|
6579
|
-
index,
|
|
6580
|
-
1,
|
|
6581
|
-
cloneSerializable(completedAssistant),
|
|
6582
|
-
cloneSerializable(steerMessage),
|
|
6583
|
-
cloneSerializable(nextAssistant),
|
|
6584
|
-
);
|
|
6585
|
-
});
|
|
6586
|
-
if (completedAssistant && nextAssistant) {
|
|
6587
|
-
run.assistantMessageId = nextAssistant.messageId;
|
|
6588
|
-
run.reservedAssistantMessage = publicSnapshot(nextAssistant);
|
|
6589
|
-
run.callbackParts = [];
|
|
6590
|
-
run.reasoningPartIds.clear();
|
|
6591
6708
|
this.emitEvent(run.scopeId, run.stored.session, {
|
|
6592
|
-
type: 'message.
|
|
6709
|
+
type: 'message.created',
|
|
6593
6710
|
runId: run.runId,
|
|
6594
|
-
messageId:
|
|
6595
|
-
message: publicSnapshot(
|
|
6711
|
+
messageId: steerMessage.messageId,
|
|
6712
|
+
message: publicSnapshot(steerMessage),
|
|
6596
6713
|
});
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6714
|
+
if (nextAssistant) {
|
|
6715
|
+
this.emitEvent(run.scopeId, run.stored.session, {
|
|
6716
|
+
type: 'message.created',
|
|
6717
|
+
runId: run.runId,
|
|
6718
|
+
messageId: nextAssistant.messageId,
|
|
6719
|
+
message: publicSnapshot(nextAssistant),
|
|
6720
|
+
});
|
|
6721
|
+
}
|
|
6605
6722
|
this.emitEvent(run.scopeId, run.stored.session, {
|
|
6606
|
-
type: '
|
|
6723
|
+
type: 'prompt.steer.applied',
|
|
6724
|
+
queueId: run.queueId,
|
|
6607
6725
|
runId: run.runId,
|
|
6608
|
-
|
|
6609
|
-
|
|
6726
|
+
steerId: applied.inputId,
|
|
6727
|
+
messageId: steerMessage.messageId,
|
|
6610
6728
|
});
|
|
6729
|
+
} finally {
|
|
6730
|
+
closePublication();
|
|
6611
6731
|
}
|
|
6612
|
-
this.emitEvent(run.scopeId, run.stored.session, {
|
|
6613
|
-
type: 'prompt.steer.applied',
|
|
6614
|
-
queueId: run.queueId,
|
|
6615
|
-
runId: run.runId,
|
|
6616
|
-
steerId: applied.inputId,
|
|
6617
|
-
messageId: steerMessage.messageId,
|
|
6618
|
-
});
|
|
6619
6732
|
}
|
|
6620
6733
|
|
|
6621
6734
|
private buildTerminal(
|
|
@@ -8255,28 +8368,33 @@ export class FlexHarness<TScope = unknown> {
|
|
|
8255
8368
|
responseQueue: Promise.resolve(),
|
|
8256
8369
|
abortListener,
|
|
8257
8370
|
};
|
|
8258
|
-
|
|
8259
|
-
run.pendingPermissionIds.add(request.permissionId);
|
|
8260
|
-
run.controller.signal.addEventListener('abort', abortListener, { once: true });
|
|
8371
|
+
const closePublication = this.openPublication(state, run.sessionId);
|
|
8261
8372
|
try {
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8373
|
+
state.pendingPermissions.set(request.permissionId, pending);
|
|
8374
|
+
run.pendingPermissionIds.add(request.permissionId);
|
|
8375
|
+
run.controller.signal.addEventListener('abort', abortListener, { once: true });
|
|
8376
|
+
try {
|
|
8377
|
+
await this.mutateScope(state, () => {
|
|
8378
|
+
if (state.sessions.get(run.sessionId) !== run.stored) {
|
|
8379
|
+
throw new FlexHarnessAbortError('The session was deleted while permission was pending.');
|
|
8380
|
+
}
|
|
8381
|
+
run.stored.session.status = 'waiting_permission';
|
|
8382
|
+
run.stored.session.activity.status = 'waiting_permission';
|
|
8383
|
+
run.stored.session.updatedAt = new Date().toISOString();
|
|
8384
|
+
}, true);
|
|
8385
|
+
} catch (error) {
|
|
8386
|
+
this.forceRejectPending(state, pending, this.projectExternalError(run, error, 'persistence'));
|
|
8387
|
+
return promise;
|
|
8388
|
+
}
|
|
8389
|
+
if (!pending.settled) {
|
|
8390
|
+
this.emitEvent(run.scopeId, run.stored.session, {
|
|
8391
|
+
type: 'permission.requested',
|
|
8392
|
+
runId: run.runId,
|
|
8393
|
+
request: publicSnapshot(request),
|
|
8394
|
+
});
|
|
8395
|
+
}
|
|
8396
|
+
} finally {
|
|
8397
|
+
closePublication();
|
|
8280
8398
|
}
|
|
8281
8399
|
return promise;
|
|
8282
8400
|
}
|
|
@@ -9085,6 +9203,7 @@ export class FlexHarness<TScope = unknown> {
|
|
|
9085
9203
|
activeRuns: new Map(),
|
|
9086
9204
|
delegatedRunAdmissionOwners: new Set(),
|
|
9087
9205
|
pendingPermissions: new Map(),
|
|
9206
|
+
publications: new Map(),
|
|
9088
9207
|
scopeQueue: Promise.resolve(),
|
|
9089
9208
|
lifecycle: 'active',
|
|
9090
9209
|
fenceInProgress: false,
|