@ponythewhite/base-context 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/base-context-runtime/pyproject.toml +1 -1
- package/dist/base-context-runtime/src/rlm/bash.py +19 -0
- package/dist/base-context-runtime/src/rlm/job_watch.py +156 -0
- package/dist/base-context-runtime/src/rlm/repl.md +28 -6
- package/dist/base-context-runtime/src/rlm/repl.py +6 -1
- package/dist/base-context-runtime/test/test_bash.py +4 -4
- package/dist/base-context-runtime/test/test_job_watch.py +67 -0
- package/dist/base-context-runtime/uv.lock +1 -1
- package/dist/build-info.json +1 -1
- package/dist/bundle/{bundled-modules-UODO7ZR7.js → bundled-modules-2DMTK7VK.js} +2 -2
- package/dist/bundle/{chunk-SX7CAXWU.js → chunk-MSH6QRP5.js} +1730 -867
- package/dist/bundle/{chunk-NLRWERWA.js → chunk-T2OK7SRV.js} +53 -21
- package/dist/bundle/{cli-main-6GXXIMQQ.js → cli-main-WL2L5IEQ.js} +2 -2
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{main-HF64B4HM.js → main-XPIEIEQJ.js} +2 -2
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +191 -33
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -9
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/history-index-worker.d.ts.map +1 -1
- package/dist/core/history-index-worker.js +22 -1
- package/dist/core/history-index-worker.js.map +1 -1
- package/dist/core/history-index.d.ts +2 -0
- package/dist/core/history-index.d.ts.map +1 -1
- package/dist/core/history-index.js.map +1 -1
- package/dist/core/inference-coordinator.d.ts.map +1 -1
- package/dist/core/inference-coordinator.js +13 -2
- package/dist/core/inference-coordinator.js.map +1 -1
- package/dist/core/job-watch.d.ts +120 -0
- package/dist/core/job-watch.d.ts.map +1 -0
- package/dist/core/job-watch.js +648 -0
- package/dist/core/job-watch.js.map +1 -0
- package/dist/core/kernel/repl-manager.d.ts +5 -1
- package/dist/core/kernel/repl-manager.d.ts.map +1 -1
- package/dist/core/kernel/repl-manager.js +57 -0
- package/dist/core/kernel/repl-manager.js.map +1 -1
- package/dist/core/kernel/shared.d.ts +12 -0
- package/dist/core/kernel/shared.d.ts.map +1 -1
- package/dist/core/kernel/shared.js.map +1 -1
- package/dist/core/prompts/rlm.d.ts.map +1 -1
- package/dist/core/prompts/rlm.js +49 -39
- package/dist/core/prompts/rlm.js.map +1 -1
- package/dist/core/session-manager.d.ts +10 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +66 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/prime-context.d.ts.map +1 -1
- package/dist/core/tools/prime-context.js +1 -1
- package/dist/core/tools/prime-context.js.map +1 -1
- package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-mode.js +17 -17
- package/dist/modes/daemon/daemon-mode.js.map +1 -1
- package/dist/modes/daemon/daemon-worker-client.d.ts +1 -0
- package/dist/modes/daemon/daemon-worker-client.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-worker-client.js +8 -0
- package/dist/modes/daemon/daemon-worker-client.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +31 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/skills/agent-message/SKILL.md +8 -2
- package/dist/skills/bounded-inspect/SKILL.md +78 -0
- package/dist/skills/bounded-inspect/pyproject.toml +16 -0
- package/dist/skills/bounded-inspect/src/bounded_inspect/__init__.py +313 -0
- package/dist/skills/bounded-inspect/src/bounded_inspect/__main__.py +3 -0
- package/dist/skills/job-watch/SKILL.md +43 -0
- package/dist/skills/job-watch/pyproject.toml +13 -0
- package/dist/skills/job-watch/src/job_watch/__init__.py +61 -0
- package/dist/skills/rlm-heartbeat/SKILL.md +10 -6
- package/dist/utils/file-lines.d.ts.map +1 -1
- package/dist/utils/file-lines.js +6 -2
- package/dist/utils/file-lines.js.map +1 -1
- package/docs/rlm-runtime.md +4 -1
- package/docs/skills.md +18 -26
- package/docs/upstream-1.0.15.md +83 -0
- package/package.json +4 -4
- package/skills/agent-message/SKILL.md +8 -2
- package/skills/bounded-inspect/SKILL.md +78 -0
- package/skills/bounded-inspect/pyproject.toml +16 -0
- package/skills/bounded-inspect/src/bounded_inspect/__init__.py +313 -0
- package/skills/bounded-inspect/src/bounded_inspect/__main__.py +3 -0
- package/skills/job-watch/SKILL.md +43 -0
- package/skills/job-watch/pyproject.toml +13 -0
- package/skills/job-watch/src/job_watch/__init__.py +61 -0
- package/skills/rlm-heartbeat/SKILL.md +10 -6
|
@@ -27,6 +27,7 @@ import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
|
|
|
27
27
|
import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
28
28
|
import { createGoalContextMessage, emptyGoalState, GOAL_CONTEXT_CUSTOM_TYPE, GOAL_CONTEXT_PREVIEW_LABEL, GOAL_SKILL_NAME, GOAL_STATE_CUSTOM_TYPE, goalHostResponse, goalTokenDeltaForUsage, isPersistedGoalState, normalizeGoalState, validateGoalBudget, validateGoalObjective, } from "./goals.js";
|
|
29
29
|
import { captureNativeBranchRequests, captureNativeCompactionRequests, captureNativePlannerRequests, captureNativeReviewerRequests, InferenceCoordinator, takeNativeInferenceAuthSource, } from "./inference-coordinator.js";
|
|
30
|
+
import { JOB_WATCH_STATE, JobWatchController } from "./job-watch.js";
|
|
30
31
|
import { snapshotPathIn } from "./kernel/state-snapshot.js";
|
|
31
32
|
import { convertToLlm, createCompactionOutcomeMessage, createHeartbeatPromptMessage, createRefinementOutcomeMessage, createRlmChildFailureMessage, createRlmChildTerminalNoticeMessage, createSessionSlashCommandMessage, createSessionSlashCommandResultMessage, HARNESS_SNAPSHOT_CUSTOM_TYPE, HEARTBEAT_PROMPT_CUSTOM_TYPE, HEARTBEAT_PROMPT_PREVIEW_LABEL, IPYTHON_STATE_RESTORED_CUSTOM_TYPE, isSessionSlashCommandMessage, RLM_CHILD_FAILURE_CUSTOM_TYPE, RLM_CHILD_TERMINAL_NOTICE_CUSTOM_TYPE, } from "./messages.js";
|
|
32
33
|
import { throwIfPromptAdmissionCancelled } from "./prompt-admission.js";
|
|
@@ -35,6 +36,7 @@ import { appendGlobalRefinement, applyRefinementProposal, formatHarnessStateForP
|
|
|
35
36
|
import { getRecoveryCompactionAuthorization, PublicContextBudgetError } from "./request-view-selection.js";
|
|
36
37
|
import { resolveConfigValue } from "./resolve-config-value.js";
|
|
37
38
|
import { assertResourceCurrent } from "./resource-view.js";
|
|
39
|
+
import { retainToolOutput } from "./retained-tool-output.js";
|
|
38
40
|
import { createDefaultRlmSubagentSessionName, createRlmDeleteSubagentHostHandler, createRlmFindModelsHostHandler, createRlmListSubagentsHostHandler, createRlmRunHostHandler, findRlmModelMatches, normalizeRequestedRlmSubagentModel, normalizeRequestedRlmSubagentSessionName, normalizeRequestedRlmSubagentThinkingLevel, } from "./rlm-runtime.js";
|
|
39
41
|
import { captureSelectedSkill, captureSkillDescriptor, readSkillSelection, sameSelectedSkills, selectedSkillBlock, selectedSkillIdentity, } from "./selected-skills.js";
|
|
40
42
|
import { createNativeRecoveryRefusal, DEFAULT_NATIVE_RECOVERY_LIMITS, NativeRecoveryBudgetRefusal, NativeRecoveryCursorStore, parseNativeRecoveryInput, recoverCapturedHistory, } from "./selective-recovery.js";
|
|
@@ -328,6 +330,7 @@ export class AgentSession {
|
|
|
328
330
|
_goalStateRevision = 0;
|
|
329
331
|
_goalAccountingStartedAt = undefined;
|
|
330
332
|
_goalContinuationAwaitsRlmWork = false;
|
|
333
|
+
_jobWatchController;
|
|
331
334
|
_goalAccountedAssistantMessages = new WeakSet();
|
|
332
335
|
_goalAbortInProgress = false;
|
|
333
336
|
_autonomousState;
|
|
@@ -1110,6 +1113,10 @@ export class AgentSession {
|
|
|
1110
1113
|
activeToolNames: this._initialActiveToolNames,
|
|
1111
1114
|
includeAllExtensionTools: true,
|
|
1112
1115
|
});
|
|
1116
|
+
if (bootstrap.jobWatchState)
|
|
1117
|
+
await this._getJobWatchController().restore(bootstrap.jobWatchState);
|
|
1118
|
+
if (bootstrap.jobWatchUnavailable)
|
|
1119
|
+
this._surfaceSessionInputError(new Error(bootstrap.jobWatchUnavailable));
|
|
1113
1120
|
if (this._goalState.status === "active" &&
|
|
1114
1121
|
this._includeGoals &&
|
|
1115
1122
|
!this.getActiveToolNames().includes("ipython")) {
|
|
@@ -1625,8 +1632,28 @@ export class AgentSession {
|
|
|
1625
1632
|
!isPersistedRlmMaxDepthState(hydrated.entry.data))
|
|
1626
1633
|
throw new Error("Bootstrap RLM depth source is unavailable or ineligible");
|
|
1627
1634
|
rlmMaxDepth = hydrated.entry.data;
|
|
1635
|
+
remaining -= bootstrap.rlmMaxDepth.locator.length;
|
|
1636
|
+
}
|
|
1637
|
+
let jobWatchState;
|
|
1638
|
+
let jobWatchUnavailable = bootstrap.jobWatchUnavailable
|
|
1639
|
+
? `Monitoring unavailable: optional job-watch bootstrap candidate budget exceeded. Inspect ${this.sessionManager.getSessionFile()}; no watches were resumed.`
|
|
1640
|
+
: undefined;
|
|
1641
|
+
if (bootstrap.jobWatchState) {
|
|
1642
|
+
if (bootstrap.jobWatchState.locator.length >
|
|
1643
|
+
Math.min(remaining, 64 * 1024 + 1024, Math.floor(maxSourceBytes / 2) + 1024)) {
|
|
1644
|
+
jobWatchUnavailable = `Monitoring unavailable: optional job-watch state exceeds the existing source budget. Inspect ${this.sessionManager.getSessionFile()} entry ${bootstrap.jobWatchState.id}; no watches were resumed.`;
|
|
1645
|
+
}
|
|
1646
|
+
else {
|
|
1647
|
+
const hydrated = await view.hydrateEntry(bootstrap.jobWatchState.id, remaining);
|
|
1648
|
+
if (hydrated?.entry.type === "custom" &&
|
|
1649
|
+
hydrated.entry.customType === JOB_WATCH_STATE &&
|
|
1650
|
+
hydrated.source.retention !== "retained-import")
|
|
1651
|
+
jobWatchState = hydrated.entry.data;
|
|
1652
|
+
}
|
|
1628
1653
|
}
|
|
1629
1654
|
return {
|
|
1655
|
+
jobWatchState,
|
|
1656
|
+
jobWatchUnavailable,
|
|
1630
1657
|
goalState,
|
|
1631
1658
|
goalSeedable: bootstrap.goalSeedable,
|
|
1632
1659
|
rlmMaxDepth,
|
|
@@ -1675,6 +1702,8 @@ export class AgentSession {
|
|
|
1675
1702
|
return true;
|
|
1676
1703
|
}
|
|
1677
1704
|
async _reloadBranchRuntimeState() {
|
|
1705
|
+
this._jobWatchController?.dispose();
|
|
1706
|
+
this._jobWatchController = undefined;
|
|
1678
1707
|
this._contextMode = await this._readContextMode();
|
|
1679
1708
|
const bootstrap = this.sessionManager.isPersisted()
|
|
1680
1709
|
? await this._readRuntimeBootstrap()
|
|
@@ -2123,7 +2152,10 @@ export class AgentSession {
|
|
|
2123
2152
|
async _resumeGoalContinuationAfterRlmWork() {
|
|
2124
2153
|
if (!this._goalContinuationAwaitsRlmWork)
|
|
2125
2154
|
return;
|
|
2126
|
-
if (this._disposed ||
|
|
2155
|
+
if (this._disposed ||
|
|
2156
|
+
this._disposing ||
|
|
2157
|
+
this._hasUnsettledRlmQuiescenceWork() ||
|
|
2158
|
+
this._jobWatchController?.isParked())
|
|
2127
2159
|
return;
|
|
2128
2160
|
if (this._goalState.status !== "active" || !this._goalState.objective) {
|
|
2129
2161
|
this._goalContinuationAwaitsRlmWork = false;
|
|
@@ -3002,7 +3034,9 @@ export class AgentSession {
|
|
|
3002
3034
|
: undefined;
|
|
3003
3035
|
}
|
|
3004
3036
|
async _queueAutonomousContinuationForThresholdCompaction(message, owner = this._captureCompactionOwner(this.agent.signal), capture = this._captureThresholdAutonomousOwner()) {
|
|
3005
|
-
if (!this._isCompactionOwnerCurrent(owner) ||
|
|
3037
|
+
if (!this._isCompactionOwnerCurrent(owner) ||
|
|
3038
|
+
!this._contextOptimizationAllowed() ||
|
|
3039
|
+
this._jobWatchController?.isParked())
|
|
3006
3040
|
return undefined;
|
|
3007
3041
|
const queued = this._queuedAutonomousThresholdContinuations.get(message);
|
|
3008
3042
|
if (queued && this._checkpointActionPending(queued))
|
|
@@ -3059,7 +3093,7 @@ export class AgentSession {
|
|
|
3059
3093
|
return undefined;
|
|
3060
3094
|
if (message.stopReason === "error" || message.stopReason === "aborted")
|
|
3061
3095
|
return undefined;
|
|
3062
|
-
if (owner.goal.status !== "active" || !owner.goal.objective)
|
|
3096
|
+
if (owner.goal.status !== "active" || !owner.goal.objective || this._jobWatchController?.isParked())
|
|
3063
3097
|
return undefined;
|
|
3064
3098
|
const queued = this._queuedGoalThresholdContinuation;
|
|
3065
3099
|
if (queued && this._checkpointActionPending(queued))
|
|
@@ -3217,14 +3251,14 @@ export class AgentSession {
|
|
|
3217
3251
|
this._assertCompactionOwner(owner);
|
|
3218
3252
|
const compaction = owner.manager.bindCompactionSink();
|
|
3219
3253
|
const captured = owner.requests.capture(compaction);
|
|
3220
|
-
let branch;
|
|
3221
3254
|
let preparation;
|
|
3255
|
+
let leafKind;
|
|
3222
3256
|
try {
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3257
|
+
if (this.isStreaming) {
|
|
3258
|
+
const prepared = await this._prepareCapturedCompaction(settings, captured, compaction, owner);
|
|
3259
|
+
preparation = prepared.preparation;
|
|
3260
|
+
leafKind = prepared.leafKind;
|
|
3261
|
+
}
|
|
3228
3262
|
}
|
|
3229
3263
|
catch (error) {
|
|
3230
3264
|
try {
|
|
@@ -3245,10 +3279,9 @@ export class AgentSession {
|
|
|
3245
3279
|
reason: "no active turn; compaction can only be requested while a turn is running",
|
|
3246
3280
|
};
|
|
3247
3281
|
if (!preparation) {
|
|
3248
|
-
const lastEntry = branch.at(-1);
|
|
3249
3282
|
return {
|
|
3250
3283
|
scheduled: false,
|
|
3251
|
-
reason:
|
|
3284
|
+
reason: leafKind === "compaction" ? "already compacted" : "session is too short to compact",
|
|
3252
3285
|
};
|
|
3253
3286
|
}
|
|
3254
3287
|
this._assertContextOptimizationAllowed();
|
|
@@ -3549,6 +3582,8 @@ export class AgentSession {
|
|
|
3549
3582
|
if (this._goalState.status !== "active" || !this._goalState.objective) {
|
|
3550
3583
|
return { kind: "finish" };
|
|
3551
3584
|
}
|
|
3585
|
+
if (this._jobWatchController?.isParked())
|
|
3586
|
+
return { kind: "wait_for_owned_work" };
|
|
3552
3587
|
// Delegating and ending the turn is correct behavior; retain the existing wakeup owner.
|
|
3553
3588
|
if (this._hasUnsettledRlmQuiescenceWork()) {
|
|
3554
3589
|
this._goalContinuationAwaitsRlmWork = true;
|
|
@@ -3591,6 +3626,8 @@ export class AgentSession {
|
|
|
3591
3626
|
return this._stoppedContinuationOutcome(signal);
|
|
3592
3627
|
if (this.queuedActionCount > 0)
|
|
3593
3628
|
return { kind: "finish" };
|
|
3629
|
+
if (this._jobWatchController?.isParked())
|
|
3630
|
+
return { kind: "wait_for_owned_work" };
|
|
3594
3631
|
const owner = this._captureGoalContinuationOwner(signal);
|
|
3595
3632
|
const arrivalEpoch = this._sessionInputArrivalEpoch;
|
|
3596
3633
|
const goalSnapshot = owner.goal;
|
|
@@ -4413,6 +4450,13 @@ export class AgentSession {
|
|
|
4413
4450
|
await drain(() => this._agentEventQueue);
|
|
4414
4451
|
await drain(() => this._goalResumeOperation);
|
|
4415
4452
|
await drain(() => this._waitForChildUsageWrites());
|
|
4453
|
+
if (this._jobWatchController) {
|
|
4454
|
+
const undelivered = new Set(this._actionStore
|
|
4455
|
+
.unfinishedActions()
|
|
4456
|
+
.filter((action) => action.queueKey && action.payload.kind === "turn" && !primaryDeliveryRecord(action).durable)
|
|
4457
|
+
.map((action) => action.queueKey));
|
|
4458
|
+
await drain(() => this._jobWatchController?.settleShutdown(undelivered));
|
|
4459
|
+
}
|
|
4416
4460
|
await drain(() => this.dispose());
|
|
4417
4461
|
}
|
|
4418
4462
|
catch (error) {
|
|
@@ -4461,6 +4505,7 @@ export class AgentSession {
|
|
|
4461
4505
|
}
|
|
4462
4506
|
this.requests.stopAdmission();
|
|
4463
4507
|
this._disposed = true;
|
|
4508
|
+
this._jobWatchController?.dispose();
|
|
4464
4509
|
for (const run of this._unsettledRlmChildRuns)
|
|
4465
4510
|
run.suppressTerminalNotice = true;
|
|
4466
4511
|
for (const controller of this._rlmQuiescenceWaitAborts)
|
|
@@ -4813,10 +4858,10 @@ export class AgentSession {
|
|
|
4813
4858
|
/** Persist mutable advice before the request owner captures its source and epoch ACK boundary. */
|
|
4814
4859
|
async _appendHarnessSnapshotIfChanged() {
|
|
4815
4860
|
const manager = this.sessionManager;
|
|
4816
|
-
|
|
4817
|
-
|
|
4861
|
+
const sourceIsCurrent = manager.captureCompactionContentOwner();
|
|
4862
|
+
let ownAppends = 0;
|
|
4818
4863
|
const assertCurrent = () => {
|
|
4819
|
-
if (this._disposed || this.sessionManager !== manager || !sourceIsCurrent()
|
|
4864
|
+
if (this._disposed || this.sessionManager !== manager || !sourceIsCurrent(ownAppends))
|
|
4820
4865
|
throw new Error("Harness snapshot source changed before request ownership");
|
|
4821
4866
|
};
|
|
4822
4867
|
let previous;
|
|
@@ -4845,8 +4890,9 @@ export class AgentSession {
|
|
|
4845
4890
|
});
|
|
4846
4891
|
if (previous === content)
|
|
4847
4892
|
return assertCurrent;
|
|
4848
|
-
|
|
4849
|
-
|
|
4893
|
+
await manager.appendCustomMessageEntryWithRollback(HARNESS_SNAPSHOT_CUSTOM_TYPE, content, false);
|
|
4894
|
+
ownAppends++;
|
|
4895
|
+
if (this._disposed || this.sessionManager !== manager || !sourceIsCurrent(ownAppends))
|
|
4850
4896
|
throw new Error("Harness snapshot source changed during append");
|
|
4851
4897
|
if (!manager.isPersisted())
|
|
4852
4898
|
this.agent.state.messages.push({
|
|
@@ -4856,7 +4902,6 @@ export class AgentSession {
|
|
|
4856
4902
|
display: false,
|
|
4857
4903
|
timestamp: Date.now(),
|
|
4858
4904
|
});
|
|
4859
|
-
leafId = entryId;
|
|
4860
4905
|
return assertCurrent;
|
|
4861
4906
|
}
|
|
4862
4907
|
_refreshExtensionSystemPrompt(extensionPrompt, baseSnapshot) {
|
|
@@ -6492,10 +6537,13 @@ export class AgentSession {
|
|
|
6492
6537
|
actionId: action.id,
|
|
6493
6538
|
submittedText: input.submitted?.text,
|
|
6494
6539
|
};
|
|
6540
|
+
const clearedGoal = Boolean(this._goalState.objective) && this._parseGoalSlashCommand(input.text)?.kind === "clear";
|
|
6495
6541
|
await this._handleGoalSlashCommand(input.text, input.images, goalOrigin);
|
|
6496
6542
|
resultText = this._goalState.objective
|
|
6497
6543
|
? `Goal ${this._goalState.status}: ${this._goalState.objective}`
|
|
6498
|
-
:
|
|
6544
|
+
: clearedGoal
|
|
6545
|
+
? "Goal cleared."
|
|
6546
|
+
: "No active goal.";
|
|
6499
6547
|
break;
|
|
6500
6548
|
}
|
|
6501
6549
|
case "autonomous":
|
|
@@ -7063,6 +7111,7 @@ export class AgentSession {
|
|
|
7063
7111
|
this._advanceCheckpointPauseEpoch();
|
|
7064
7112
|
this._notifySessionInputCheckpointChange();
|
|
7065
7113
|
this._flushDeferredRlmTerminalNotices();
|
|
7114
|
+
void this._jobWatchController?.flushPending().catch((error) => this._surfaceSessionInputError(error));
|
|
7066
7115
|
this._scheduleGoalContinuationAfterRlmWork();
|
|
7067
7116
|
this._scheduleSessionInputPump();
|
|
7068
7117
|
},
|
|
@@ -7179,6 +7228,7 @@ export class AgentSession {
|
|
|
7179
7228
|
this._sessionInputPumpEpoch++;
|
|
7180
7229
|
this._notifySessionInputCheckpointChange();
|
|
7181
7230
|
this._flushDeferredRlmTerminalNotices();
|
|
7231
|
+
void this._jobWatchController?.flushPending().catch((error) => this._surfaceSessionInputError(error));
|
|
7182
7232
|
}
|
|
7183
7233
|
/** Resume the scheduler after requestAbort/abortForUpdateRestart suspended it; owned pause leases are unaffected. */
|
|
7184
7234
|
resumeQueuedWork() {
|
|
@@ -7786,14 +7836,12 @@ export class AgentSession {
|
|
|
7786
7836
|
const semanticEdges = owner.semanticEdges;
|
|
7787
7837
|
compaction = owner.manager.bindCompactionSink();
|
|
7788
7838
|
requests = owner.requests.capture(compaction);
|
|
7789
|
-
const pathEntries = await compaction.readBranch();
|
|
7790
7839
|
this._assertCompactionOwner(owner);
|
|
7791
7840
|
const { apiKey, headers } = await this._getRequiredRequestAuth(model);
|
|
7792
7841
|
this._assertCompactionOwner(owner);
|
|
7793
7842
|
committed = await this._performCompaction({
|
|
7794
7843
|
model,
|
|
7795
7844
|
thinkingLevel,
|
|
7796
|
-
pathEntries,
|
|
7797
7845
|
settings,
|
|
7798
7846
|
requests,
|
|
7799
7847
|
compaction,
|
|
@@ -7887,33 +7935,47 @@ export class AgentSession {
|
|
|
7887
7935
|
}
|
|
7888
7936
|
}
|
|
7889
7937
|
/** A private compiled context keeps extension summary edits separate from retained source recipes. */
|
|
7890
|
-
async _prepareCapturedCompaction(
|
|
7938
|
+
async _prepareCapturedCompaction(settings, requests, compaction, owner = this._captureCompactionOwner(), allowShortSession = false, budgetPressure = false, capacity) {
|
|
7891
7939
|
this._assertCompactionOwner(owner);
|
|
7892
7940
|
const limits = this.settingsManager.getCanonicalContextLimits();
|
|
7893
7941
|
const resource = this._captureKernelResource();
|
|
7894
7942
|
const source = await compaction.source;
|
|
7895
7943
|
this._assertCompactionOwner(owner);
|
|
7896
|
-
if (!source.persistent)
|
|
7944
|
+
if (!source.persistent) {
|
|
7945
|
+
const pathEntries = await compaction.readBranch();
|
|
7946
|
+
this._assertCompactionOwner(owner);
|
|
7897
7947
|
return {
|
|
7898
7948
|
preparation: prepareCompaction(pathEntries, settings, allowShortSession),
|
|
7949
|
+
pathEntries,
|
|
7950
|
+
leafKind: pathEntries.at(-1)?.type,
|
|
7899
7951
|
maxSourceBytes: limits.maxSourceBytes,
|
|
7900
7952
|
};
|
|
7953
|
+
}
|
|
7901
7954
|
return requests.readHistory(async (view) => {
|
|
7902
7955
|
const messages = await new CanonicalContextCompiler().compile(view, limits, undefined, {}, resource, this._initialContextMode, false, "read");
|
|
7903
7956
|
this._assertCompactionOwner(owner);
|
|
7904
7957
|
const context = getCanonicalEpochContext(messages);
|
|
7905
|
-
|
|
7906
|
-
|
|
7958
|
+
const entryIds = context.references.map((ref) => ref?.ref.entryId);
|
|
7959
|
+
const selected = await compaction.readCompactionEntries(entryIds.filter((id) => id !== undefined));
|
|
7960
|
+
this._assertCompactionOwner(owner);
|
|
7961
|
+
const pathEntries = selected.entries;
|
|
7962
|
+
if (context.checkpoint?.includeSummary && selected.leafKind === "compaction")
|
|
7963
|
+
return {
|
|
7964
|
+
preparation: undefined,
|
|
7965
|
+
pathEntries,
|
|
7966
|
+
leafKind: selected.leafKind,
|
|
7967
|
+
maxSourceBytes: limits.maxSourceBytes,
|
|
7968
|
+
};
|
|
7907
7969
|
const authorization = getRecoveryCompactionAuthorization(capacity);
|
|
7908
7970
|
const boundary = canonicalRecoveryBoundary(messages, authorization);
|
|
7909
|
-
const preparation =
|
|
7910
|
-
? prepareViewCompaction(messages, context.references.map((ref) => ref?.ref.entryId), pathEntries, settings, boundary, allowShortSession, budgetPressure)
|
|
7911
|
-
: prepareCompaction(pathEntries, settings, allowShortSession);
|
|
7971
|
+
const preparation = prepareViewCompaction(messages, entryIds, pathEntries, settings, boundary, allowShortSession, budgetPressure, selected.suffixAnchors);
|
|
7912
7972
|
// Refuse unsupported public data or open groups before starting the summary model call.
|
|
7913
7973
|
if (preparation)
|
|
7914
7974
|
prepareRecoveryCompaction(messages, preparation.firstKeptEntryId, limits.maxSourceBytes, authorization);
|
|
7915
7975
|
return {
|
|
7916
7976
|
preparation,
|
|
7977
|
+
pathEntries,
|
|
7978
|
+
leafKind: selected.leafKind,
|
|
7917
7979
|
messages,
|
|
7918
7980
|
resource: context.resourceRevision !== undefined ? resource : undefined,
|
|
7919
7981
|
maxSourceBytes: limits.maxSourceBytes,
|
|
@@ -7926,15 +7988,15 @@ export class AgentSession {
|
|
|
7926
7988
|
* Error("Compaction cancelled") on abort or extension cancel.
|
|
7927
7989
|
*/
|
|
7928
7990
|
async _performCompaction(options) {
|
|
7929
|
-
const { model, apiKey, headers, customInstructions, signal, thinkingLevel, requests, compaction, semanticEdges,
|
|
7930
|
-
const prepared = await this._prepareCapturedCompaction(
|
|
7991
|
+
const { model, apiKey, headers, customInstructions, signal, thinkingLevel, requests, compaction, semanticEdges, settings, owner = this._captureCompactionOwner(), allowShortSession = false, budgetPressure = false, } = options;
|
|
7992
|
+
const prepared = await this._prepareCapturedCompaction(settings, requests, compaction, owner, allowShortSession, budgetPressure, options.capacity);
|
|
7931
7993
|
this._assertCompactionOwner(owner);
|
|
7932
7994
|
if (prepared.resource)
|
|
7933
7995
|
assertResourceCurrent(prepared.resource);
|
|
7934
7996
|
const preparation = prepared.preparation;
|
|
7997
|
+
const pathEntries = prepared.pathEntries;
|
|
7935
7998
|
if (!preparation) {
|
|
7936
|
-
|
|
7937
|
-
if (lastEntry?.type === "compaction") {
|
|
7999
|
+
if (prepared.leafKind === "compaction") {
|
|
7938
8000
|
throw new CompactionSkippedError("Already compacted");
|
|
7939
8001
|
}
|
|
7940
8002
|
throw new CompactionSkippedError("Session is too short to compact — try again once it grows");
|
|
@@ -9366,7 +9428,6 @@ export class AgentSession {
|
|
|
9366
9428
|
const semanticEdges = owner.semanticEdges;
|
|
9367
9429
|
compaction = owner.manager.bindCompactionSink();
|
|
9368
9430
|
requests = owner.requests.capture(compaction);
|
|
9369
|
-
const pathEntries = await compaction.readBranch();
|
|
9370
9431
|
this._assertCompactionOwner(owner);
|
|
9371
9432
|
const authResult = model ? await this._modelRegistry.getApiKeyAndHeaders(model) : undefined;
|
|
9372
9433
|
this._assertCompactionOwner(owner);
|
|
@@ -9385,7 +9446,6 @@ export class AgentSession {
|
|
|
9385
9446
|
committed = await this._performCompaction({
|
|
9386
9447
|
model,
|
|
9387
9448
|
thinkingLevel,
|
|
9388
|
-
pathEntries,
|
|
9389
9449
|
settings,
|
|
9390
9450
|
requests,
|
|
9391
9451
|
compaction,
|
|
@@ -10015,6 +10075,75 @@ export class AgentSession {
|
|
|
10015
10075
|
return createNativeRecoveryRefusal("unavailable", "captured_history_unavailable", responseBytes);
|
|
10016
10076
|
}
|
|
10017
10077
|
}
|
|
10078
|
+
_getJobWatchController() {
|
|
10079
|
+
if (this._jobWatchController?.isCurrent())
|
|
10080
|
+
return this._jobWatchController;
|
|
10081
|
+
this._jobWatchController?.dispose();
|
|
10082
|
+
const manager = this.sessionManager;
|
|
10083
|
+
const ownsSource = manager.captureCompactionSourceOwner();
|
|
10084
|
+
const controller = new JobWatchController({
|
|
10085
|
+
sessionId: manager.getSessionId(),
|
|
10086
|
+
sessionFile: manager.getSessionFile(),
|
|
10087
|
+
current: () => !this._disposed && !this._disposing && this.sessionManager === manager && ownsSource(),
|
|
10088
|
+
goalId: () => this._goalState.goalId,
|
|
10089
|
+
persist: async (state) => {
|
|
10090
|
+
if (this.sessionManager !== manager || !ownsSource())
|
|
10091
|
+
return;
|
|
10092
|
+
await manager.appendCustomEntry(JOB_WATCH_STATE, state);
|
|
10093
|
+
},
|
|
10094
|
+
waitForDelivery: true,
|
|
10095
|
+
stateBudgetBytes: Math.min(64 * 1024, Math.floor(this.settingsManager.getCanonicalContextLimits().maxSourceBytes / 2)),
|
|
10096
|
+
probe: async (request) => {
|
|
10097
|
+
const kernel = await this._ipythonKernelProvisioner?.ensure();
|
|
10098
|
+
if (!kernel?.jobWatchProbe)
|
|
10099
|
+
throw new Error("Kernel job_watch_probe_v1 is unavailable");
|
|
10100
|
+
const result = await kernel.jobWatchProbe({
|
|
10101
|
+
watchId: request.id,
|
|
10102
|
+
generation: request.generation,
|
|
10103
|
+
resourceId: request.resource_id,
|
|
10104
|
+
jobId: request.job_id,
|
|
10105
|
+
completionSource: request.completion_source,
|
|
10106
|
+
command: request.command ?? null,
|
|
10107
|
+
timeoutMs: request.timeout_ms,
|
|
10108
|
+
});
|
|
10109
|
+
await controller.observe({ id: request.id, generation: request.generation, ...result });
|
|
10110
|
+
},
|
|
10111
|
+
cancelProbe: (id) => this._ipythonKernelProvisioner?.manager?.cancelJobWatchProbe?.(id),
|
|
10112
|
+
retain: async (output) => {
|
|
10113
|
+
const directory = manager.getSessionArtifactDir();
|
|
10114
|
+
if (!directory)
|
|
10115
|
+
return undefined;
|
|
10116
|
+
const retained = await retainToolOutput(directory, [output], false);
|
|
10117
|
+
return join(directory, retained.artifactId);
|
|
10118
|
+
},
|
|
10119
|
+
onError: (error) => this._surfaceSessionInputError(error),
|
|
10120
|
+
admit: (text, eventId) => {
|
|
10121
|
+
if (this._sessionInputAdmissionPauses.size)
|
|
10122
|
+
return false;
|
|
10123
|
+
const content = `[job-watch] ${text}`;
|
|
10124
|
+
const admission = this._admitSessionInput(this._createPreparedTurnAction("followUp", content, undefined, {
|
|
10125
|
+
message: {
|
|
10126
|
+
role: "custom",
|
|
10127
|
+
customType: "job_watch_event",
|
|
10128
|
+
content,
|
|
10129
|
+
display: true,
|
|
10130
|
+
timestamp: Date.now(),
|
|
10131
|
+
},
|
|
10132
|
+
queueKey: eventId,
|
|
10133
|
+
resumeIfIdle: !this._sessionInputPumpSuspended,
|
|
10134
|
+
}), { wake: !this._sessionInputPumpSuspended });
|
|
10135
|
+
if (admission.ticket)
|
|
10136
|
+
void admission.ticket.delivered
|
|
10137
|
+
.then((outcome) => outcome.status === "delivered"
|
|
10138
|
+
? controller.acknowledge(eventId)
|
|
10139
|
+
: controller.releaseDelivery(eventId, true), () => controller.releaseDelivery(eventId, false))
|
|
10140
|
+
.catch((error) => this._surfaceSessionInputError(error));
|
|
10141
|
+
return admission.accepted;
|
|
10142
|
+
},
|
|
10143
|
+
});
|
|
10144
|
+
this._jobWatchController = controller;
|
|
10145
|
+
return controller;
|
|
10146
|
+
}
|
|
10018
10147
|
_createKernelHostHandlers() {
|
|
10019
10148
|
const handlers = {
|
|
10020
10149
|
prime_context: async (payload, context) => {
|
|
@@ -10061,6 +10190,35 @@ export class AgentSession {
|
|
|
10061
10190
|
handlers[type] = async (payload) => this.handleRlmHeartbeatHostRequest(type, payload);
|
|
10062
10191
|
}
|
|
10063
10192
|
}
|
|
10193
|
+
for (const type of ["watch", "status", "park", "unregister", "observation"]) {
|
|
10194
|
+
handlers[`job_watch.${type}`] = async (payload) => {
|
|
10195
|
+
if (this._disposed || this._disposing)
|
|
10196
|
+
throw new Error("Job watch source owner changed");
|
|
10197
|
+
if (type === "observation" && !this._jobWatchController?.isCurrent())
|
|
10198
|
+
return { ignored: true };
|
|
10199
|
+
const controller = this._getJobWatchController();
|
|
10200
|
+
switch (type) {
|
|
10201
|
+
case "watch":
|
|
10202
|
+
return { ...(await controller.watch(payload)) };
|
|
10203
|
+
case "status":
|
|
10204
|
+
return controller.status(typeof payload.id === "string" ? payload.id : undefined);
|
|
10205
|
+
case "park":
|
|
10206
|
+
if (!Array.isArray(payload.ids) || !payload.ids.every((id) => typeof id === "string"))
|
|
10207
|
+
throw new Error("park requires watch ids");
|
|
10208
|
+
return controller.park(payload.ids);
|
|
10209
|
+
case "unregister":
|
|
10210
|
+
return controller.unregister(String(payload.id));
|
|
10211
|
+
default:
|
|
10212
|
+
if (typeof payload.id !== "string" ||
|
|
10213
|
+
typeof payload.generation !== "string" ||
|
|
10214
|
+
!payload.result ||
|
|
10215
|
+
typeof payload.result !== "object")
|
|
10216
|
+
throw new Error("Invalid job watch observation");
|
|
10217
|
+
await controller.observe({ ...payload.result, id: payload.id, generation: payload.generation });
|
|
10218
|
+
return {};
|
|
10219
|
+
}
|
|
10220
|
+
};
|
|
10221
|
+
}
|
|
10064
10222
|
const visibleKernelSkillNames = new Set(this._modelVisibleSkills()
|
|
10065
10223
|
.filter((skill) => !skill.disableModelInvocation)
|
|
10066
10224
|
.map((skill) => skill.name));
|