@rivus/agent 0.6.2 → 0.8.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/README.md +12 -0
- package/dist/acp.d.ts +11 -0
- package/dist/acp.js +14 -1
- package/dist/agent-loop.d.ts +10 -1
- package/dist/background-session-authority.js +224 -0
- package/dist/background-session-input.js +45 -0
- package/dist/background-session-service.d.ts +291 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +191 -2
- package/dist/index.js +1522 -65
- package/dist/mcp.d.ts +86 -0
- package/dist/mcp.js +416 -0
- package/dist/pi-tool-proxy.d.ts +3 -0
- package/dist/pi.js +3 -0
- package/dist/rivus-daemon-cli.js +133 -6
- package/dist/rivus-plugin-registry.js +5 -2
- package/dist/rivus-plugin.d.ts +8 -0
- package/dist/tool-input-digest.js +2 -0
- package/examples/pi-feishu-deployment.bootstrap.ts +187 -4
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { a as createAgentLoopSkillExecutionStart, c as createAgentLoopToolExecutionEnd, d as createAsyncIterableAgentLoop, f as createEventAgentLoop, i as createAgentLoopSkillExecutionEnd, l as createAgentLoopToolExecutionStart, m as createTextAgentLoopFromCallback, n as createAgentLoopModelExecutionEnd, o as createAgentLoopTextDelta, p as createTextAgentLoop, r as createAgentLoopModelExecutionStart, s as createAgentLoopThinkingDelta, t as createAgentLoopFromCallback, u as createAgentLoopToolExecutionUpdate } from "./agent-loop.js";
|
|
2
2
|
import { a as createMemoryNamespace, c as InvalidRivusPlugin, d as requiresToolApproval, i as RIVUS_MEMORY_TOOL_VERSION, l as RIVUS_PLUGIN_API_VERSION, n as RIVUS_MEMORY_TOOL_ID, o as createRivusMemoryToolContract, r as RIVUS_MEMORY_TOOL_PLUGIN_ID, s as restrictMemoryScopesForAudience, t as MEMORY_SCOPES, u as RivusToolInputRejected } from "./agent-memory.js";
|
|
3
|
+
import { a as BACKGROUND_SESSION_TOOL_VERSION, c as createBackgroundSessionStepSourceMessageId, d as isBackgroundSessionToolId, f as narrowBackgroundSessionDefinition, i as BACKGROUND_SESSION_TOOL_PLUGIN_ID, l as createBackgroundSessionToolContracts, n as BACKGROUND_SESSION_START_TOOL_ID, o as backgroundSessionToolIds, r as BACKGROUND_SESSION_TOOL_IDS, s as createBackgroundSessionKey, t as BACKGROUND_SESSION_SESSION_KEY_PREFIX, u as extendBackgroundSessionDefinition } from "./background-session-authority.js";
|
|
3
4
|
import { n as resolveRivusAgentDefinition, t as createRivusPluginCatalog } from "./rivus-plugin-registry.js";
|
|
4
|
-
import { A as
|
|
5
|
+
import { $ as loadRivusDeployment, A as DEFAULT_BACKGROUND_SESSION_LIFETIME_MS, B as acceptsCardPresentationProgress, C as loadRivusDaemonConfig, D as loadRivusDeploymentManifest, E as RivusDeploymentManifestError, F as DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS, G as createCardPresentationChain, H as beginCardPresentationHandoff, I as resolveBackgroundSessionSupervisorIntervalMs, J as markCardPresentationTerminal, K as failCardPresentationHandoff, L as DEFAULT_CARD_STREAM_LEASE_MS, M as DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, N as DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS, O as DEFAULT_BACKGROUND_SESSION_LEASE_MS, P as DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS, Q as RivusPluginLoadError, R as createFeishuCardRollover, S as RivusDaemonConfigError, U as compensateCardPresentationHandoff, V as activeCardPresentation, W as completeCardPresentationHandoff, X as resolveFeishuEndpointCredentials, Y as FeishuEndpointCredentialError, _ as createStableId, a as InvalidRivusProjectSpace, b as createRivusEnvFromOpenClawConfig, c as RivusDeploymentReadinessError, d as createRivusAgentHost, et as validateRivusDeploymentManifest, f as AgentInstanceBusy, g as createAgentInstanceRegistry, h as AgentInstanceConflict, i as resolveRivusProjectSpace, j as DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, k as DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, l as createRivusDeploymentDaemon, m as createAgentRuntimePool, n as createRivusDeploymentCliProcess, o as RivusDeploymentAutomationReadinessError, p as AgentRuntimeDisposed, q as isCardPresentationHandoffDue, r as createConfiguredRivusDeploymentDaemon, s as RivusDeploymentDaemonLifecycleError, t as runRivusDaemonCli, u as InvalidRivusEndpointBinding, v as createRivusDaemonShutdownController, w as loadNodeRivusPluginModule, x as formatRivusEnvFile, y as OpenClawEnvImportError, z as CardPresentationTransitionDenied } from "./rivus-daemon-cli.js";
|
|
5
6
|
import { a as InvalidInvocationAuthority, i as normalizeStableJson, n as InvalidToolInput, o as createInvocationAuthority, r as createToolInputDigest, s as resolveInvocationAuthority, t as InvalidStableJson } from "./tool-input-digest.js";
|
|
7
|
+
import { a as readBackgroundSessionWaitInput, i as readBackgroundSessionString, n as readBackgroundSessionObject, r as readBackgroundSessionPhase, t as readBackgroundSessionInteger } from "./background-session-input.js";
|
|
6
8
|
import { n as assertRivusPluginConforms, r as createFakeRivusPlugin, t as RivusPluginConformanceError } from "./rivus-plugin-testkit.js";
|
|
7
9
|
import { Cause, Deferred, Effect, Either, Exit, Fiber, Option, Stream } from "effect";
|
|
8
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
9
11
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
10
|
-
import { appendFile, lstat, mkdir, readFile, readdir, realpath, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
12
|
+
import { appendFile, lstat, mkdir, readFile, readdir, realpath, rename, stat, truncate, unlink, writeFile } from "node:fs/promises";
|
|
11
13
|
import { isDeepStrictEqual } from "node:util";
|
|
12
14
|
import { createServer } from "node:http";
|
|
13
15
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
@@ -3443,36 +3445,36 @@ function parseAgentDomainEvent(value, lineNumber) {
|
|
|
3443
3445
|
switch (value.type) {
|
|
3444
3446
|
case "agent_run_accepted": return {
|
|
3445
3447
|
occurredAt,
|
|
3446
|
-
prompt: readString(value, "prompt", lineNumber),
|
|
3447
|
-
runId: readString(value, "runId", lineNumber),
|
|
3448
|
-
sessionKey: readString(value, "sessionKey", lineNumber),
|
|
3448
|
+
prompt: readString$1(value, "prompt", lineNumber),
|
|
3449
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3450
|
+
sessionKey: readString$1(value, "sessionKey", lineNumber),
|
|
3449
3451
|
type: value.type
|
|
3450
3452
|
};
|
|
3451
3453
|
case "agent_turn_started": return {
|
|
3452
3454
|
occurredAt,
|
|
3453
|
-
runId: readString(value, "runId", lineNumber),
|
|
3454
|
-
sessionKey: readString(value, "sessionKey", lineNumber),
|
|
3455
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3456
|
+
sessionKey: readString$1(value, "sessionKey", lineNumber),
|
|
3455
3457
|
type: value.type
|
|
3456
3458
|
};
|
|
3457
3459
|
case "assistant_text_delta": return {
|
|
3458
|
-
delta: readString(value, "delta", lineNumber),
|
|
3460
|
+
delta: readString$1(value, "delta", lineNumber),
|
|
3459
3461
|
occurredAt,
|
|
3460
|
-
runId: readString(value, "runId", lineNumber),
|
|
3462
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3461
3463
|
type: value.type
|
|
3462
3464
|
};
|
|
3463
3465
|
case "assistant_thinking_delta": return {
|
|
3464
|
-
delta: readString(value, "delta", lineNumber),
|
|
3466
|
+
delta: readString$1(value, "delta", lineNumber),
|
|
3465
3467
|
occurredAt,
|
|
3466
|
-
runId: readString(value, "runId", lineNumber),
|
|
3468
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3467
3469
|
type: value.type
|
|
3468
3470
|
};
|
|
3469
3471
|
case "agent_model_execution_started": return {
|
|
3470
|
-
api: readString(value, "api", lineNumber),
|
|
3471
|
-
model: readString(value, "model", lineNumber),
|
|
3472
|
-
modelCallId: readString(value, "modelCallId", lineNumber),
|
|
3472
|
+
api: readString$1(value, "api", lineNumber),
|
|
3473
|
+
model: readString$1(value, "model", lineNumber),
|
|
3474
|
+
modelCallId: readString$1(value, "modelCallId", lineNumber),
|
|
3473
3475
|
occurredAt,
|
|
3474
|
-
provider: readString(value, "provider", lineNumber),
|
|
3475
|
-
runId: readString(value, "runId", lineNumber),
|
|
3476
|
+
provider: readString$1(value, "provider", lineNumber),
|
|
3477
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3476
3478
|
type: value.type
|
|
3477
3479
|
};
|
|
3478
3480
|
case "agent_model_execution_ended": {
|
|
@@ -3481,15 +3483,15 @@ function parseAgentDomainEvent(value, lineNumber) {
|
|
|
3481
3483
|
const responseModel = readOptionalString(value, "responseModel", lineNumber);
|
|
3482
3484
|
const reasoning = readOptionalNumber(usage, "reasoning", lineNumber);
|
|
3483
3485
|
return {
|
|
3484
|
-
api: readString(value, "api", lineNumber),
|
|
3486
|
+
api: readString$1(value, "api", lineNumber),
|
|
3485
3487
|
...errorMessage ? { errorMessage } : {},
|
|
3486
|
-
model: readString(value, "model", lineNumber),
|
|
3487
|
-
modelCallId: readString(value, "modelCallId", lineNumber),
|
|
3488
|
+
model: readString$1(value, "model", lineNumber),
|
|
3489
|
+
modelCallId: readString$1(value, "modelCallId", lineNumber),
|
|
3488
3490
|
occurredAt,
|
|
3489
|
-
provider: readString(value, "provider", lineNumber),
|
|
3491
|
+
provider: readString$1(value, "provider", lineNumber),
|
|
3490
3492
|
...responseModel ? { responseModel } : {},
|
|
3491
|
-
runId: readString(value, "runId", lineNumber),
|
|
3492
|
-
stopReason: readString(value, "stopReason", lineNumber),
|
|
3493
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3494
|
+
stopReason: readString$1(value, "stopReason", lineNumber),
|
|
3493
3495
|
type: value.type,
|
|
3494
3496
|
usage: {
|
|
3495
3497
|
cacheRead: readNumber(usage, "cacheRead", lineNumber),
|
|
@@ -3519,10 +3521,10 @@ function parseAgentDomainEvent(value, lineNumber) {
|
|
|
3519
3521
|
} : {
|
|
3520
3522
|
...common,
|
|
3521
3523
|
contentLength: readNumber(value, "contentLength", lineNumber),
|
|
3522
|
-
digest: readString(value, "digest", lineNumber),
|
|
3524
|
+
digest: readString$1(value, "digest", lineNumber),
|
|
3523
3525
|
isError: false,
|
|
3524
|
-
title: readString(value, "title", lineNumber),
|
|
3525
|
-
version: readString(value, "version", lineNumber)
|
|
3526
|
+
title: readString$1(value, "title", lineNumber),
|
|
3527
|
+
version: readString$1(value, "version", lineNumber)
|
|
3526
3528
|
};
|
|
3527
3529
|
}
|
|
3528
3530
|
case "agent_tool_execution_started": return {
|
|
@@ -3546,21 +3548,21 @@ function parseAgentDomainEvent(value, lineNumber) {
|
|
|
3546
3548
|
type: value.type
|
|
3547
3549
|
};
|
|
3548
3550
|
case "agent_turn_completed": return {
|
|
3549
|
-
finalText: readString(value, "finalText", lineNumber),
|
|
3551
|
+
finalText: readString$1(value, "finalText", lineNumber),
|
|
3550
3552
|
occurredAt,
|
|
3551
|
-
runId: readString(value, "runId", lineNumber),
|
|
3553
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3552
3554
|
type: value.type
|
|
3553
3555
|
};
|
|
3554
3556
|
case "agent_run_completed": return {
|
|
3555
|
-
finalText: readString(value, "finalText", lineNumber),
|
|
3557
|
+
finalText: readString$1(value, "finalText", lineNumber),
|
|
3556
3558
|
occurredAt,
|
|
3557
|
-
runId: readString(value, "runId", lineNumber),
|
|
3559
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3558
3560
|
type: value.type
|
|
3559
3561
|
};
|
|
3560
3562
|
case "agent_run_failed": return {
|
|
3561
|
-
errorMessage: readString(value, "errorMessage", lineNumber),
|
|
3563
|
+
errorMessage: readString$1(value, "errorMessage", lineNumber),
|
|
3562
3564
|
occurredAt,
|
|
3563
|
-
runId: readString(value, "runId", lineNumber),
|
|
3565
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3564
3566
|
type: value.type
|
|
3565
3567
|
};
|
|
3566
3568
|
case "agent_run_cancelled": {
|
|
@@ -3568,7 +3570,7 @@ function parseAgentDomainEvent(value, lineNumber) {
|
|
|
3568
3570
|
return {
|
|
3569
3571
|
occurredAt,
|
|
3570
3572
|
...reason ? { reason } : {},
|
|
3571
|
-
runId: readString(value, "runId", lineNumber),
|
|
3573
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3572
3574
|
type: value.type
|
|
3573
3575
|
};
|
|
3574
3576
|
}
|
|
@@ -3577,19 +3579,19 @@ function parseAgentDomainEvent(value, lineNumber) {
|
|
|
3577
3579
|
}
|
|
3578
3580
|
function readSkillExecutionIdentity(value, lineNumber) {
|
|
3579
3581
|
return {
|
|
3580
|
-
runId: readString(value, "runId", lineNumber),
|
|
3581
|
-
skillCallId: readString(value, "skillCallId", lineNumber),
|
|
3582
|
-
skillId: readString(value, "skillId", lineNumber)
|
|
3582
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3583
|
+
skillCallId: readString$1(value, "skillCallId", lineNumber),
|
|
3584
|
+
skillId: readString$1(value, "skillId", lineNumber)
|
|
3583
3585
|
};
|
|
3584
3586
|
}
|
|
3585
3587
|
function readToolExecutionIdentity(value, lineNumber) {
|
|
3586
3588
|
return {
|
|
3587
|
-
runId: readString(value, "runId", lineNumber),
|
|
3588
|
-
toolCallId: readString(value, "toolCallId", lineNumber),
|
|
3589
|
-
toolName: readString(value, "toolName", lineNumber)
|
|
3589
|
+
runId: readString$1(value, "runId", lineNumber),
|
|
3590
|
+
toolCallId: readString$1(value, "toolCallId", lineNumber),
|
|
3591
|
+
toolName: readString$1(value, "toolName", lineNumber)
|
|
3590
3592
|
};
|
|
3591
3593
|
}
|
|
3592
|
-
function readString(value, field, lineNumber) {
|
|
3594
|
+
function readString$1(value, field, lineNumber) {
|
|
3593
3595
|
const fieldValue = value[field];
|
|
3594
3596
|
if (typeof fieldValue !== "string") throw new Error(`Invalid agent event field '${field}' at line ${lineNumber}.`);
|
|
3595
3597
|
return fieldValue;
|
|
@@ -4760,7 +4762,7 @@ function unwrapResult(value) {
|
|
|
4760
4762
|
}
|
|
4761
4763
|
function presentResult(toolName, value) {
|
|
4762
4764
|
if (isTruncated(value)) return "结果过长,已省略。";
|
|
4763
|
-
if (!isRecord(value)) return truncate(presentValue(value) ?? "工具执行完成。", 2e3);
|
|
4765
|
+
if (!isRecord(value)) return truncate$1(presentValue(value) ?? "工具执行完成。", 2e3);
|
|
4764
4766
|
const tool = resolveTool(toolName);
|
|
4765
4767
|
const orderedKeys = tool.fields?.map(([key]) => key) ?? [];
|
|
4766
4768
|
const lines = Object.entries(value).sort(([left], [right]) => {
|
|
@@ -4772,7 +4774,7 @@ function presentResult(toolName, value) {
|
|
|
4772
4774
|
const presented = presentValue(item, field?.[2]);
|
|
4773
4775
|
return presented ? [`${clean(field?.[1] ?? humanizeKey(key), 80)}:${presented}`] : [];
|
|
4774
4776
|
}).slice(0, 12);
|
|
4775
|
-
return truncate(lines.length > 0 ? lines.join("\n") : "工具执行完成。", 2e3);
|
|
4777
|
+
return truncate$1(lines.length > 0 ? lines.join("\n") : "工具执行完成。", 2e3);
|
|
4776
4778
|
}
|
|
4777
4779
|
function presentValue(value, unit) {
|
|
4778
4780
|
if (value === REDACTED || value === TRUNCATED || value === void 0) return void 0;
|
|
@@ -4796,9 +4798,9 @@ function humanizeKey(key) {
|
|
|
4796
4798
|
return `${text.slice(0, 1).toUpperCase()}${text.slice(1)}` || "结果";
|
|
4797
4799
|
}
|
|
4798
4800
|
function clean(value, maxLength) {
|
|
4799
|
-
return truncate(value.replace(/[\p{Cc}\p{Cf}]+/gu, " ").replace(/\s+/gu, " ").trim(), maxLength);
|
|
4801
|
+
return truncate$1(value.replace(/[\p{Cc}\p{Cf}]+/gu, " ").replace(/\s+/gu, " ").trim(), maxLength);
|
|
4800
4802
|
}
|
|
4801
|
-
function truncate(value, maxLength) {
|
|
4803
|
+
function truncate$1(value, maxLength) {
|
|
4802
4804
|
return value.length <= maxLength ? value : `${value.slice(0, maxLength - 1)}…`;
|
|
4803
4805
|
}
|
|
4804
4806
|
function isRecord(value) {
|
|
@@ -5346,7 +5348,14 @@ function createToolBroker(options) {
|
|
|
5346
5348
|
runId: authority.runId,
|
|
5347
5349
|
sessionKey: authority.sessionKey,
|
|
5348
5350
|
toolId: tool.id,
|
|
5349
|
-
toolVersion: tool.version
|
|
5351
|
+
toolVersion: tool.version,
|
|
5352
|
+
...authority.endpointId ? { origin: Object.freeze({
|
|
5353
|
+
...authority.allowedActorOpenIds ? { allowedActorOpenIds: authority.allowedActorOpenIds } : { allowedActorOpenIds: Object.freeze([]) },
|
|
5354
|
+
endpointId: authority.endpointId,
|
|
5355
|
+
tenantKey: authority.tenantKey,
|
|
5356
|
+
...authority.conversationId ? { conversationId: authority.conversationId } : {}
|
|
5357
|
+
}) } : {},
|
|
5358
|
+
...authority.sourceMessageId ? { sourceMessageId: authority.sourceMessageId } : {}
|
|
5350
5359
|
});
|
|
5351
5360
|
const replayCompleted = (result) => "replayCompleted" in tool && tool.replayCompleted ? tool.replayCompleted(request.input, result, context) : result;
|
|
5352
5361
|
if (request.operationId) {
|
|
@@ -5881,6 +5890,1464 @@ function isVisibleMemory(record) {
|
|
|
5881
5890
|
return record.scope !== "shared-user-profile" && record.scope !== "project" || record.state === "confirmed";
|
|
5882
5891
|
}
|
|
5883
5892
|
//#endregion
|
|
5893
|
+
//#region src/domain/background-agent-session.ts
|
|
5894
|
+
const BACKGROUND_SESSION_TERMINAL_PHASES = /* @__PURE__ */ new Set([
|
|
5895
|
+
"stopped",
|
|
5896
|
+
"completed",
|
|
5897
|
+
"failed",
|
|
5898
|
+
"reconciliation-required"
|
|
5899
|
+
]);
|
|
5900
|
+
function isBackgroundSessionTerminalPhase(phase) {
|
|
5901
|
+
return BACKGROUND_SESSION_TERMINAL_PHASES.has(phase);
|
|
5902
|
+
}
|
|
5903
|
+
var BackgroundSessionTransitionDenied = class extends Error {
|
|
5904
|
+
sessionId;
|
|
5905
|
+
name = "BackgroundSessionTransitionDenied";
|
|
5906
|
+
constructor(sessionId, message) {
|
|
5907
|
+
super(message);
|
|
5908
|
+
this.sessionId = sessionId;
|
|
5909
|
+
}
|
|
5910
|
+
};
|
|
5911
|
+
function createBackgroundSession(input) {
|
|
5912
|
+
if (!input.sessionId.trim()) throw new BackgroundSessionTransitionDenied(input.sessionId, "background session id must not be empty");
|
|
5913
|
+
if (!input.authority.sessionKey.trim()) throw new BackgroundSessionTransitionDenied(input.sessionId, "background session key must not be empty");
|
|
5914
|
+
if (!input.authority.agentId.trim()) throw new BackgroundSessionTransitionDenied(input.sessionId, "background session authority requires an agent id");
|
|
5915
|
+
if (!input.sourceMessageId.trim()) throw new BackgroundSessionTransitionDenied(input.sessionId, "background session requires a trusted source message id");
|
|
5916
|
+
return Object.freeze({
|
|
5917
|
+
authority: Object.freeze({
|
|
5918
|
+
...input.authority,
|
|
5919
|
+
memoryScopes: Object.freeze([...input.authority.memoryScopes])
|
|
5920
|
+
}),
|
|
5921
|
+
consecutiveFailures: 0,
|
|
5922
|
+
createdAt: input.now,
|
|
5923
|
+
displayName: input.displayName,
|
|
5924
|
+
origin: Object.freeze({
|
|
5925
|
+
...input.origin,
|
|
5926
|
+
allowedActorOpenIds: Object.freeze([...input.origin.allowedActorOpenIds])
|
|
5927
|
+
}),
|
|
5928
|
+
parentRunId: input.parentRunId,
|
|
5929
|
+
pendingInput: Object.freeze([]),
|
|
5930
|
+
phase: "queued",
|
|
5931
|
+
prompt: input.prompt,
|
|
5932
|
+
revision: 1,
|
|
5933
|
+
sessionId: input.sessionId,
|
|
5934
|
+
sourceMessageId: input.sourceMessageId,
|
|
5935
|
+
stepCount: 0,
|
|
5936
|
+
updatedAt: input.now,
|
|
5937
|
+
wakeCount: 0
|
|
5938
|
+
});
|
|
5939
|
+
}
|
|
5940
|
+
function claimBackgroundSession(state, input) {
|
|
5941
|
+
if (isBackgroundSessionTerminalPhase(state.phase)) throw new BackgroundSessionTransitionDenied(state.sessionId, `terminal ${state.phase} session cannot run again`);
|
|
5942
|
+
if (state.phase !== "queued" && state.phase !== "waiting") throw new BackgroundSessionTransitionDenied(state.sessionId, `background session cannot start a step while ${state.phase}`);
|
|
5943
|
+
if (state.wakeAt !== void 0 && state.wakeAt > input.now) throw new BackgroundSessionTransitionDenied(state.sessionId, "background session is not due");
|
|
5944
|
+
return next(state, {
|
|
5945
|
+
currentStepRunId: input.stepRunId,
|
|
5946
|
+
lastWakeText: input.wakeText,
|
|
5947
|
+
lease: input.lease,
|
|
5948
|
+
pendingInput: Object.freeze([]),
|
|
5949
|
+
phase: "running",
|
|
5950
|
+
stepCount: state.stepCount + 1,
|
|
5951
|
+
updatedAt: input.now,
|
|
5952
|
+
wakeAt: void 0,
|
|
5953
|
+
wakeCount: state.wakeCount + 1
|
|
5954
|
+
});
|
|
5955
|
+
}
|
|
5956
|
+
function renewBackgroundSessionLease(state, lease) {
|
|
5957
|
+
if (state.lease === void 0) throw new BackgroundSessionTransitionDenied(state.sessionId, "cannot renew a lease that is not held");
|
|
5958
|
+
if (state.lease.owner !== lease.owner || state.lease.epoch !== lease.epoch) throw new BackgroundSessionTransitionDenied(state.sessionId, "cannot renew a lease from another worker");
|
|
5959
|
+
return next(state, { lease });
|
|
5960
|
+
}
|
|
5961
|
+
function releaseBackgroundSessionLease(state) {
|
|
5962
|
+
if (state.lease === void 0) throw new BackgroundSessionTransitionDenied(state.sessionId, "no lease to release");
|
|
5963
|
+
return next(state, { lease: void 0 });
|
|
5964
|
+
}
|
|
5965
|
+
function suspendBackgroundSession(state, input) {
|
|
5966
|
+
if (state.phase !== "running") throw new BackgroundSessionTransitionDenied(state.sessionId, `background session cannot wait while ${state.phase}`);
|
|
5967
|
+
if (input.wakeAt !== void 0 && input.wakeAt <= input.now) throw new BackgroundSessionTransitionDenied(state.sessionId, "background session wake time must be in the future");
|
|
5968
|
+
return next(state, {
|
|
5969
|
+
currentStepRunId: void 0,
|
|
5970
|
+
phase: input.wakeAt === void 0 ? "input-required" : "waiting",
|
|
5971
|
+
updatedAt: input.now,
|
|
5972
|
+
...input.wakeAt === void 0 ? {} : { wakeAt: input.wakeAt }
|
|
5973
|
+
});
|
|
5974
|
+
}
|
|
5975
|
+
function completeBackgroundSessionStep(state, input) {
|
|
5976
|
+
if (state.phase !== "running") throw new BackgroundSessionTransitionDenied(state.sessionId, `background session cannot complete while ${state.phase}`);
|
|
5977
|
+
return next(state, {
|
|
5978
|
+
consecutiveFailures: 0,
|
|
5979
|
+
currentStepRunId: void 0,
|
|
5980
|
+
lease: void 0,
|
|
5981
|
+
phase: "completed",
|
|
5982
|
+
result: {
|
|
5983
|
+
completedAt: input.now,
|
|
5984
|
+
stepRunId: input.stepRunId,
|
|
5985
|
+
text: input.text
|
|
5986
|
+
},
|
|
5987
|
+
updatedAt: input.now
|
|
5988
|
+
});
|
|
5989
|
+
}
|
|
5990
|
+
function failBackgroundSessionStep(state, input) {
|
|
5991
|
+
if (state.phase !== "running") throw new BackgroundSessionTransitionDenied(state.sessionId, `background session cannot fail while ${state.phase}`);
|
|
5992
|
+
if (input.retryable) {
|
|
5993
|
+
if (input.wakeAt === void 0 || input.wakeAt <= input.now) throw new BackgroundSessionTransitionDenied(state.sessionId, "retryable failure requires a future wake time");
|
|
5994
|
+
return next(state, {
|
|
5995
|
+
consecutiveFailures: state.consecutiveFailures + 1,
|
|
5996
|
+
currentStepRunId: void 0,
|
|
5997
|
+
errorMessage: input.errorMessage,
|
|
5998
|
+
lease: void 0,
|
|
5999
|
+
phase: "queued",
|
|
6000
|
+
updatedAt: input.now,
|
|
6001
|
+
wakeAt: input.wakeAt
|
|
6002
|
+
});
|
|
6003
|
+
}
|
|
6004
|
+
return next(state, {
|
|
6005
|
+
consecutiveFailures: state.consecutiveFailures + 1,
|
|
6006
|
+
currentStepRunId: void 0,
|
|
6007
|
+
errorMessage: input.errorMessage,
|
|
6008
|
+
lease: void 0,
|
|
6009
|
+
phase: "failed",
|
|
6010
|
+
updatedAt: input.now
|
|
6011
|
+
});
|
|
6012
|
+
}
|
|
6013
|
+
function requestBackgroundSessionStop(state, input) {
|
|
6014
|
+
if (isBackgroundSessionTerminalPhase(state.phase)) throw new BackgroundSessionTransitionDenied(state.sessionId, `terminal ${state.phase} session cannot stop`);
|
|
6015
|
+
if (state.phase === "stopping") throw new BackgroundSessionTransitionDenied(state.sessionId, "background session is already stopping");
|
|
6016
|
+
if (state.phase === "running") return next(state, {
|
|
6017
|
+
cancellation: {
|
|
6018
|
+
reason: input.reason,
|
|
6019
|
+
requestedAt: input.now
|
|
6020
|
+
},
|
|
6021
|
+
phase: "stopping",
|
|
6022
|
+
updatedAt: input.now
|
|
6023
|
+
});
|
|
6024
|
+
return next(state, {
|
|
6025
|
+
cancellation: {
|
|
6026
|
+
reason: input.reason,
|
|
6027
|
+
requestedAt: input.now
|
|
6028
|
+
},
|
|
6029
|
+
currentStepRunId: void 0,
|
|
6030
|
+
lease: void 0,
|
|
6031
|
+
phase: "stopped",
|
|
6032
|
+
updatedAt: input.now,
|
|
6033
|
+
wakeAt: void 0
|
|
6034
|
+
});
|
|
6035
|
+
}
|
|
6036
|
+
function completeBackgroundSessionStop(state, input) {
|
|
6037
|
+
if (state.phase !== "stopping") throw new BackgroundSessionTransitionDenied(state.sessionId, `background session cannot finish stopping while ${state.phase}`);
|
|
6038
|
+
return next(state, {
|
|
6039
|
+
currentStepRunId: void 0,
|
|
6040
|
+
lease: void 0,
|
|
6041
|
+
phase: "stopped",
|
|
6042
|
+
updatedAt: input.now,
|
|
6043
|
+
wakeAt: void 0
|
|
6044
|
+
});
|
|
6045
|
+
}
|
|
6046
|
+
function parkBackgroundSessionForReconciliation(state, input) {
|
|
6047
|
+
if (isBackgroundSessionTerminalPhase(state.phase) && state.phase !== "reconciliation-required") throw new BackgroundSessionTransitionDenied(state.sessionId, `terminal ${state.phase} session cannot be reconciled`);
|
|
6048
|
+
return next(state, {
|
|
6049
|
+
currentStepRunId: void 0,
|
|
6050
|
+
errorMessage: input.reason,
|
|
6051
|
+
lease: void 0,
|
|
6052
|
+
phase: "reconciliation-required",
|
|
6053
|
+
reconciliationNote: input.reason,
|
|
6054
|
+
updatedAt: input.now,
|
|
6055
|
+
wakeAt: void 0
|
|
6056
|
+
});
|
|
6057
|
+
}
|
|
6058
|
+
function resolveBackgroundSessionReconciliation(state, input) {
|
|
6059
|
+
if (state.phase !== "reconciliation-required") throw new BackgroundSessionTransitionDenied(state.sessionId, `background session cannot resolve while ${state.phase}`);
|
|
6060
|
+
if (input.outcome === "continue") return next(state, {
|
|
6061
|
+
errorMessage: void 0,
|
|
6062
|
+
phase: "queued",
|
|
6063
|
+
reconciliationNote: void 0,
|
|
6064
|
+
updatedAt: input.now,
|
|
6065
|
+
wakeAt: input.now
|
|
6066
|
+
});
|
|
6067
|
+
return next(state, {
|
|
6068
|
+
errorMessage: void 0,
|
|
6069
|
+
phase: "stopped",
|
|
6070
|
+
reconciliationNote: void 0,
|
|
6071
|
+
updatedAt: input.now,
|
|
6072
|
+
wakeAt: void 0
|
|
6073
|
+
});
|
|
6074
|
+
}
|
|
6075
|
+
function appendBackgroundSessionInput(state, input) {
|
|
6076
|
+
if (isBackgroundSessionTerminalPhase(state.phase)) throw new BackgroundSessionTransitionDenied(state.sessionId, `terminal ${state.phase} session cannot accept input`);
|
|
6077
|
+
return next(state, {
|
|
6078
|
+
pendingInput: Object.freeze([...state.pendingInput, input.message]),
|
|
6079
|
+
updatedAt: input.now,
|
|
6080
|
+
...state.phase === "waiting" || state.phase === "input-required" ? {
|
|
6081
|
+
phase: "queued",
|
|
6082
|
+
wakeAt: input.now
|
|
6083
|
+
} : {}
|
|
6084
|
+
});
|
|
6085
|
+
}
|
|
6086
|
+
function requeueInterruptedBackgroundSessionStep(state, input) {
|
|
6087
|
+
if (state.phase !== "running") throw new BackgroundSessionTransitionDenied(state.sessionId, "interrupted step recovery requires a running session");
|
|
6088
|
+
return next(state, {
|
|
6089
|
+
currentStepRunId: void 0,
|
|
6090
|
+
lastWakeText: input.wakeText,
|
|
6091
|
+
lease: void 0,
|
|
6092
|
+
phase: "queued",
|
|
6093
|
+
updatedAt: input.now,
|
|
6094
|
+
wakeAt: input.now
|
|
6095
|
+
});
|
|
6096
|
+
}
|
|
6097
|
+
function isBackgroundSessionLeaseExpired(state, now) {
|
|
6098
|
+
return state.lease !== void 0 && state.lease.expiresAt <= now;
|
|
6099
|
+
}
|
|
6100
|
+
function isBackgroundSessionDue(state, now) {
|
|
6101
|
+
if (state.phase !== "queued" && state.phase !== "waiting") return false;
|
|
6102
|
+
return state.wakeAt === void 0 || state.wakeAt <= now;
|
|
6103
|
+
}
|
|
6104
|
+
function next(state, changes) {
|
|
6105
|
+
const merged = {
|
|
6106
|
+
...state,
|
|
6107
|
+
...changes,
|
|
6108
|
+
revision: state.revision + 1
|
|
6109
|
+
};
|
|
6110
|
+
for (const key of Object.keys(merged)) if (merged[key] === void 0) delete merged[key];
|
|
6111
|
+
return Object.freeze(merged);
|
|
6112
|
+
}
|
|
6113
|
+
function isValidBackgroundSessionTransition(previous, nextState) {
|
|
6114
|
+
if (nextState.sessionId !== previous.sessionId) return "session id must not change";
|
|
6115
|
+
if (nextState.revision !== previous.revision + 1) return "revision must increase by exactly one";
|
|
6116
|
+
if (nextState.authority.agentId !== previous.authority.agentId) return "authority agent must not change";
|
|
6117
|
+
if (nextState.authority.profileRevision !== previous.authority.profileRevision) return "authority profile revision must not change";
|
|
6118
|
+
if (nextState.authority.toolGrantRevision !== previous.authority.toolGrantRevision) return "authority tool grant revision must not change";
|
|
6119
|
+
if (nextState.authority.sessionKey !== previous.authority.sessionKey) return "authority session key must not change";
|
|
6120
|
+
if (nextState.origin.endpointId !== previous.origin.endpointId) return "origin endpoint must not change";
|
|
6121
|
+
if (nextState.origin.tenantKey !== previous.origin.tenantKey) return "origin tenant must not change";
|
|
6122
|
+
if (nextState.origin.conversationId !== previous.origin.conversationId) return "origin conversation must not change";
|
|
6123
|
+
if (nextState.parentRunId !== previous.parentRunId) return "parent run must not change";
|
|
6124
|
+
if (nextState.sourceMessageId !== previous.sourceMessageId) return "source message must not change";
|
|
6125
|
+
if (nextState.prompt !== previous.prompt) return "prompt must not change";
|
|
6126
|
+
if (nextState.stepCount < previous.stepCount) return "step count must not decrease";
|
|
6127
|
+
if (nextState.wakeCount < previous.wakeCount) return "wake count must not decrease";
|
|
6128
|
+
if (nextState.consecutiveFailures < previous.consecutiveFailures) {
|
|
6129
|
+
if (previous.consecutiveFailures > 0 && nextState.phase === "completed" && nextState.consecutiveFailures === 0) return;
|
|
6130
|
+
return "failure count must not decrease";
|
|
6131
|
+
}
|
|
6132
|
+
if (nextState.createdAt !== previous.createdAt) return "created time must not change";
|
|
6133
|
+
if (previous.phase === "running") {
|
|
6134
|
+
if (nextState.phase === "running") return holdsSameLease(previous, nextState) ? void 0 : "running lease owner or epoch must not change";
|
|
6135
|
+
if (nextState.phase === "waiting" || nextState.phase === "input-required") return holdsSameLease(previous, nextState) ? void 0 : "suspension requires the same lease";
|
|
6136
|
+
if (nextState.phase === "completed" && nextState.result !== void 0 && nextState.lease === void 0) return;
|
|
6137
|
+
if (nextState.phase === "failed" && nextState.errorMessage !== void 0 && nextState.lease === void 0) return;
|
|
6138
|
+
if (nextState.phase === "stopping" && nextState.cancellation !== void 0) return void 0;
|
|
6139
|
+
if (nextState.phase === "reconciliation-required" && nextState.lease === void 0) return void 0;
|
|
6140
|
+
if (nextState.phase === "queued" && nextState.lease === void 0 && nextState.wakeAt !== void 0) return;
|
|
6141
|
+
return `running session cannot transition to ${nextState.phase}`;
|
|
6142
|
+
}
|
|
6143
|
+
if (previous.phase === "stopping") {
|
|
6144
|
+
if (nextState.phase === "stopped" && nextState.lease === void 0 && nextState.cancellation !== void 0) return;
|
|
6145
|
+
if (nextState.phase === "reconciliation-required" && nextState.lease === void 0) return void 0;
|
|
6146
|
+
return `stopping session cannot transition to ${nextState.phase}`;
|
|
6147
|
+
}
|
|
6148
|
+
if (previous.phase === "queued") {
|
|
6149
|
+
if (nextState.phase === "running" && nextState.lease !== void 0) return void 0;
|
|
6150
|
+
if (nextState.phase === "queued" && nextState.lease === void 0 && nextState.wakeAt === previous.wakeAt) return;
|
|
6151
|
+
if (nextState.phase === "stopped" && nextState.lease === void 0) return void 0;
|
|
6152
|
+
if (nextState.phase === "reconciliation-required" && nextState.lease === void 0) return void 0;
|
|
6153
|
+
return `queued session cannot transition to ${nextState.phase}`;
|
|
6154
|
+
}
|
|
6155
|
+
if (previous.phase === "waiting") {
|
|
6156
|
+
if (nextState.phase === "running" && nextState.lease !== void 0) return void 0;
|
|
6157
|
+
if (nextState.phase === "waiting" && nextState.lease === void 0 && nextState.wakeAt === previous.wakeAt) return;
|
|
6158
|
+
if (nextState.phase === "queued" && nextState.wakeAt !== void 0) return void 0;
|
|
6159
|
+
if (nextState.phase === "stopped" && nextState.lease === void 0) return void 0;
|
|
6160
|
+
if (nextState.phase === "reconciliation-required" && nextState.lease === void 0) return void 0;
|
|
6161
|
+
return `waiting session cannot transition to ${nextState.phase}`;
|
|
6162
|
+
}
|
|
6163
|
+
if (previous.phase === "input-required") {
|
|
6164
|
+
if (nextState.phase === "input-required" && nextState.lease === void 0) return void 0;
|
|
6165
|
+
if (nextState.phase === "queued" && nextState.wakeAt !== void 0) return void 0;
|
|
6166
|
+
if (nextState.phase === "stopped" && nextState.lease === void 0) return void 0;
|
|
6167
|
+
if (nextState.phase === "reconciliation-required" && nextState.lease === void 0) return void 0;
|
|
6168
|
+
return `input-required session cannot transition to ${nextState.phase}`;
|
|
6169
|
+
}
|
|
6170
|
+
if (isBackgroundSessionTerminalPhase(previous.phase)) {
|
|
6171
|
+
if (previous.phase === "reconciliation-required") {
|
|
6172
|
+
if (nextState.phase !== "queued" && nextState.phase !== "stopped") return "reconciliation-required session can only continue or stop";
|
|
6173
|
+
return;
|
|
6174
|
+
}
|
|
6175
|
+
return `terminal ${previous.phase} session cannot transition`;
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
function holdsSameLease(previous, nextState) {
|
|
6179
|
+
return nextState.lease !== void 0 && previous.lease !== void 0 && nextState.lease.owner === previous.lease.owner && nextState.lease.epoch === previous.lease.epoch;
|
|
6180
|
+
}
|
|
6181
|
+
//#endregion
|
|
6182
|
+
//#region src/application/background-session/background-session-service.ts
|
|
6183
|
+
var BackgroundSessionCallerDenied = class extends Error {
|
|
6184
|
+
name = "BackgroundSessionCallerDenied";
|
|
6185
|
+
constructor(message) {
|
|
6186
|
+
super(message);
|
|
6187
|
+
}
|
|
6188
|
+
};
|
|
6189
|
+
function createBackgroundSessionService(options) {
|
|
6190
|
+
const serial = createSerialExecutor();
|
|
6191
|
+
const start = async (input) => {
|
|
6192
|
+
const now = options.clock.now();
|
|
6193
|
+
const origin = requireOrigin(input.context);
|
|
6194
|
+
const state = createBackgroundSession({
|
|
6195
|
+
authority: input.authority,
|
|
6196
|
+
displayName: input.displayName?.trim() || input.sessionId,
|
|
6197
|
+
now,
|
|
6198
|
+
origin: {
|
|
6199
|
+
allowedActorOpenIds: origin.allowedActorOpenIds,
|
|
6200
|
+
...origin.conversationId === void 0 ? {} : { conversationId: origin.conversationId },
|
|
6201
|
+
endpointId: origin.endpointId,
|
|
6202
|
+
...input.context.memory ? { memory: {
|
|
6203
|
+
audience: input.context.memory.audience,
|
|
6204
|
+
...input.context.memory.conversationId ? { conversationId: input.context.memory.conversationId } : {},
|
|
6205
|
+
...input.context.memory.projectId ? { projectId: input.context.memory.projectId } : {},
|
|
6206
|
+
subjectId: input.context.memory.subjectId,
|
|
6207
|
+
tenantId: input.context.memory.tenantId
|
|
6208
|
+
} } : {},
|
|
6209
|
+
tenantKey: origin.tenantKey
|
|
6210
|
+
},
|
|
6211
|
+
parentRunId: input.context.runId,
|
|
6212
|
+
prompt: input.prompt,
|
|
6213
|
+
sessionId: input.sessionId,
|
|
6214
|
+
sourceMessageId: requireSourceMessageId(input.context)
|
|
6215
|
+
});
|
|
6216
|
+
return toSummary(await options.repository.create(state));
|
|
6217
|
+
};
|
|
6218
|
+
const wait = async (input) => {
|
|
6219
|
+
const sessionId = sessionIdFromSessionKey(input.context.sessionKey);
|
|
6220
|
+
if (!sessionId) throw new BackgroundSessionCallerDenied("background.wait is only available inside a background session step");
|
|
6221
|
+
if (input.delayMs !== void 0 && input.until !== void 0) throw new BackgroundSessionCallerDenied("background.wait cannot combine delayMs and until");
|
|
6222
|
+
const now = options.clock.now();
|
|
6223
|
+
let wakeAt;
|
|
6224
|
+
if (input.delayMs !== void 0) {
|
|
6225
|
+
if (!Number.isSafeInteger(input.delayMs) || input.delayMs < 1) throw new BackgroundSessionCallerDenied("background.wait delayMs must be a positive integer");
|
|
6226
|
+
wakeAt = new Date(Date.parse(now) + input.delayMs).toISOString();
|
|
6227
|
+
} else if (input.until !== void 0) {
|
|
6228
|
+
const parsed = Date.parse(input.until);
|
|
6229
|
+
if (!Number.isFinite(parsed)) throw new BackgroundSessionCallerDenied("background.wait until must be an ISO 8601 timestamp");
|
|
6230
|
+
wakeAt = new Date(parsed).toISOString();
|
|
6231
|
+
}
|
|
6232
|
+
return toSummary(requireState(await serial.run(async () => {
|
|
6233
|
+
const state = await options.repository.get(sessionId);
|
|
6234
|
+
if (!state) throw new BackgroundSessionCallerDenied(`background session not found: ${sessionId}`);
|
|
6235
|
+
requireMatchingOrigin(state, input.context);
|
|
6236
|
+
const nextState = suspendBackgroundSession(state, {
|
|
6237
|
+
now,
|
|
6238
|
+
...input.reason === void 0 ? {} : { reason: input.reason },
|
|
6239
|
+
...wakeAt ? { wakeAt } : {}
|
|
6240
|
+
});
|
|
6241
|
+
return options.repository.update({
|
|
6242
|
+
build: () => nextState,
|
|
6243
|
+
expectedRevision: state.revision,
|
|
6244
|
+
sessionId
|
|
6245
|
+
});
|
|
6246
|
+
}), sessionId));
|
|
6247
|
+
};
|
|
6248
|
+
const list = async (input) => {
|
|
6249
|
+
return (await options.repository.list(input.phase === void 0 ? void 0 : { phase: input.phase })).filter((state) => matchesOrigin(state, input.context)).sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, input.limit).map(toSummary);
|
|
6250
|
+
};
|
|
6251
|
+
const status = async (input) => {
|
|
6252
|
+
return toDetail(await requireOwnedState(input.sessionId, input.context));
|
|
6253
|
+
};
|
|
6254
|
+
const send = async (input) => {
|
|
6255
|
+
return toSummary(requireState(await serial.run(async () => {
|
|
6256
|
+
const state = await requireOwnedState(input.sessionId, input.context);
|
|
6257
|
+
const nextState = appendBackgroundSessionInput(state, {
|
|
6258
|
+
message: input.message,
|
|
6259
|
+
now: options.clock.now()
|
|
6260
|
+
});
|
|
6261
|
+
return options.repository.update({
|
|
6262
|
+
build: () => nextState,
|
|
6263
|
+
expectedRevision: state.revision,
|
|
6264
|
+
sessionId: input.sessionId
|
|
6265
|
+
});
|
|
6266
|
+
}), input.sessionId));
|
|
6267
|
+
};
|
|
6268
|
+
const stop = async (input) => {
|
|
6269
|
+
return toSummary(requireState(await serial.run(async () => {
|
|
6270
|
+
const state = await requireOwnedState(input.sessionId, input.context);
|
|
6271
|
+
const nextState = requestBackgroundSessionStop(state, {
|
|
6272
|
+
now: options.clock.now(),
|
|
6273
|
+
reason: input.reason ?? "stopped by conversation"
|
|
6274
|
+
});
|
|
6275
|
+
return updateAndEnqueueStopped(input.sessionId, state.revision, nextState);
|
|
6276
|
+
}), input.sessionId));
|
|
6277
|
+
};
|
|
6278
|
+
const requireOwnedState = async (sessionId, context) => {
|
|
6279
|
+
const state = await options.repository.get(sessionId);
|
|
6280
|
+
if (!state) throw new BackgroundSessionCallerDenied(`background session not found: ${sessionId}`);
|
|
6281
|
+
requireMatchingOrigin(state, context);
|
|
6282
|
+
return state;
|
|
6283
|
+
};
|
|
6284
|
+
const updateAndEnqueueStopped = async (sessionId, expectedRevision, nextState) => {
|
|
6285
|
+
const stored = await options.repository.update({
|
|
6286
|
+
build: () => nextState,
|
|
6287
|
+
expectedRevision,
|
|
6288
|
+
sessionId
|
|
6289
|
+
});
|
|
6290
|
+
if (stored?.phase === "stopped") await options.deliveries.enqueue({
|
|
6291
|
+
createdAt: options.clock.now(),
|
|
6292
|
+
deliveryId: terminalDeliveryId(sessionId, "stopped"),
|
|
6293
|
+
kind: "stopped",
|
|
6294
|
+
sessionId,
|
|
6295
|
+
text: stopNotice$1(stored)
|
|
6296
|
+
});
|
|
6297
|
+
return stored;
|
|
6298
|
+
};
|
|
6299
|
+
const resolveReconciliation = async (input) => {
|
|
6300
|
+
return toSummary(requireState(await serial.run(async () => {
|
|
6301
|
+
const state = await options.repository.get(input.sessionId);
|
|
6302
|
+
if (!state) throw new BackgroundSessionCallerDenied(`background session not found: ${input.sessionId}`);
|
|
6303
|
+
const nextState = resolveBackgroundSessionReconciliation(state, {
|
|
6304
|
+
now: options.clock.now(),
|
|
6305
|
+
outcome: input.outcome
|
|
6306
|
+
});
|
|
6307
|
+
return updateAndEnqueueStopped(input.sessionId, state.revision, nextState);
|
|
6308
|
+
}), input.sessionId));
|
|
6309
|
+
};
|
|
6310
|
+
return {
|
|
6311
|
+
list,
|
|
6312
|
+
resolveReconciliation,
|
|
6313
|
+
send,
|
|
6314
|
+
start,
|
|
6315
|
+
status,
|
|
6316
|
+
stop,
|
|
6317
|
+
wait
|
|
6318
|
+
};
|
|
6319
|
+
}
|
|
6320
|
+
function sessionIdFromSessionKey(sessionKey) {
|
|
6321
|
+
const prefix = `${createBackgroundSessionKey("")}`;
|
|
6322
|
+
return sessionKey.startsWith(prefix) ? sessionKey.slice(prefix.length) : void 0;
|
|
6323
|
+
}
|
|
6324
|
+
function terminalDeliveryId(sessionId, kind) {
|
|
6325
|
+
return `bg-final:${sessionId}:${kind}`;
|
|
6326
|
+
}
|
|
6327
|
+
function progressDeliveryId(sessionId, stepCount) {
|
|
6328
|
+
return `bg-progress:${sessionId}:${stepCount}`;
|
|
6329
|
+
}
|
|
6330
|
+
function requireOrigin(context) {
|
|
6331
|
+
if (!context.origin || !context.origin.endpointId || !context.origin.tenantKey) throw new BackgroundSessionCallerDenied("background tools require a trusted invocation origin; this run has none");
|
|
6332
|
+
return context.origin;
|
|
6333
|
+
}
|
|
6334
|
+
function requireSourceMessageId(context) {
|
|
6335
|
+
if (!context.sourceMessageId?.trim()) throw new BackgroundSessionCallerDenied("background.start requires a trusted source message id");
|
|
6336
|
+
return context.sourceMessageId;
|
|
6337
|
+
}
|
|
6338
|
+
function requireMatchingOrigin(state, context) {
|
|
6339
|
+
if (!matchesOrigin(state, context)) throw new BackgroundSessionCallerDenied("background session is not owned by this conversation");
|
|
6340
|
+
}
|
|
6341
|
+
function matchesOrigin(state, context) {
|
|
6342
|
+
const origin = context.origin;
|
|
6343
|
+
if (!origin) return false;
|
|
6344
|
+
if (context.agentId !== state.authority.agentId) return false;
|
|
6345
|
+
if (state.origin.endpointId !== origin.endpointId) return false;
|
|
6346
|
+
if (state.origin.tenantKey !== origin.tenantKey) return false;
|
|
6347
|
+
if ((state.origin.conversationId ?? void 0) !== (origin.conversationId ?? void 0)) return false;
|
|
6348
|
+
return true;
|
|
6349
|
+
}
|
|
6350
|
+
function requireState(state, sessionId) {
|
|
6351
|
+
if (!state) throw new BackgroundSessionCallerDenied(`background session not found: ${sessionId}`);
|
|
6352
|
+
return state;
|
|
6353
|
+
}
|
|
6354
|
+
function toSummary(state) {
|
|
6355
|
+
return {
|
|
6356
|
+
createdAt: state.createdAt,
|
|
6357
|
+
displayName: state.displayName,
|
|
6358
|
+
phase: state.phase,
|
|
6359
|
+
sessionId: state.sessionId,
|
|
6360
|
+
stepCount: state.stepCount,
|
|
6361
|
+
updatedAt: state.updatedAt,
|
|
6362
|
+
...state.wakeAt ? { wakeAt: state.wakeAt } : {}
|
|
6363
|
+
};
|
|
6364
|
+
}
|
|
6365
|
+
function toDetail(state) {
|
|
6366
|
+
return {
|
|
6367
|
+
...toSummary(state),
|
|
6368
|
+
...state.cancellation ? { cancellationReason: state.cancellation.reason } : {},
|
|
6369
|
+
...state.errorMessage ? { errorMessage: state.errorMessage } : {},
|
|
6370
|
+
parentRunId: state.parentRunId,
|
|
6371
|
+
pendingInput: state.pendingInput,
|
|
6372
|
+
...state.reconciliationNote ? { reconciliationNote: state.reconciliationNote } : {},
|
|
6373
|
+
...state.result ? { result: state.result } : {}
|
|
6374
|
+
};
|
|
6375
|
+
}
|
|
6376
|
+
function stopNotice$1(state) {
|
|
6377
|
+
return `后台会话「${state.displayName}」已停止。${state.cancellation ? `原因:${state.cancellation.reason}` : ""}`.trim();
|
|
6378
|
+
}
|
|
6379
|
+
//#endregion
|
|
6380
|
+
//#region src/application/background-session/background-session-repository.ts
|
|
6381
|
+
var BackgroundSessionRepositoryConflict = class extends Error {
|
|
6382
|
+
name = "BackgroundSessionRepositoryConflict";
|
|
6383
|
+
constructor(sessionId, message) {
|
|
6384
|
+
super(`${sessionId}: ${message}`);
|
|
6385
|
+
}
|
|
6386
|
+
};
|
|
6387
|
+
var BackgroundSessionRepositoryCorrupted = class extends Error {
|
|
6388
|
+
name = "BackgroundSessionRepositoryCorrupted";
|
|
6389
|
+
};
|
|
6390
|
+
function createBackgroundSessionRepository(options = {}) {
|
|
6391
|
+
const serial = createSerialExecutor();
|
|
6392
|
+
const states = /* @__PURE__ */ new Map();
|
|
6393
|
+
for (const state of options.initial ?? []) states.set(state.sessionId, structuredClone(state));
|
|
6394
|
+
const persist = async (state) => {
|
|
6395
|
+
if (options.persist) await options.persist(state);
|
|
6396
|
+
};
|
|
6397
|
+
return {
|
|
6398
|
+
create: (state) => serial.run(async () => {
|
|
6399
|
+
if (states.has(state.sessionId)) throw new BackgroundSessionRepositoryConflict(state.sessionId, "duplicate background session id");
|
|
6400
|
+
states.set(state.sessionId, structuredClone(state));
|
|
6401
|
+
await persist(state);
|
|
6402
|
+
return structuredClone(state);
|
|
6403
|
+
}),
|
|
6404
|
+
get: (sessionId) => serial.run(async () => {
|
|
6405
|
+
const state = states.get(sessionId);
|
|
6406
|
+
return state ? structuredClone(state) : void 0;
|
|
6407
|
+
}),
|
|
6408
|
+
list: (options) => serial.run(async () => {
|
|
6409
|
+
let results = [...states.values()];
|
|
6410
|
+
if (options?.phase !== void 0) results = results.filter((state) => state.phase === options.phase);
|
|
6411
|
+
results.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
6412
|
+
if (options?.limit !== void 0) results = results.slice(0, options.limit);
|
|
6413
|
+
return results.map((state) => structuredClone(state));
|
|
6414
|
+
}),
|
|
6415
|
+
update: (input) => serial.run(async () => {
|
|
6416
|
+
const previous = states.get(input.sessionId);
|
|
6417
|
+
if (!previous) return void 0;
|
|
6418
|
+
if (previous.revision !== input.expectedRevision) throw new BackgroundSessionRepositoryConflict(input.sessionId, `revision conflict: expected ${input.expectedRevision}, found ${previous.revision}`);
|
|
6419
|
+
if (input.expectedLease !== void 0) {
|
|
6420
|
+
const leaseMismatch = leaseConflict(input.sessionId, previous.lease, input.expectedLease, input.now);
|
|
6421
|
+
if (leaseMismatch) throw new BackgroundSessionRepositoryConflict(input.sessionId, leaseMismatch);
|
|
6422
|
+
}
|
|
6423
|
+
const nextState = input.build(previous);
|
|
6424
|
+
const invalid = isValidBackgroundSessionTransition(previous, nextState);
|
|
6425
|
+
if (invalid) throw new BackgroundSessionRepositoryConflict(input.sessionId, `invalid transition: ${invalid}`);
|
|
6426
|
+
states.set(input.sessionId, structuredClone(nextState));
|
|
6427
|
+
await persist(nextState);
|
|
6428
|
+
return structuredClone(nextState);
|
|
6429
|
+
})
|
|
6430
|
+
};
|
|
6431
|
+
}
|
|
6432
|
+
function leaseConflict(_sessionId, lease, expected, now) {
|
|
6433
|
+
if (expected.kind === "absent") {
|
|
6434
|
+
if (lease !== void 0) return `lease conflict: expected no lease, found owner ${lease.owner} epoch ${lease.epoch}`;
|
|
6435
|
+
return;
|
|
6436
|
+
}
|
|
6437
|
+
if (expected.kind === "absent-or-stale") {
|
|
6438
|
+
if (lease !== void 0 && !isExpired(lease, now)) return `lease conflict: expected absent or stale lease, found live lease owner ${lease.owner} epoch ${lease.epoch}`;
|
|
6439
|
+
return;
|
|
6440
|
+
}
|
|
6441
|
+
if (lease === void 0 || lease.owner !== expected.owner || lease.epoch !== expected.epoch) return `lease conflict: expected owner ${expected.owner} epoch ${expected.epoch}`;
|
|
6442
|
+
}
|
|
6443
|
+
function isExpired(lease, now) {
|
|
6444
|
+
return lease !== void 0 && lease.expiresAt <= (now ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
6445
|
+
}
|
|
6446
|
+
//#endregion
|
|
6447
|
+
//#region src/application/background-session/background-session-delivery-store.ts
|
|
6448
|
+
var BackgroundSessionDeliveryConflict = class extends Error {
|
|
6449
|
+
name = "BackgroundSessionDeliveryConflict";
|
|
6450
|
+
constructor(deliveryId, message) {
|
|
6451
|
+
super(`${deliveryId}: ${message}`);
|
|
6452
|
+
}
|
|
6453
|
+
};
|
|
6454
|
+
function createBackgroundSessionDeliveryStore(options = {}) {
|
|
6455
|
+
const serial = createSerialExecutor();
|
|
6456
|
+
const records = /* @__PURE__ */ new Map();
|
|
6457
|
+
for (const record of options.initial ?? []) records.set(record.deliveryId, structuredClone(record));
|
|
6458
|
+
const persist = async (record) => {
|
|
6459
|
+
if (options.persist) await options.persist(record);
|
|
6460
|
+
};
|
|
6461
|
+
return {
|
|
6462
|
+
enqueue: (input) => serial.run(async () => {
|
|
6463
|
+
const existing = records.get(input.deliveryId);
|
|
6464
|
+
if (existing) return structuredClone(existing);
|
|
6465
|
+
const record = {
|
|
6466
|
+
createdAt: input.createdAt,
|
|
6467
|
+
deliveryId: input.deliveryId,
|
|
6468
|
+
kind: input.kind,
|
|
6469
|
+
revision: 1,
|
|
6470
|
+
sessionId: input.sessionId,
|
|
6471
|
+
state: "pending",
|
|
6472
|
+
text: input.text
|
|
6473
|
+
};
|
|
6474
|
+
records.set(input.deliveryId, structuredClone(record));
|
|
6475
|
+
await persist(record);
|
|
6476
|
+
return structuredClone(record);
|
|
6477
|
+
}),
|
|
6478
|
+
pending: () => serial.run(async () => {
|
|
6479
|
+
return [...records.values()].filter((record) => record.state === "pending").sort((left, right) => left.createdAt.localeCompare(right.createdAt)).map((record) => structuredClone(record));
|
|
6480
|
+
}),
|
|
6481
|
+
markDelivered: (deliveryId, providerMessageId) => serial.run(async () => {
|
|
6482
|
+
const previous = records.get(deliveryId);
|
|
6483
|
+
if (!previous) return void 0;
|
|
6484
|
+
if (previous.state !== "pending") return structuredClone(previous);
|
|
6485
|
+
const nextState = {
|
|
6486
|
+
...previous,
|
|
6487
|
+
providerMessageId,
|
|
6488
|
+
revision: previous.revision + 1,
|
|
6489
|
+
state: "delivered"
|
|
6490
|
+
};
|
|
6491
|
+
records.set(deliveryId, structuredClone(nextState));
|
|
6492
|
+
await persist(nextState);
|
|
6493
|
+
return structuredClone(nextState);
|
|
6494
|
+
})
|
|
6495
|
+
};
|
|
6496
|
+
}
|
|
6497
|
+
//#endregion
|
|
6498
|
+
//#region src/application/background-session/background-session-supervisor.ts
|
|
6499
|
+
function createBackgroundSessionSupervisor(options) {
|
|
6500
|
+
const owner = `supervisor:${randomUUID()}`;
|
|
6501
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
6502
|
+
const stepControllers = /* @__PURE__ */ new Map();
|
|
6503
|
+
const counters = {
|
|
6504
|
+
completed: 0,
|
|
6505
|
+
deliveriesDelivered: 0,
|
|
6506
|
+
failed: 0,
|
|
6507
|
+
started: 0,
|
|
6508
|
+
staleLeaseEventsDropped: 0,
|
|
6509
|
+
stopped: 0
|
|
6510
|
+
};
|
|
6511
|
+
let repositoryHealthy = true;
|
|
6512
|
+
let snapshot = emptyPhaseCounts();
|
|
6513
|
+
let oldestDueAt;
|
|
6514
|
+
const requeuedInPass = /* @__PURE__ */ new Set();
|
|
6515
|
+
const refreshSnapshot = async () => {
|
|
6516
|
+
const counts = {
|
|
6517
|
+
completed: 0,
|
|
6518
|
+
failed: 0,
|
|
6519
|
+
"input-required": 0,
|
|
6520
|
+
queued: 0,
|
|
6521
|
+
"reconciliation-required": 0,
|
|
6522
|
+
running: 0,
|
|
6523
|
+
stopped: 0,
|
|
6524
|
+
stopping: 0,
|
|
6525
|
+
waiting: 0
|
|
6526
|
+
};
|
|
6527
|
+
let oldest;
|
|
6528
|
+
const now = options.clock.now();
|
|
6529
|
+
for (const session of await options.repository.list()) {
|
|
6530
|
+
counts[session.phase] += 1;
|
|
6531
|
+
if (isBackgroundSessionDue(session, now)) {
|
|
6532
|
+
const dueAt = session.wakeAt ?? session.createdAt;
|
|
6533
|
+
if (oldest === void 0 || dueAt < oldest) oldest = dueAt;
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
snapshot = Object.freeze(counts);
|
|
6537
|
+
oldestDueAt = oldest;
|
|
6538
|
+
};
|
|
6539
|
+
const supervisionPass = async () => {
|
|
6540
|
+
try {
|
|
6541
|
+
await resolveStaleLeases();
|
|
6542
|
+
await expireLifetime();
|
|
6543
|
+
await stopStoppingSessions();
|
|
6544
|
+
await drainDeliveries();
|
|
6545
|
+
await startDueSessions();
|
|
6546
|
+
await refreshSnapshot();
|
|
6547
|
+
repositoryHealthy = true;
|
|
6548
|
+
} catch (error) {
|
|
6549
|
+
repositoryHealthy = false;
|
|
6550
|
+
throw error;
|
|
6551
|
+
}
|
|
6552
|
+
};
|
|
6553
|
+
const resolveStaleLeases = async () => {
|
|
6554
|
+
const now = options.clock.now();
|
|
6555
|
+
for (const session of await options.repository.list()) {
|
|
6556
|
+
if (session.lease === void 0 || inFlight.has(session.sessionId)) continue;
|
|
6557
|
+
if (!(session.lease.owner !== owner || isBackgroundSessionLeaseExpired(session, now))) continue;
|
|
6558
|
+
counters.staleLeaseEventsDropped += 1;
|
|
6559
|
+
const expectedLease = {
|
|
6560
|
+
kind: "held",
|
|
6561
|
+
owner: session.lease.owner,
|
|
6562
|
+
epoch: session.lease.epoch
|
|
6563
|
+
};
|
|
6564
|
+
if (session.phase === "running") {
|
|
6565
|
+
await options.repository.update({
|
|
6566
|
+
build: (state) => requeueInterruptedBackgroundSessionStep(state, {
|
|
6567
|
+
now,
|
|
6568
|
+
wakeText: interruptedStepWakeText(state)
|
|
6569
|
+
}),
|
|
6570
|
+
expectedLease,
|
|
6571
|
+
expectedRevision: session.revision,
|
|
6572
|
+
sessionId: session.sessionId
|
|
6573
|
+
});
|
|
6574
|
+
requeuedInPass.add(session.sessionId);
|
|
6575
|
+
continue;
|
|
6576
|
+
}
|
|
6577
|
+
if (session.phase === "stopping") {
|
|
6578
|
+
await completeStop(session, expectedLease);
|
|
6579
|
+
continue;
|
|
6580
|
+
}
|
|
6581
|
+
await options.repository.update({
|
|
6582
|
+
build: (state) => releaseBackgroundSessionLease(state),
|
|
6583
|
+
expectedLease,
|
|
6584
|
+
expectedRevision: session.revision,
|
|
6585
|
+
sessionId: session.sessionId
|
|
6586
|
+
});
|
|
6587
|
+
}
|
|
6588
|
+
};
|
|
6589
|
+
const expireLifetime = async () => {
|
|
6590
|
+
const now = options.clock.now();
|
|
6591
|
+
const cutoff = Date.parse(now) - options.config.sessionLifetimeMs;
|
|
6592
|
+
for (const session of await options.repository.list()) {
|
|
6593
|
+
if (isBackgroundSessionTerminalPhase(session.phase)) continue;
|
|
6594
|
+
if (Date.parse(session.createdAt) > cutoff) continue;
|
|
6595
|
+
const nextState = requestBackgroundSessionStop(session, {
|
|
6596
|
+
now,
|
|
6597
|
+
reason: "background session lifetime exceeded"
|
|
6598
|
+
});
|
|
6599
|
+
const stored = await options.repository.update({
|
|
6600
|
+
build: () => nextState,
|
|
6601
|
+
expectedRevision: session.revision,
|
|
6602
|
+
sessionId: session.sessionId
|
|
6603
|
+
});
|
|
6604
|
+
if (stored?.phase === "stopped") {
|
|
6605
|
+
counters.stopped += 1;
|
|
6606
|
+
await enqueueDelivery(stored.sessionId, "stopped", stopNotice(stored), stored);
|
|
6607
|
+
} else stepControllers.get(session.sessionId)?.abort(/* @__PURE__ */ new Error("background session lifetime exceeded"));
|
|
6608
|
+
}
|
|
6609
|
+
};
|
|
6610
|
+
const stopStoppingSessions = async () => {
|
|
6611
|
+
for (const session of await options.repository.list()) {
|
|
6612
|
+
if (session.phase !== "stopping") continue;
|
|
6613
|
+
if (inFlight.has(session.sessionId)) {
|
|
6614
|
+
stepControllers.get(session.sessionId)?.abort(/* @__PURE__ */ new Error("background session stop requested"));
|
|
6615
|
+
continue;
|
|
6616
|
+
}
|
|
6617
|
+
const expectedLease = session.lease ? {
|
|
6618
|
+
kind: "held",
|
|
6619
|
+
owner: session.lease.owner,
|
|
6620
|
+
epoch: session.lease.epoch
|
|
6621
|
+
} : { kind: "absent" };
|
|
6622
|
+
await completeStop(session, expectedLease);
|
|
6623
|
+
}
|
|
6624
|
+
};
|
|
6625
|
+
const completeStop = async (state, expectedLease) => {
|
|
6626
|
+
const stopped = await options.repository.update({
|
|
6627
|
+
build: (current) => completeBackgroundSessionStop(current, { now: options.clock.now() }),
|
|
6628
|
+
expectedLease,
|
|
6629
|
+
expectedRevision: state.revision,
|
|
6630
|
+
sessionId: state.sessionId
|
|
6631
|
+
});
|
|
6632
|
+
if (stopped) {
|
|
6633
|
+
counters.stopped += 1;
|
|
6634
|
+
await enqueueDelivery(stopped.sessionId, "stopped", stopNotice(stopped), stopped);
|
|
6635
|
+
}
|
|
6636
|
+
};
|
|
6637
|
+
const startDueSessions = async () => {
|
|
6638
|
+
const now = options.clock.now();
|
|
6639
|
+
for (const session of await options.repository.list()) {
|
|
6640
|
+
if (inFlight.size >= options.config.maxConcurrentSessions) break;
|
|
6641
|
+
if (inFlight.has(session.sessionId)) continue;
|
|
6642
|
+
if (!isBackgroundSessionDue(session, now)) continue;
|
|
6643
|
+
if (session.lease !== void 0 && !isBackgroundSessionLeaseExpired(session, now)) continue;
|
|
6644
|
+
const step = runStep(session).finally(() => {
|
|
6645
|
+
inFlight.delete(session.sessionId);
|
|
6646
|
+
});
|
|
6647
|
+
inFlight.set(session.sessionId, step);
|
|
6648
|
+
step.catch((error) => {
|
|
6649
|
+
options.onError?.(error);
|
|
6650
|
+
});
|
|
6651
|
+
}
|
|
6652
|
+
};
|
|
6653
|
+
const runStep = async (session) => {
|
|
6654
|
+
const now = options.clock.now();
|
|
6655
|
+
const stepCount = session.stepCount + 1;
|
|
6656
|
+
const wakeText = requeuedInPass.has(session.sessionId) ? interruptedStepWakeText(session) : buildWakeText(session);
|
|
6657
|
+
requeuedInPass.delete(session.sessionId);
|
|
6658
|
+
const lease = {
|
|
6659
|
+
epoch: (session.lease?.epoch ?? 0) + 1,
|
|
6660
|
+
expiresAt: toIso(Date.parse(now) + options.config.leaseMs),
|
|
6661
|
+
owner
|
|
6662
|
+
};
|
|
6663
|
+
const claimed = await options.repository.update({
|
|
6664
|
+
build: (state) => claimBackgroundSession(state, {
|
|
6665
|
+
lease,
|
|
6666
|
+
now,
|
|
6667
|
+
stepRunId: `bg-step:${session.sessionId}:${stepCount}`,
|
|
6668
|
+
wakeText
|
|
6669
|
+
}),
|
|
6670
|
+
expectedLease: { kind: "absent-or-stale" },
|
|
6671
|
+
expectedRevision: session.revision,
|
|
6672
|
+
now,
|
|
6673
|
+
sessionId: session.sessionId
|
|
6674
|
+
});
|
|
6675
|
+
if (!claimed) return;
|
|
6676
|
+
counters.started += 1;
|
|
6677
|
+
const controller = new AbortController();
|
|
6678
|
+
stepControllers.set(session.sessionId, controller);
|
|
6679
|
+
let outcome;
|
|
6680
|
+
try {
|
|
6681
|
+
let settled = false;
|
|
6682
|
+
const renewal = (async () => {
|
|
6683
|
+
while (!settled) {
|
|
6684
|
+
await Effect.runPromise(options.sleep(options.config.leaseRenewalIntervalMs)).catch(() => void 0);
|
|
6685
|
+
if (settled) break;
|
|
6686
|
+
try {
|
|
6687
|
+
const current = await options.repository.get(session.sessionId);
|
|
6688
|
+
if (!current || current.lease === void 0 || current.lease.owner !== owner || current.lease.epoch !== lease.epoch) {
|
|
6689
|
+
controller.abort(/* @__PURE__ */ new Error("background session lease was lost"));
|
|
6690
|
+
break;
|
|
6691
|
+
}
|
|
6692
|
+
if (!await options.repository.update({
|
|
6693
|
+
build: (state) => renewBackgroundSessionLease(state, {
|
|
6694
|
+
expiresAt: toIso(Date.parse(options.clock.now()) + options.config.leaseMs),
|
|
6695
|
+
epoch: lease.epoch,
|
|
6696
|
+
owner
|
|
6697
|
+
}),
|
|
6698
|
+
expectedLease: {
|
|
6699
|
+
kind: "held",
|
|
6700
|
+
owner,
|
|
6701
|
+
epoch: lease.epoch
|
|
6702
|
+
},
|
|
6703
|
+
expectedRevision: current.revision,
|
|
6704
|
+
sessionId: session.sessionId
|
|
6705
|
+
})) controller.abort(/* @__PURE__ */ new Error("background session lease renewal was denied"));
|
|
6706
|
+
} catch {
|
|
6707
|
+
controller.abort(/* @__PURE__ */ new Error("background session lease renewal failed"));
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6710
|
+
})();
|
|
6711
|
+
outcome = await options.runStep({
|
|
6712
|
+
session: claimed,
|
|
6713
|
+
signal: controller.signal,
|
|
6714
|
+
wakeText
|
|
6715
|
+
}).then((result) => ({
|
|
6716
|
+
ok: true,
|
|
6717
|
+
result
|
|
6718
|
+
}), (error) => ({
|
|
6719
|
+
error,
|
|
6720
|
+
ok: false
|
|
6721
|
+
}));
|
|
6722
|
+
settled = true;
|
|
6723
|
+
renewal.catch(() => void 0);
|
|
6724
|
+
} finally {
|
|
6725
|
+
stepControllers.delete(session.sessionId);
|
|
6726
|
+
}
|
|
6727
|
+
try {
|
|
6728
|
+
await commitStep(claimed, outcome, lease);
|
|
6729
|
+
} catch (error) {
|
|
6730
|
+
options.onError?.(error);
|
|
6731
|
+
await requeueAfterFailedCommit(session.sessionId, lease, error);
|
|
6732
|
+
}
|
|
6733
|
+
};
|
|
6734
|
+
const requeueAfterFailedCommit = async (sessionId, lease, error) => {
|
|
6735
|
+
try {
|
|
6736
|
+
const current = await options.repository.get(sessionId);
|
|
6737
|
+
if (!current || current.phase !== "running") return;
|
|
6738
|
+
await options.repository.update({
|
|
6739
|
+
build: (state) => requeueInterruptedBackgroundSessionStep(state, {
|
|
6740
|
+
now: options.clock.now(),
|
|
6741
|
+
wakeText: `步骤结果提交失败(${errorMessageOf(error)}),将重试`
|
|
6742
|
+
}),
|
|
6743
|
+
expectedLease: {
|
|
6744
|
+
kind: "held",
|
|
6745
|
+
owner: lease.owner,
|
|
6746
|
+
epoch: lease.epoch
|
|
6747
|
+
},
|
|
6748
|
+
expectedRevision: current.revision,
|
|
6749
|
+
sessionId
|
|
6750
|
+
});
|
|
6751
|
+
} catch (commitError) {
|
|
6752
|
+
options.onError?.(commitError);
|
|
6753
|
+
}
|
|
6754
|
+
};
|
|
6755
|
+
const commitStep = async (claimed, outcome, lease) => {
|
|
6756
|
+
const now = options.clock.now();
|
|
6757
|
+
const current = await options.repository.get(claimed.sessionId);
|
|
6758
|
+
if (!current) return;
|
|
6759
|
+
const expectedLease = {
|
|
6760
|
+
kind: "held",
|
|
6761
|
+
owner: lease.owner,
|
|
6762
|
+
epoch: lease.epoch
|
|
6763
|
+
};
|
|
6764
|
+
if (current.phase === "waiting" || current.phase === "input-required") {
|
|
6765
|
+
const text = outcome.ok ? outcome.result.finalText : "";
|
|
6766
|
+
await options.repository.update({
|
|
6767
|
+
build: (state) => releaseBackgroundSessionLease(state),
|
|
6768
|
+
expectedLease,
|
|
6769
|
+
expectedRevision: current.revision,
|
|
6770
|
+
sessionId: current.sessionId
|
|
6771
|
+
});
|
|
6772
|
+
if (text.trim() !== "") await enqueueDelivery(current.sessionId, "progress", text, current);
|
|
6773
|
+
return;
|
|
6774
|
+
}
|
|
6775
|
+
if (current.phase === "stopping") {
|
|
6776
|
+
await completeStop(current, expectedLease);
|
|
6777
|
+
return;
|
|
6778
|
+
}
|
|
6779
|
+
if (current.phase !== "running") return;
|
|
6780
|
+
if (outcome.ok) {
|
|
6781
|
+
const completed = await options.repository.update({
|
|
6782
|
+
build: (state) => completeBackgroundSessionStep(state, {
|
|
6783
|
+
now,
|
|
6784
|
+
stepRunId: outcome.result.runId,
|
|
6785
|
+
text: outcome.result.finalText
|
|
6786
|
+
}),
|
|
6787
|
+
expectedLease,
|
|
6788
|
+
expectedRevision: current.revision,
|
|
6789
|
+
sessionId: current.sessionId
|
|
6790
|
+
});
|
|
6791
|
+
if (completed) {
|
|
6792
|
+
counters.completed += 1;
|
|
6793
|
+
await enqueueDelivery(completed.sessionId, "final", completed.result?.text ?? "", completed);
|
|
6794
|
+
}
|
|
6795
|
+
return;
|
|
6796
|
+
}
|
|
6797
|
+
const errorMessage = errorMessageOf(outcome.error);
|
|
6798
|
+
if (isFencedOperationError(outcome.error)) {
|
|
6799
|
+
const parked = await options.repository.update({
|
|
6800
|
+
build: (state) => parkBackgroundSessionForReconciliation(state, {
|
|
6801
|
+
now,
|
|
6802
|
+
reason: `step failed on a fenced operation: ${errorMessage}`
|
|
6803
|
+
}),
|
|
6804
|
+
expectedLease,
|
|
6805
|
+
expectedRevision: current.revision,
|
|
6806
|
+
sessionId: current.sessionId
|
|
6807
|
+
});
|
|
6808
|
+
if (parked) await enqueueDelivery(parked.sessionId, "reconciliation", `后台会话「${parked.displayName}」需要人工对账:${parked.reconciliationNote ?? errorMessage}`, parked);
|
|
6809
|
+
return;
|
|
6810
|
+
}
|
|
6811
|
+
if (isRetryableStepError(outcome.error) && current.consecutiveFailures + 1 < options.config.maxConsecutiveFailures) {
|
|
6812
|
+
const backoff = options.config.retryBackoffMs * 2 ** current.consecutiveFailures;
|
|
6813
|
+
await options.repository.update({
|
|
6814
|
+
build: (state) => failBackgroundSessionStep(state, {
|
|
6815
|
+
errorMessage,
|
|
6816
|
+
now,
|
|
6817
|
+
retryable: true,
|
|
6818
|
+
wakeAt: toIso(Date.parse(now) + backoff)
|
|
6819
|
+
}),
|
|
6820
|
+
expectedLease,
|
|
6821
|
+
expectedRevision: current.revision,
|
|
6822
|
+
sessionId: current.sessionId
|
|
6823
|
+
});
|
|
6824
|
+
return;
|
|
6825
|
+
}
|
|
6826
|
+
const failed = await options.repository.update({
|
|
6827
|
+
build: (state) => failBackgroundSessionStep(state, {
|
|
6828
|
+
errorMessage,
|
|
6829
|
+
now,
|
|
6830
|
+
retryable: false
|
|
6831
|
+
}),
|
|
6832
|
+
expectedLease,
|
|
6833
|
+
expectedRevision: current.revision,
|
|
6834
|
+
sessionId: current.sessionId
|
|
6835
|
+
});
|
|
6836
|
+
if (failed) {
|
|
6837
|
+
counters.failed += 1;
|
|
6838
|
+
await enqueueDelivery(failed.sessionId, "failed", `后台会话「${failed.displayName}」失败:${failed.errorMessage ?? errorMessage}`, failed);
|
|
6839
|
+
}
|
|
6840
|
+
};
|
|
6841
|
+
const drainDeliveries = async () => {
|
|
6842
|
+
const pending = await options.deliveries.pending();
|
|
6843
|
+
for (const record of pending) try {
|
|
6844
|
+
const { providerMessageId } = await options.deliver(record);
|
|
6845
|
+
if (await options.deliveries.markDelivered(record.deliveryId, providerMessageId)) counters.deliveriesDelivered += 1;
|
|
6846
|
+
} catch (error) {
|
|
6847
|
+
options.onError?.(error);
|
|
6848
|
+
}
|
|
6849
|
+
};
|
|
6850
|
+
const enqueueDelivery = async (sessionId, kind, text, session) => {
|
|
6851
|
+
if (text.trim() === "") return;
|
|
6852
|
+
const deliveryId = kind === "progress" ? progressDeliveryId(sessionId, session.stepCount) : terminalDeliveryId(sessionId, kind);
|
|
6853
|
+
await options.deliveries.enqueue({
|
|
6854
|
+
createdAt: options.clock.now(),
|
|
6855
|
+
deliveryId,
|
|
6856
|
+
kind,
|
|
6857
|
+
sessionId,
|
|
6858
|
+
text
|
|
6859
|
+
});
|
|
6860
|
+
};
|
|
6861
|
+
const buildWakeText = (session) => {
|
|
6862
|
+
if (session.wakeCount === 0) return `你在一个后台会话中开始工作。任务:\n${session.prompt}\n\n继续工作直到给出最终结果;如果必须等待外部变化或用户输入,调用 background.wait。`;
|
|
6863
|
+
const parts = [`后台会话第 ${session.stepCount + 1} 步。任务:\n${session.prompt}`, "继续先前的工作(历史在会话记录中)。"];
|
|
6864
|
+
if (session.pendingInput.length > 0) parts.push(`用户新的输入:\n${session.pendingInput.join("\n---\n")}`);
|
|
6865
|
+
parts.push("完成后给出最终结果;需要等待时调用 background.wait。");
|
|
6866
|
+
return parts.join("\n\n");
|
|
6867
|
+
};
|
|
6868
|
+
const interruptedStepWakeText = (session) => `你的上一步在后台会话中被守护进程重启打断,未产生确定的工具副作用(副作用由工具账本保护)。继续先前的工作(历史在会话记录中)。` + (session.pendingInput.length > 0 ? `\n\n用户新的输入:\n${session.pendingInput.join("\n---\n")}` : "");
|
|
6869
|
+
const tick = () => Effect.promise(() => supervisionPass());
|
|
6870
|
+
const loop = createPeriodicEffectLoop({
|
|
6871
|
+
intervalMs: options.config.intervalMs,
|
|
6872
|
+
onError: (error) => {
|
|
6873
|
+
options.onError?.(error);
|
|
6874
|
+
},
|
|
6875
|
+
run: tick,
|
|
6876
|
+
sleep: options.sleep
|
|
6877
|
+
});
|
|
6878
|
+
const statusOf = () => Object.freeze({
|
|
6879
|
+
capacity: options.config.maxConcurrentSessions,
|
|
6880
|
+
counters: Object.freeze({ ...counters }),
|
|
6881
|
+
inFlightSteps: inFlight.size,
|
|
6882
|
+
...oldestDueAt === void 0 ? {} : { oldestDueAt },
|
|
6883
|
+
owner,
|
|
6884
|
+
phaseCounts: snapshot,
|
|
6885
|
+
reconciliationCount: snapshot["reconciliation-required"],
|
|
6886
|
+
repositoryHealthy,
|
|
6887
|
+
running: loop.running()
|
|
6888
|
+
});
|
|
6889
|
+
return {
|
|
6890
|
+
recover: tick,
|
|
6891
|
+
start: () => Effect.sync(() => loop.start()),
|
|
6892
|
+
status: statusOf,
|
|
6893
|
+
stop: () => Effect.promise(async () => {
|
|
6894
|
+
for (const controller of stepControllers.values()) controller.abort(/* @__PURE__ */ new Error("background session supervisor is stopping"));
|
|
6895
|
+
await Promise.allSettled(inFlight.values());
|
|
6896
|
+
await loop.stop();
|
|
6897
|
+
}),
|
|
6898
|
+
tick
|
|
6899
|
+
};
|
|
6900
|
+
}
|
|
6901
|
+
function isFencedOperationError(error) {
|
|
6902
|
+
const message = errorMessageOf(error).toLowerCase();
|
|
6903
|
+
return message.includes("fenced") || message.includes("reconciliation-required");
|
|
6904
|
+
}
|
|
6905
|
+
function isRetryableStepError(error) {
|
|
6906
|
+
if (!(error instanceof Error)) return false;
|
|
6907
|
+
if (error.name === "AgentRunCancelled") return true;
|
|
6908
|
+
if (error.name === "AgentHarnessBusy" || error.name === "AgentInstanceBusy") return true;
|
|
6909
|
+
return false;
|
|
6910
|
+
}
|
|
6911
|
+
function errorMessageOf(error) {
|
|
6912
|
+
return error instanceof Error ? error.message : String(error);
|
|
6913
|
+
}
|
|
6914
|
+
function toIso(timeMs) {
|
|
6915
|
+
return new Date(timeMs).toISOString();
|
|
6916
|
+
}
|
|
6917
|
+
function stopNotice(session) {
|
|
6918
|
+
return `后台会话「${session.displayName}」已停止。${session.cancellation ? `原因:${session.cancellation.reason}` : ""}`.trim();
|
|
6919
|
+
}
|
|
6920
|
+
function emptyPhaseCounts() {
|
|
6921
|
+
return Object.freeze({
|
|
6922
|
+
completed: 0,
|
|
6923
|
+
failed: 0,
|
|
6924
|
+
"input-required": 0,
|
|
6925
|
+
queued: 0,
|
|
6926
|
+
"reconciliation-required": 0,
|
|
6927
|
+
running: 0,
|
|
6928
|
+
stopped: 0,
|
|
6929
|
+
stopping: 0,
|
|
6930
|
+
waiting: 0
|
|
6931
|
+
});
|
|
6932
|
+
}
|
|
6933
|
+
//#endregion
|
|
6934
|
+
//#region src/application/background-session/background-session-tools.ts
|
|
6935
|
+
function createBackgroundSessionHostTools(options) {
|
|
6936
|
+
const contracts = createBackgroundSessionToolContracts();
|
|
6937
|
+
const executors = new Map(contracts.map((contract) => [contract.id, createExecutor(contract, options)]));
|
|
6938
|
+
return contracts.map((contract) => Object.freeze({
|
|
6939
|
+
...contract,
|
|
6940
|
+
createExecutor: () => ({ execute: (input, context) => asToolInputRejection$1(() => executors.get(contract.id)(input, context)) }),
|
|
6941
|
+
replayCompleted: (input, completedResult, context) => asToolInputRejection$1(() => replayCompleted(contract.id, input, completedResult, context, options))
|
|
6942
|
+
}));
|
|
6943
|
+
}
|
|
6944
|
+
function createExecutor(contract, options) {
|
|
6945
|
+
switch (contract.id) {
|
|
6946
|
+
case "background.start": return (input, context) => {
|
|
6947
|
+
const { prompt, displayName } = readObject(input, ["prompt", "displayName"]);
|
|
6948
|
+
if (typeof prompt !== "string" || prompt.trim() === "") throw new BackgroundSessionCallerDenied("background.start requires a non-empty prompt");
|
|
6949
|
+
if (displayName !== void 0 && typeof displayName !== "string") throw new BackgroundSessionCallerDenied("background.start displayName must be a string");
|
|
6950
|
+
const sessionId = options.createSessionId();
|
|
6951
|
+
return options.service.start({
|
|
6952
|
+
authority: buildAuthority(options.definition, context, sessionId),
|
|
6953
|
+
context,
|
|
6954
|
+
sessionId,
|
|
6955
|
+
...displayName === void 0 ? {} : { displayName },
|
|
6956
|
+
prompt
|
|
6957
|
+
});
|
|
6958
|
+
};
|
|
6959
|
+
case "background.wait": return (input, context) => {
|
|
6960
|
+
const { delayMs, reason, until } = readWaitInput(input);
|
|
6961
|
+
return options.service.wait({
|
|
6962
|
+
context,
|
|
6963
|
+
...delayMs === void 0 ? {} : { delayMs },
|
|
6964
|
+
...reason === void 0 ? {} : { reason },
|
|
6965
|
+
...until === void 0 ? {} : { until }
|
|
6966
|
+
});
|
|
6967
|
+
};
|
|
6968
|
+
case "background.list": return async (input, context) => {
|
|
6969
|
+
const { limit, phase } = readObject(input, ["limit", "phase"]);
|
|
6970
|
+
return { sessions: await options.service.list({
|
|
6971
|
+
context,
|
|
6972
|
+
...limit === void 0 ? {} : { limit: readInteger(limit, "limit") },
|
|
6973
|
+
...phase === void 0 ? {} : { phase: readPhase(phase) }
|
|
6974
|
+
}) };
|
|
6975
|
+
};
|
|
6976
|
+
case "background.status": return (input, context) => {
|
|
6977
|
+
const { sessionId } = readObject(input, ["sessionId"]);
|
|
6978
|
+
return options.service.status({
|
|
6979
|
+
context,
|
|
6980
|
+
sessionId: readString(sessionId, "sessionId")
|
|
6981
|
+
});
|
|
6982
|
+
};
|
|
6983
|
+
case "background.send": return (input, context) => {
|
|
6984
|
+
const { message, sessionId } = readObject(input, ["message", "sessionId"]);
|
|
6985
|
+
return options.service.send({
|
|
6986
|
+
context,
|
|
6987
|
+
message: readString(message, "message"),
|
|
6988
|
+
sessionId: readString(sessionId, "sessionId")
|
|
6989
|
+
});
|
|
6990
|
+
};
|
|
6991
|
+
case "background.stop": return (input, context) => {
|
|
6992
|
+
const { reason, sessionId } = readObject(input, ["reason", "sessionId"]);
|
|
6993
|
+
return options.service.stop({
|
|
6994
|
+
context,
|
|
6995
|
+
...reason === void 0 ? {} : { reason: readString(reason, "reason") },
|
|
6996
|
+
sessionId: readString(sessionId, "sessionId")
|
|
6997
|
+
});
|
|
6998
|
+
};
|
|
6999
|
+
default: throw new BackgroundSessionCallerDenied(`unknown background tool: ${contract.id}`);
|
|
7000
|
+
}
|
|
7001
|
+
}
|
|
7002
|
+
function replayCompleted(toolId, input, completedResult, context, options) {
|
|
7003
|
+
switch (toolId) {
|
|
7004
|
+
case "background.start": {
|
|
7005
|
+
const sessionId = readReplayedSessionId(completedResult);
|
|
7006
|
+
return options.service.status({
|
|
7007
|
+
context,
|
|
7008
|
+
sessionId
|
|
7009
|
+
});
|
|
7010
|
+
}
|
|
7011
|
+
case "background.wait": return completedResult;
|
|
7012
|
+
case "background.list": return completedResult;
|
|
7013
|
+
case "background.status": {
|
|
7014
|
+
const sessionId = readReplayedSessionId(input);
|
|
7015
|
+
return options.service.status({
|
|
7016
|
+
context,
|
|
7017
|
+
sessionId
|
|
7018
|
+
});
|
|
7019
|
+
}
|
|
7020
|
+
case "background.send": {
|
|
7021
|
+
const sessionId = readReplayedSessionId(input);
|
|
7022
|
+
return options.service.status({
|
|
7023
|
+
context,
|
|
7024
|
+
sessionId
|
|
7025
|
+
});
|
|
7026
|
+
}
|
|
7027
|
+
case "background.stop": {
|
|
7028
|
+
const sessionId = readReplayedSessionId(input);
|
|
7029
|
+
return options.service.status({
|
|
7030
|
+
context,
|
|
7031
|
+
sessionId
|
|
7032
|
+
});
|
|
7033
|
+
}
|
|
7034
|
+
default: return completedResult;
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
function buildAuthority(definition, context, sessionId) {
|
|
7038
|
+
return Object.freeze({
|
|
7039
|
+
agentId: definition.agentId,
|
|
7040
|
+
memoryScopes: definition.memory.scopes,
|
|
7041
|
+
policyEpoch: context.policyEpoch,
|
|
7042
|
+
profileRevision: definition.profileRevision,
|
|
7043
|
+
...definition.projectSpaceId === void 0 ? {} : { projectSpaceId: definition.projectSpaceId },
|
|
7044
|
+
sessionKey: createBackgroundSessionKey(sessionId),
|
|
7045
|
+
toolGrantRevision: definition.toolGrantSet.revision
|
|
7046
|
+
});
|
|
7047
|
+
}
|
|
7048
|
+
function readObject(input, allowed) {
|
|
7049
|
+
return readBackgroundSessionObject(input, allowed, (message) => new BackgroundSessionCallerDenied(message));
|
|
7050
|
+
}
|
|
7051
|
+
function readString(value, name) {
|
|
7052
|
+
return readBackgroundSessionString(value, name, (message) => new BackgroundSessionCallerDenied(message));
|
|
7053
|
+
}
|
|
7054
|
+
function readInteger(value, name) {
|
|
7055
|
+
return readBackgroundSessionInteger(value, name, (message) => new BackgroundSessionCallerDenied(message));
|
|
7056
|
+
}
|
|
7057
|
+
function readPhase(value) {
|
|
7058
|
+
return readBackgroundSessionPhase(value, (message) => new BackgroundSessionCallerDenied(message));
|
|
7059
|
+
}
|
|
7060
|
+
function readReplayedSessionId(value) {
|
|
7061
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new BackgroundSessionCallerDenied("replayed background tool result is invalid");
|
|
7062
|
+
return readString(value.sessionId, "sessionId");
|
|
7063
|
+
}
|
|
7064
|
+
function readWaitInput(input) {
|
|
7065
|
+
return readBackgroundSessionWaitInput(input, (message) => new BackgroundSessionCallerDenied(message));
|
|
7066
|
+
}
|
|
7067
|
+
async function asToolInputRejection$1(operation) {
|
|
7068
|
+
try {
|
|
7069
|
+
return await operation();
|
|
7070
|
+
} catch (error) {
|
|
7071
|
+
if (error instanceof BackgroundSessionCallerDenied || error instanceof BackgroundSessionTransitionDenied) throw new RivusToolInputRejected(error.message);
|
|
7072
|
+
throw error;
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
//#endregion
|
|
7076
|
+
//#region src/infrastructure/persistence/jsonl-snapshot-store.ts
|
|
7077
|
+
var JsonlSnapshotStoreCorrupted = class extends Error {
|
|
7078
|
+
name = "JsonlSnapshotStoreCorrupted";
|
|
7079
|
+
};
|
|
7080
|
+
async function loadJsonlSnapshotStore(options) {
|
|
7081
|
+
const raw = await readPersistenceFile(options.filePath);
|
|
7082
|
+
if (raw === void 0) return [];
|
|
7083
|
+
const lines = raw.split("\n");
|
|
7084
|
+
const latest = /* @__PURE__ */ new Map();
|
|
7085
|
+
let lineCount = 0;
|
|
7086
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
7087
|
+
const line = lines[index] ?? "";
|
|
7088
|
+
if (!line.trim()) continue;
|
|
7089
|
+
lineCount += 1;
|
|
7090
|
+
let parsed;
|
|
7091
|
+
try {
|
|
7092
|
+
parsed = JSON.parse(line);
|
|
7093
|
+
} catch {
|
|
7094
|
+
if (index === lines.length - 1) {
|
|
7095
|
+
await truncateAtLine(options.filePath, lines, index);
|
|
7096
|
+
break;
|
|
7097
|
+
}
|
|
7098
|
+
throw new JsonlSnapshotStoreCorrupted(`${options.errorLabel} JSONL at line ${index + 1} is not valid JSON`);
|
|
7099
|
+
}
|
|
7100
|
+
if (!isRecord$4(parsed) || parsed.version !== options.version) throw new JsonlSnapshotStoreCorrupted(`unsupported ${options.errorLabel} version at line ${index + 1}`);
|
|
7101
|
+
if (isRecord$4(parsed.snapshot)) {
|
|
7102
|
+
if (!Array.isArray(parsed.snapshot.records)) throw new JsonlSnapshotStoreCorrupted(`invalid ${options.errorLabel} snapshot at line ${index + 1}`);
|
|
7103
|
+
latest.clear();
|
|
7104
|
+
for (const value of parsed.snapshot.records) {
|
|
7105
|
+
if (!options.isRecord(value)) throw new JsonlSnapshotStoreCorrupted(`invalid ${options.errorLabel} snapshot at line ${index + 1}`);
|
|
7106
|
+
latest.set(options.keyOf(value), structuredClone(value));
|
|
7107
|
+
}
|
|
7108
|
+
continue;
|
|
7109
|
+
}
|
|
7110
|
+
if (!options.isRecord(parsed.record)) throw new JsonlSnapshotStoreCorrupted(`invalid ${options.errorLabel} record at line ${index + 1}`);
|
|
7111
|
+
const record = parsed.record;
|
|
7112
|
+
const previous = latest.get(options.keyOf(record));
|
|
7113
|
+
const invalid = options.validateSequence(previous, record, index + 1);
|
|
7114
|
+
if (invalid) throw new JsonlSnapshotStoreCorrupted(`invalid ${options.errorLabel} ${invalid} at line ${index + 1}`);
|
|
7115
|
+
latest.set(options.keyOf(record), structuredClone(record));
|
|
7116
|
+
}
|
|
7117
|
+
if (lineCount >= options.compactThreshold) await writeJsonlSnapshot(options.filePath, options.version, [...latest.values()]);
|
|
7118
|
+
return [...latest.values()];
|
|
7119
|
+
}
|
|
7120
|
+
async function appendJsonlRecord(filePath, version, record) {
|
|
7121
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
7122
|
+
await appendFile(filePath, `${JSON.stringify({
|
|
7123
|
+
record,
|
|
7124
|
+
version
|
|
7125
|
+
})}\n`, "utf8");
|
|
7126
|
+
}
|
|
7127
|
+
async function writeJsonlSnapshot(filePath, version, records) {
|
|
7128
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
7129
|
+
await writeFile(filePath, `${JSON.stringify({
|
|
7130
|
+
snapshot: { records },
|
|
7131
|
+
version
|
|
7132
|
+
})}\n`, "utf8");
|
|
7133
|
+
}
|
|
7134
|
+
async function truncateAtLine(filePath, lines, lineIndex) {
|
|
7135
|
+
let offset = 0;
|
|
7136
|
+
for (let index = 0; index < lineIndex; index += 1) offset += (lines[index] ?? "").length + 1;
|
|
7137
|
+
await truncate(filePath, offset);
|
|
7138
|
+
}
|
|
7139
|
+
//#endregion
|
|
7140
|
+
//#region src/infrastructure/persistence/jsonl-background-session-repository.ts
|
|
7141
|
+
const BACKGROUND_SESSION_JSONL_VERSION = 1;
|
|
7142
|
+
async function openJsonlBackgroundSessionRepository(options) {
|
|
7143
|
+
return createBackgroundSessionRepository({
|
|
7144
|
+
initial: await loadJsonlSnapshotStore({
|
|
7145
|
+
compactThreshold: options.compactThreshold ?? 5e3,
|
|
7146
|
+
errorLabel: "background session",
|
|
7147
|
+
filePath: options.filePath,
|
|
7148
|
+
isRecord: isBackgroundSessionState,
|
|
7149
|
+
keyOf: (record) => record.sessionId,
|
|
7150
|
+
validateSequence: validateSessionSequence,
|
|
7151
|
+
version: 1
|
|
7152
|
+
}),
|
|
7153
|
+
persist: (record) => appendJsonlRecord(options.filePath, 1, record)
|
|
7154
|
+
});
|
|
7155
|
+
}
|
|
7156
|
+
function validateSessionSequence(previous, record, _lineNumber) {
|
|
7157
|
+
if (previous === void 0) {
|
|
7158
|
+
if (record.revision !== 1 || record.phase !== "queued") return "first record";
|
|
7159
|
+
return;
|
|
7160
|
+
}
|
|
7161
|
+
if (record.revision !== previous.revision + 1) return "revision";
|
|
7162
|
+
return isValidBackgroundSessionTransition(previous, record);
|
|
7163
|
+
}
|
|
7164
|
+
function isBackgroundSessionState(value) {
|
|
7165
|
+
if (!isRecord$4(value)) return false;
|
|
7166
|
+
const phases = [
|
|
7167
|
+
"queued",
|
|
7168
|
+
"running",
|
|
7169
|
+
"waiting",
|
|
7170
|
+
"input-required",
|
|
7171
|
+
"stopping",
|
|
7172
|
+
"stopped",
|
|
7173
|
+
"completed",
|
|
7174
|
+
"failed",
|
|
7175
|
+
"reconciliation-required"
|
|
7176
|
+
];
|
|
7177
|
+
if (typeof value.sessionId !== "string" || value.sessionId.trim() === "") return false;
|
|
7178
|
+
if (typeof value.displayName !== "string") return false;
|
|
7179
|
+
if (!phases.includes(value.phase)) return false;
|
|
7180
|
+
if (!Number.isInteger(value.revision) || value.revision < 1) return false;
|
|
7181
|
+
if (typeof value.parentRunId !== "string" || value.parentRunId.trim() === "") return false;
|
|
7182
|
+
if (typeof value.sourceMessageId !== "string" || value.sourceMessageId.trim() === "") return false;
|
|
7183
|
+
if (!isRecord$4(value.origin)) return false;
|
|
7184
|
+
const origin = value.origin;
|
|
7185
|
+
if (typeof origin.endpointId !== "string" || origin.endpointId.trim() === "") return false;
|
|
7186
|
+
if (typeof origin.tenantKey !== "string") return false;
|
|
7187
|
+
if (!Array.isArray(origin.allowedActorOpenIds) || origin.allowedActorOpenIds.some((id) => typeof id !== "string")) return false;
|
|
7188
|
+
if (origin.conversationId !== void 0 && typeof origin.conversationId !== "string") return false;
|
|
7189
|
+
if (origin.memory !== void 0 && !isRecord$4(origin.memory)) return false;
|
|
7190
|
+
if (!isRecord$4(value.authority)) return false;
|
|
7191
|
+
const authority = value.authority;
|
|
7192
|
+
if (typeof authority.agentId !== "string" || authority.agentId.trim() === "") return false;
|
|
7193
|
+
if (typeof authority.profileRevision !== "string" || authority.profileRevision.trim() === "") return false;
|
|
7194
|
+
if (typeof authority.toolGrantRevision !== "string" || authority.toolGrantRevision.trim() === "") return false;
|
|
7195
|
+
if (!Number.isInteger(authority.policyEpoch) || authority.policyEpoch < 0) return false;
|
|
7196
|
+
if (!Array.isArray(authority.memoryScopes) || authority.memoryScopes.some((scope) => typeof scope !== "string")) return false;
|
|
7197
|
+
if (typeof authority.sessionKey !== "string" || authority.sessionKey.trim() === "") return false;
|
|
7198
|
+
if (authority.projectSpaceId !== void 0 && typeof authority.projectSpaceId !== "string") return false;
|
|
7199
|
+
if (typeof value.prompt !== "string") return false;
|
|
7200
|
+
if (!Number.isInteger(value.stepCount) || value.stepCount < 0) return false;
|
|
7201
|
+
if (!Number.isInteger(value.wakeCount) || value.wakeCount < 0) return false;
|
|
7202
|
+
if (!Number.isInteger(value.consecutiveFailures) || value.consecutiveFailures < 0) return false;
|
|
7203
|
+
if (!Array.isArray(value.pendingInput) || value.pendingInput.some((entry) => typeof entry !== "string")) return false;
|
|
7204
|
+
if (value.currentStepRunId !== void 0 && typeof value.currentStepRunId !== "string") return false;
|
|
7205
|
+
if (value.lastWakeText !== void 0 && typeof value.lastWakeText !== "string") return false;
|
|
7206
|
+
if (value.wakeAt !== void 0 && typeof value.wakeAt !== "string") return false;
|
|
7207
|
+
if (value.errorMessage !== void 0 && typeof value.errorMessage !== "string") return false;
|
|
7208
|
+
if (value.reconciliationNote !== void 0 && typeof value.reconciliationNote !== "string") return false;
|
|
7209
|
+
if (typeof value.createdAt !== "string" || !isIso(value.createdAt)) return false;
|
|
7210
|
+
if (typeof value.updatedAt !== "string" || !isIso(value.updatedAt)) return false;
|
|
7211
|
+
if (value.lease !== void 0 && !isLease(value.lease)) return false;
|
|
7212
|
+
if (value.cancellation !== void 0) {
|
|
7213
|
+
if (!isRecord$4(value.cancellation)) return false;
|
|
7214
|
+
if (typeof value.cancellation.reason !== "string" || !isIso(value.cancellation.requestedAt)) return false;
|
|
7215
|
+
}
|
|
7216
|
+
if (value.result !== void 0) {
|
|
7217
|
+
if (!isRecord$4(value.result)) return false;
|
|
7218
|
+
if (typeof value.result.text !== "string" || typeof value.result.stepRunId !== "string" || !isIso(value.result.completedAt)) return false;
|
|
7219
|
+
}
|
|
7220
|
+
return true;
|
|
7221
|
+
}
|
|
7222
|
+
function isLease(value) {
|
|
7223
|
+
if (!isRecord$4(value)) return false;
|
|
7224
|
+
return typeof value.owner === "string" && value.owner.trim() !== "" && Number.isInteger(value.epoch) && value.epoch >= 0 && isIso(value.expiresAt);
|
|
7225
|
+
}
|
|
7226
|
+
function isIso(value) {
|
|
7227
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
7228
|
+
}
|
|
7229
|
+
//#endregion
|
|
7230
|
+
//#region src/infrastructure/persistence/jsonl-background-session-delivery-store.ts
|
|
7231
|
+
const BACKGROUND_SESSION_DELIVERY_JSONL_VERSION = 1;
|
|
7232
|
+
async function openJsonlBackgroundSessionDeliveryStore(options) {
|
|
7233
|
+
return createBackgroundSessionDeliveryStore({
|
|
7234
|
+
initial: await loadJsonlSnapshotStore({
|
|
7235
|
+
compactThreshold: options.compactThreshold ?? 2e3,
|
|
7236
|
+
errorLabel: "background session delivery",
|
|
7237
|
+
filePath: options.filePath,
|
|
7238
|
+
isRecord: isDeliveryRecord,
|
|
7239
|
+
keyOf: (record) => record.deliveryId,
|
|
7240
|
+
validateSequence: validateDeliverySequence,
|
|
7241
|
+
version: 1
|
|
7242
|
+
}),
|
|
7243
|
+
persist: (record) => appendJsonlRecord(options.filePath, 1, record)
|
|
7244
|
+
});
|
|
7245
|
+
}
|
|
7246
|
+
function validateDeliverySequence(previous, record, _lineNumber) {
|
|
7247
|
+
if (previous === void 0) return record.revision === 1 && record.state === "pending" ? void 0 : "first record";
|
|
7248
|
+
if (record.revision !== previous.revision + 1) return "revision";
|
|
7249
|
+
return previous.state === "pending" && record.state === "delivered" ? void 0 : "transition";
|
|
7250
|
+
}
|
|
7251
|
+
function isDeliveryRecord(value) {
|
|
7252
|
+
if (!isRecord$4(value)) return false;
|
|
7253
|
+
if (typeof value.deliveryId !== "string" || value.deliveryId.trim() === "" || typeof value.sessionId !== "string" || value.sessionId.trim() === "") return false;
|
|
7254
|
+
if (typeof value.kind !== "string" || ![
|
|
7255
|
+
"progress",
|
|
7256
|
+
"final",
|
|
7257
|
+
"stopped",
|
|
7258
|
+
"failed",
|
|
7259
|
+
"reconciliation"
|
|
7260
|
+
].includes(value.kind)) return false;
|
|
7261
|
+
if (typeof value.text !== "string") return false;
|
|
7262
|
+
if (value.state !== "pending" && value.state !== "delivered") return false;
|
|
7263
|
+
if (!Number.isInteger(value.revision) || value.revision < 1) return false;
|
|
7264
|
+
if (value.providerMessageId !== void 0 && typeof value.providerMessageId !== "string") return false;
|
|
7265
|
+
return typeof value.createdAt === "string" && Number.isFinite(Date.parse(value.createdAt));
|
|
7266
|
+
}
|
|
7267
|
+
//#endregion
|
|
7268
|
+
//#region src/infrastructure/feishu/feishu-interactive-card-send.ts
|
|
7269
|
+
function sendFeishuInteractiveCard(client, request, errorMessage) {
|
|
7270
|
+
return Effect.gen(function* () {
|
|
7271
|
+
const response = yield* client.request(request, errorMessage);
|
|
7272
|
+
const providerMessageId = readMessageId(response.body.data);
|
|
7273
|
+
if (!providerMessageId) return yield* Effect.fail(new FeishuOpenApiError(response.status, response.body.code, "Feishu send response missing message_id"));
|
|
7274
|
+
return Object.freeze({ providerMessageId });
|
|
7275
|
+
});
|
|
7276
|
+
}
|
|
7277
|
+
function readMessageId(data) {
|
|
7278
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) return void 0;
|
|
7279
|
+
const value = data.message_id;
|
|
7280
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
7281
|
+
}
|
|
7282
|
+
//#endregion
|
|
7283
|
+
//#region src/infrastructure/feishu/feishu-background-session-delivery.ts
|
|
7284
|
+
function createConfiguredFeishuBackgroundSessionDelivery(options) {
|
|
7285
|
+
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
7286
|
+
return { deliver: (input) => Effect.runPromise(sendFeishuInteractiveCard(options.client, {
|
|
7287
|
+
body: {
|
|
7288
|
+
content: JSON.stringify(createBackgroundSessionCard(input)),
|
|
7289
|
+
msg_type: "interactive",
|
|
7290
|
+
receive_id: input.chatId,
|
|
7291
|
+
uuid: deliveryUuid$1(input.deliveryId)
|
|
7292
|
+
},
|
|
7293
|
+
method: "POST",
|
|
7294
|
+
url: `${baseUrl}/open-apis/im/v1/messages?receive_id_type=chat_id`
|
|
7295
|
+
}, "Feishu background session delivery failed")) };
|
|
7296
|
+
}
|
|
7297
|
+
function createBackgroundSessionCard(input) {
|
|
7298
|
+
const title = cardTitle(input.kind);
|
|
7299
|
+
const headerTemplate = cardHeaderTemplate(input.kind);
|
|
7300
|
+
return {
|
|
7301
|
+
body: { elements: [
|
|
7302
|
+
{
|
|
7303
|
+
content: `**${escapeCardText(title)}** · ${escapeCardText(input.displayName)}`,
|
|
7304
|
+
tag: "markdown"
|
|
7305
|
+
},
|
|
7306
|
+
{
|
|
7307
|
+
content: input.text,
|
|
7308
|
+
tag: "markdown"
|
|
7309
|
+
},
|
|
7310
|
+
{
|
|
7311
|
+
content: `会话 ${escapeCardText(input.sessionId)} · _由 Rivus 后台会话生成_`,
|
|
7312
|
+
tag: "markdown"
|
|
7313
|
+
}
|
|
7314
|
+
] },
|
|
7315
|
+
config: { update_multi: true },
|
|
7316
|
+
header: {
|
|
7317
|
+
template: headerTemplate,
|
|
7318
|
+
title: {
|
|
7319
|
+
content: title,
|
|
7320
|
+
tag: "plain_text"
|
|
7321
|
+
}
|
|
7322
|
+
},
|
|
7323
|
+
schema: "2.0"
|
|
7324
|
+
};
|
|
7325
|
+
}
|
|
7326
|
+
function cardTitle(kind) {
|
|
7327
|
+
switch (kind) {
|
|
7328
|
+
case "progress": return "后台会话进展";
|
|
7329
|
+
case "final": return "后台会话完成";
|
|
7330
|
+
case "failed": return "后台会话失败";
|
|
7331
|
+
case "stopped": return "后台会话已停止";
|
|
7332
|
+
case "reconciliation": return "后台会话需对账";
|
|
7333
|
+
}
|
|
7334
|
+
}
|
|
7335
|
+
function cardHeaderTemplate(kind) {
|
|
7336
|
+
switch (kind) {
|
|
7337
|
+
case "final": return "green";
|
|
7338
|
+
case "failed":
|
|
7339
|
+
case "reconciliation": return "red";
|
|
7340
|
+
case "stopped": return "orange";
|
|
7341
|
+
case "progress": return "blue";
|
|
7342
|
+
}
|
|
7343
|
+
}
|
|
7344
|
+
function escapeCardText(value) {
|
|
7345
|
+
return value.replace(/([*_~`|<>])/g, "\\$1");
|
|
7346
|
+
}
|
|
7347
|
+
function deliveryUuid$1(deliveryId) {
|
|
7348
|
+
return `rivus-${createHash("sha256").update(`background-session:${deliveryId}`).digest("hex").slice(0, 32)}`;
|
|
7349
|
+
}
|
|
7350
|
+
//#endregion
|
|
5884
7351
|
//#region src/application/memory/rivus-memory-tool.ts
|
|
5885
7352
|
function createRivusMemoryTool(memory) {
|
|
5886
7353
|
const handles = Array.isArray(memory) ? [...memory] : [memory];
|
|
@@ -7051,21 +8518,16 @@ function createConfiguredFeishuTextReplySender(options) {
|
|
|
7051
8518
|
//#region src/infrastructure/feishu/feishu-automation-card-sender.ts
|
|
7052
8519
|
function createConfiguredFeishuAutomationCardSender(options) {
|
|
7053
8520
|
const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
|
|
7054
|
-
return { send: (input) =>
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
}, "Feishu send failed");
|
|
7065
|
-
const providerMessageId = readMessageId(response.body.data);
|
|
7066
|
-
if (!providerMessageId) return yield* Effect.fail(new FeishuOpenApiError(response.status, response.body.code, "Feishu send response missing message_id"));
|
|
7067
|
-
return Object.freeze({ providerMessageId });
|
|
7068
|
-
}) };
|
|
8521
|
+
return { send: (input) => sendFeishuInteractiveCard(options.client, {
|
|
8522
|
+
body: {
|
|
8523
|
+
content: JSON.stringify(createAutomationCard(input.markdown)),
|
|
8524
|
+
msg_type: "interactive",
|
|
8525
|
+
receive_id: input.receiveId,
|
|
8526
|
+
uuid: deliveryUuid(input.idempotencyKey)
|
|
8527
|
+
},
|
|
8528
|
+
method: "POST",
|
|
8529
|
+
url: `${baseUrl}/open-apis/im/v1/messages?receive_id_type=${encodeURIComponent(input.receiveIdType)}`
|
|
8530
|
+
}, "Feishu send failed") };
|
|
7069
8531
|
}
|
|
7070
8532
|
function createAutomationCard(markdown) {
|
|
7071
8533
|
const presentation = splitPresentation(markdown);
|
|
@@ -7104,11 +8566,6 @@ function splitPresentation(markdown) {
|
|
|
7104
8566
|
function deliveryUuid(idempotencyKey) {
|
|
7105
8567
|
return `rivus-${createHash("sha256").update(idempotencyKey).digest("hex").slice(0, 32)}`;
|
|
7106
8568
|
}
|
|
7107
|
-
function readMessageId(data) {
|
|
7108
|
-
if (data === null || typeof data !== "object" || Array.isArray(data)) return void 0;
|
|
7109
|
-
const value = data.message_id;
|
|
7110
|
-
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
7111
|
-
}
|
|
7112
8569
|
//#endregion
|
|
7113
8570
|
//#region src/application/interaction/human-interaction-repository.ts
|
|
7114
8571
|
var HumanInteractionRepositoryError = class extends Error {
|
|
@@ -7462,4 +8919,4 @@ function validateDecisionInput(input) {
|
|
|
7462
8919
|
if (input.recommendedOptionId && !optionIds.includes(input.recommendedOptionId)) throw new Error("recommended user decision option must be available");
|
|
7463
8920
|
}
|
|
7464
8921
|
//#endregion
|
|
7465
|
-
export { AgentContextBudgetExceeded, AgentEventHandlerFailed, AgentEventLogStoreError, AgentEventSinkFailed, AgentHarnessBusy, AgentInstanceBusy, AgentInstanceConflict, AgentLoopFailed, AgentMemoryError, AgentRunCancelled, AgentRuntimeDisposed, AutomationMandateError, CardPresentationTransitionDenied, CompactionError, DEFAULT_CARD_STREAM_LEASE_MS, DelegationDenied, DeliveryOutboxError, FeishuCardPresentationNotFound, FeishuCardTargetNotFound, FeishuCardTargetRegistryStoreError, FeishuCotProtocolError, FeishuEndpointCredentialError, FeishuOpenApiError, FeishuTenantAccessTokenError, HumanInteractionRepositoryError, HumanInteractionTransitionDenied, InvalidFeishuCardAction, InvalidFeishuMessageContent, InvalidFeishuSessionReference, InvalidInvocationAuthority, InvalidProjectSkillCatalog, InvalidRecoveryAction, InvalidRivusEndpointBinding, InvalidRivusPlugin, InvalidRivusProjectSpace, InvalidStableJson, InvalidToolInput, InvalidWorkspaceRoot, LangfuseTelemetryConfigError, MEMORY_SCOPES, OpenClawEnvImportError, PluginStateConflict, RIVUS_MEMORY_TOOL_ID, RIVUS_MEMORY_TOOL_PLUGIN_ID, RIVUS_MEMORY_TOOL_VERSION, RIVUS_PLUGIN_API_VERSION, RivusDaemonConfigError, RivusDeploymentAutomationReadinessError, RivusDeploymentDaemonLifecycleError, RivusDeploymentManifestError, RivusDeploymentReadinessError, RivusPluginConformanceError, RivusPluginLoadError, RivusToolInputRejected, SessionSchedulerCapacityExceeded, SessionSchedulerDisposed, ToolInvocationDenied, UnsupportedFeishuCardAction, UnsupportedFeishuMessage, WorkspaceInstructionsSourceError, acceptsCardPresentationProgress, activeCardPresentation, assembleAgentContext, assertRivusPluginConforms, commitAutomationOutcome, createAgentCommandFromFeishuCardAction, createAgentCommandFromFeishuMessage, createAgentConversationMessages, createAgentDomainEventHandler, createAgentDomainEventSink, createAgentDomainEventSinkFromCallback, createAgentHarness, createAgentHarnessClient, createAgentHarnessPooledRuntime, createAgentInstanceRegistry, createAgentLoopFromCallback, createAgentLoopModelExecutionEnd, createAgentLoopModelExecutionStart, createAgentLoopPromptTransformer, createAgentLoopSkillExecutionEnd, createAgentLoopSkillExecutionStart, createAgentLoopTextDelta, createAgentLoopThinkingDelta, createAgentLoopToolExecutionEnd, createAgentLoopToolExecutionStart, createAgentLoopToolExecutionUpdate, createAgentMemoryService, createAgentRunUpdateHandler, createAgentRuntime, createAgentRuntimePool, createAgentTranscriptMessages, createAgentTranscriptTurn, createAgentsMdInstructionsProvider, createAsyncIterableAgentLoop, createAutomationMandateStore, createCoalescingFeishuPublisher, createCompactionService, createCompositeRivusDaemonTransport, createConfiguredFeishuAutomationCardSender, createConfiguredFeishuCardKitPublisher, createConfiguredFeishuCardKitTargetCreator, createConfiguredFeishuCardRolloverRuntime, createConfiguredFeishuHumanInteractionPresenter, createConfiguredFeishuOpenApiClient, createConfiguredFeishuTextReplySender, createConfiguredRivusDaemonBootstrap, createConfiguredRivusDeploymentDaemon, createDailyAutomationSchedule, createDefaultAgentHarness, createDefaultAgentHarnessClient, createDefaultAgentHarnessClientFromCallback, createDefaultAgentHarnessClientFromTextCallback, createDefaultAgentHarnessFromCallback, createDefaultAgentHarnessFromTextCallback, createDefaultAgentRuntime, createDefaultAgentRuntimeFromCallback, createDefaultAgentRuntimeFromTextCallback, createDelegationService, createDeliveryOutbox, createEventAgentLoop, createFakeRivusPlugin, createFeishuAgentDaemon, createFeishuAgentRunCard, createFeishuAgentRuntime, createFeishuCardActionCallbackResponse, createFeishuCardActionErrorResponse, createFeishuCardDeliveryLedger, createFeishuCardDeliveryReconciler, createFeishuCardKitOpenApiClient, createFeishuCardKitOpenApiTargetCreator, createFeishuCardKitPublisher, createFeishuCardPresentationStore, createFeishuCardRollover, createFeishuCardRolloverSupervisor, createFeishuCardTargetPreparation, createFeishuConversationId, createFeishuCotPublisher, createFeishuDeploymentEndpoint, createFeishuEventHandlers, createFeishuHumanInteractionCard, createFeishuHumanInteractionPresenter, createFeishuInboxRepository, createFeishuMessageQueue, createFeishuMessageWorker, createFeishuOpenApiClient, createFeishuPeriodicFlush, createFeishuPresentationPreparation, createFeishuSessionKey, createFeishuStreamProjector, createFeishuTenantAccessTokenProvider, createFeishuWebSocketDaemon, createFeishuWorkerLoop, createFixedClock, createHumanInteractionEndpointRegistry, createHumanInteractionService, createHumanInteractionToolApprovalGateway, createInMemoryFeishuCardTargetRegistry, createInMemoryHumanInteractionRepository, createInvocationAuthority, createJsonFetchRequest, createJsonFileFeishuCardTargetRegistry, createJsonlAgentEventLog, createJsonlHumanInteractionRepository, createLangfuseAgentTelemetry, createLazyFeishuWebSocketEventDispatcher, createMemoryNamespace, createOpenTelemetryAgentEventSink, createOpenTelemetryAgentTelemetry, createPiAgentLoop, createPiSdkAgentLoop, createPiSessionRegistry, createPluginStateStore, createProjectMemoryPromptPreparer, createRateLimitedFeishuPublisher, createRecoveryAction, createRecoveryControl, createRivusAgentHost, createRivusDaemonProcess, createRivusDaemonShutdownController, createRivusDaemonStatusHttpServer, createRivusDaemonStatusReporter, createRivusDeploymentCliProcess, createRivusDeploymentDaemon, createRivusEnvFromOpenClawConfig, createRivusMemoryTool, createRivusMemoryToolContract, createRivusMemoryToolDescriptor, createRivusPluginCatalog, createRoutedHumanInteractionToolApprovalService, createScheduledAutomation, createSequenceRunIds, createSessionScheduler, createSubagentCoordinator, createSystemClock, createTelemetryContentRedactor, createTestClock, createTextAgentLoop, createTextAgentLoopFromCallback, createToolBroker, createToolInputDigest, createToolOperationLedger, createUuidRunIds, createWorkspaceRootHandle, describeFeishuMessageIntake, evolveAgentRun, formatRivusEnvFile, initialAgentRunState, intersectToolIds, isAgentToolExecutionEvent, isAssistantTextDeltaEvent, isAssistantThinkingDeltaEvent, isCardPresentationHandoffDue, isTerminalAgentDomainEvent, isTerminalAgentRunPhase, loadNodeRivusPluginModule, loadRivusDaemonConfig, loadRivusDeployment, loadRivusDeploymentManifest, normalizeStableJson, openJsonAutomationTickRepository, openJsonlAgentMemoryService, openJsonlFeishuCardDeliveryLedger, openJsonlFeishuInboxRepository, openJsonlRecoveryControl, openJsonlToolOperationLedger, replayAgentHistory, replayAgentTranscript, requiresToolApproval, resolveFeishuEndpointCredentials, resolveLangfuseTelemetryConfig, resolveRivusAgentDefinition, resolveRivusProjectSpace, restoreAgentHistory, restoreConfiguredRivusDaemonBootstrap, restrictMemoryScopesForAudience, runRivusDaemonCli, shouldAcceptFeishuEndpointMessage, transitionHumanInteraction, validateProjectSkillCatalog, validateProjectSkillCommand, validateRivusDeploymentManifest };
|
|
8922
|
+
export { AgentContextBudgetExceeded, AgentEventHandlerFailed, AgentEventLogStoreError, AgentEventSinkFailed, AgentHarnessBusy, AgentInstanceBusy, AgentInstanceConflict, AgentLoopFailed, AgentMemoryError, AgentRunCancelled, AgentRuntimeDisposed, AutomationMandateError, BACKGROUND_SESSION_DELIVERY_JSONL_VERSION, BACKGROUND_SESSION_JSONL_VERSION, BACKGROUND_SESSION_SESSION_KEY_PREFIX, BACKGROUND_SESSION_START_TOOL_ID, BACKGROUND_SESSION_TOOL_IDS, BACKGROUND_SESSION_TOOL_PLUGIN_ID, BACKGROUND_SESSION_TOOL_VERSION, BackgroundSessionCallerDenied, BackgroundSessionDeliveryConflict, BackgroundSessionRepositoryConflict, BackgroundSessionRepositoryCorrupted, BackgroundSessionTransitionDenied, CardPresentationTransitionDenied, CompactionError, DEFAULT_BACKGROUND_SESSION_LEASE_MS, DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, DEFAULT_BACKGROUND_SESSION_LIFETIME_MS, DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS, DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS, DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS, DEFAULT_CARD_STREAM_LEASE_MS, DelegationDenied, DeliveryOutboxError, FeishuCardPresentationNotFound, FeishuCardTargetNotFound, FeishuCardTargetRegistryStoreError, FeishuCotProtocolError, FeishuEndpointCredentialError, FeishuOpenApiError, FeishuTenantAccessTokenError, HumanInteractionRepositoryError, HumanInteractionTransitionDenied, InvalidFeishuCardAction, InvalidFeishuMessageContent, InvalidFeishuSessionReference, InvalidInvocationAuthority, InvalidProjectSkillCatalog, InvalidRecoveryAction, InvalidRivusEndpointBinding, InvalidRivusPlugin, InvalidRivusProjectSpace, InvalidStableJson, InvalidToolInput, InvalidWorkspaceRoot, LangfuseTelemetryConfigError, MEMORY_SCOPES, OpenClawEnvImportError, PluginStateConflict, RIVUS_MEMORY_TOOL_ID, RIVUS_MEMORY_TOOL_PLUGIN_ID, RIVUS_MEMORY_TOOL_VERSION, RIVUS_PLUGIN_API_VERSION, RivusDaemonConfigError, RivusDeploymentAutomationReadinessError, RivusDeploymentDaemonLifecycleError, RivusDeploymentManifestError, RivusDeploymentReadinessError, RivusPluginConformanceError, RivusPluginLoadError, RivusToolInputRejected, SessionSchedulerCapacityExceeded, SessionSchedulerDisposed, ToolInvocationDenied, UnsupportedFeishuCardAction, UnsupportedFeishuMessage, WorkspaceInstructionsSourceError, acceptsCardPresentationProgress, activeCardPresentation, appendBackgroundSessionInput, assembleAgentContext, assertRivusPluginConforms, backgroundSessionToolIds, claimBackgroundSession, commitAutomationOutcome, completeBackgroundSessionStep, completeBackgroundSessionStop, createAgentCommandFromFeishuCardAction, createAgentCommandFromFeishuMessage, createAgentConversationMessages, createAgentDomainEventHandler, createAgentDomainEventSink, createAgentDomainEventSinkFromCallback, createAgentHarness, createAgentHarnessClient, createAgentHarnessPooledRuntime, createAgentInstanceRegistry, createAgentLoopFromCallback, createAgentLoopModelExecutionEnd, createAgentLoopModelExecutionStart, createAgentLoopPromptTransformer, createAgentLoopSkillExecutionEnd, createAgentLoopSkillExecutionStart, createAgentLoopTextDelta, createAgentLoopThinkingDelta, createAgentLoopToolExecutionEnd, createAgentLoopToolExecutionStart, createAgentLoopToolExecutionUpdate, createAgentMemoryService, createAgentRunUpdateHandler, createAgentRuntime, createAgentRuntimePool, createAgentTranscriptMessages, createAgentTranscriptTurn, createAgentsMdInstructionsProvider, createAsyncIterableAgentLoop, createAutomationMandateStore, createBackgroundSession, createBackgroundSessionCard, createBackgroundSessionDeliveryStore, createBackgroundSessionHostTools, createBackgroundSessionKey, createBackgroundSessionRepository, createBackgroundSessionService, createBackgroundSessionStepSourceMessageId, createBackgroundSessionSupervisor, createBackgroundSessionToolContracts, createCoalescingFeishuPublisher, createCompactionService, createCompositeRivusDaemonTransport, createConfiguredFeishuAutomationCardSender, createConfiguredFeishuBackgroundSessionDelivery, createConfiguredFeishuCardKitPublisher, createConfiguredFeishuCardKitTargetCreator, createConfiguredFeishuCardRolloverRuntime, createConfiguredFeishuHumanInteractionPresenter, createConfiguredFeishuOpenApiClient, createConfiguredFeishuTextReplySender, createConfiguredRivusDaemonBootstrap, createConfiguredRivusDeploymentDaemon, createDailyAutomationSchedule, createDefaultAgentHarness, createDefaultAgentHarnessClient, createDefaultAgentHarnessClientFromCallback, createDefaultAgentHarnessClientFromTextCallback, createDefaultAgentHarnessFromCallback, createDefaultAgentHarnessFromTextCallback, createDefaultAgentRuntime, createDefaultAgentRuntimeFromCallback, createDefaultAgentRuntimeFromTextCallback, createDelegationService, createDeliveryOutbox, createEventAgentLoop, createFakeRivusPlugin, createFeishuAgentDaemon, createFeishuAgentRunCard, createFeishuAgentRuntime, createFeishuCardActionCallbackResponse, createFeishuCardActionErrorResponse, createFeishuCardDeliveryLedger, createFeishuCardDeliveryReconciler, createFeishuCardKitOpenApiClient, createFeishuCardKitOpenApiTargetCreator, createFeishuCardKitPublisher, createFeishuCardPresentationStore, createFeishuCardRollover, createFeishuCardRolloverSupervisor, createFeishuCardTargetPreparation, createFeishuConversationId, createFeishuCotPublisher, createFeishuDeploymentEndpoint, createFeishuEventHandlers, createFeishuHumanInteractionCard, createFeishuHumanInteractionPresenter, createFeishuInboxRepository, createFeishuMessageQueue, createFeishuMessageWorker, createFeishuOpenApiClient, createFeishuPeriodicFlush, createFeishuPresentationPreparation, createFeishuSessionKey, createFeishuStreamProjector, createFeishuTenantAccessTokenProvider, createFeishuWebSocketDaemon, createFeishuWorkerLoop, createFixedClock, createHumanInteractionEndpointRegistry, createHumanInteractionService, createHumanInteractionToolApprovalGateway, createInMemoryFeishuCardTargetRegistry, createInMemoryHumanInteractionRepository, createInvocationAuthority, createJsonFetchRequest, createJsonFileFeishuCardTargetRegistry, createJsonlAgentEventLog, createJsonlHumanInteractionRepository, createLangfuseAgentTelemetry, createLazyFeishuWebSocketEventDispatcher, createMemoryNamespace, createOpenTelemetryAgentEventSink, createOpenTelemetryAgentTelemetry, createPiAgentLoop, createPiSdkAgentLoop, createPiSessionRegistry, createPluginStateStore, createProjectMemoryPromptPreparer, createRateLimitedFeishuPublisher, createRecoveryAction, createRecoveryControl, createRivusAgentHost, createRivusDaemonProcess, createRivusDaemonShutdownController, createRivusDaemonStatusHttpServer, createRivusDaemonStatusReporter, createRivusDeploymentCliProcess, createRivusDeploymentDaemon, createRivusEnvFromOpenClawConfig, createRivusMemoryTool, createRivusMemoryToolContract, createRivusMemoryToolDescriptor, createRivusPluginCatalog, createRoutedHumanInteractionToolApprovalService, createScheduledAutomation, createSequenceRunIds, createSessionScheduler, createSubagentCoordinator, createSystemClock, createTelemetryContentRedactor, createTestClock, createTextAgentLoop, createTextAgentLoopFromCallback, createToolBroker, createToolInputDigest, createToolOperationLedger, createUuidRunIds, createWorkspaceRootHandle, describeFeishuMessageIntake, evolveAgentRun, extendBackgroundSessionDefinition, failBackgroundSessionStep, formatRivusEnvFile, initialAgentRunState, intersectToolIds, isAgentToolExecutionEvent, isAssistantTextDeltaEvent, isAssistantThinkingDeltaEvent, isBackgroundSessionDue, isBackgroundSessionLeaseExpired, isBackgroundSessionState, isBackgroundSessionTerminalPhase, isBackgroundSessionToolId, isCardPresentationHandoffDue, isTerminalAgentDomainEvent, isTerminalAgentRunPhase, loadNodeRivusPluginModule, loadRivusDaemonConfig, loadRivusDeployment, loadRivusDeploymentManifest, narrowBackgroundSessionDefinition, normalizeStableJson, openJsonAutomationTickRepository, openJsonlAgentMemoryService, openJsonlBackgroundSessionDeliveryStore, openJsonlBackgroundSessionRepository, openJsonlFeishuCardDeliveryLedger, openJsonlFeishuInboxRepository, openJsonlRecoveryControl, openJsonlToolOperationLedger, parkBackgroundSessionForReconciliation, progressDeliveryId, releaseBackgroundSessionLease, renewBackgroundSessionLease, replayAgentHistory, replayAgentTranscript, requestBackgroundSessionStop, requeueInterruptedBackgroundSessionStep, requiresToolApproval, resolveBackgroundSessionReconciliation, resolveBackgroundSessionSupervisorIntervalMs, resolveFeishuEndpointCredentials, resolveLangfuseTelemetryConfig, resolveRivusAgentDefinition, resolveRivusProjectSpace, restoreAgentHistory, restoreConfiguredRivusDaemonBootstrap, restrictMemoryScopesForAudience, runRivusDaemonCli, sessionIdFromSessionKey, shouldAcceptFeishuEndpointMessage, suspendBackgroundSession, terminalDeliveryId, transitionHumanInteraction, validateProjectSkillCatalog, validateProjectSkillCommand, validateRivusDeploymentManifest };
|