@rivus/agent 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap/pi-feishu.js +37 -31
- package/dist/chunks/agent-loop.d.ts +2 -1
- package/dist/chunks/agent-loop.js +8 -2
- package/dist/chunks/index.d.ts +7 -0
- package/dist/chunks/pi.js +39 -2
- package/dist/chunks/rivus-daemon-cli.js +1 -14
- package/dist/chunks/src.js +459 -263
- package/dist/pi.d.ts +22 -1
- package/dist/pi.js +2 -2
- package/examples/pi-feishu-deployment.bootstrap.ts +17 -11
- package/examples/pi-feishu.bootstrap.ts +1 -0
- package/package.json +1 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { i as resolvePiSessionToolNames, l as validatePiSkillCommand, m as createPiBashTool, n as createPiToolProxyDefinitions, p as createPiSkillReadTools, r as createPiSessionResources, t as createPiToolNameResolver } from "../chunks/pi.js";
|
|
1
2
|
import { d as resolveInvocationAuthority, r as createPiSkillRuntime, s as createToolInputDigest, u as createInvocationAuthority } from "../chunks/rivus-tool.js";
|
|
2
3
|
import { $t as openJsonlRecoveryControl, A as createScheduledAutomation, An as createLangfuseAgentTelemetry, Bn as createFeishuTopicContextResolver, Dn as createPiAgentLoop, Dt as createBackgroundSessionHostTools, Ei as openJsonFeishuSessionStore, En as createPiSessionRegistry, Gi as readPersistenceFile, Hi as ToolInvocationDenied, Ii as createFeishuCardDeliveryReconciler, In as createJsonlAgentEventLog, Ji as mergePiProviderModelDeclaration, Jt as createFeishuPresentationPreparation, K as createRivusMemoryToolDescriptor, Ki as isRecord$4, Lt as createBackgroundSessionService, Nt as openJsonlBackgroundSessionDeliveryStore, Oi as openJsonlFeishuInboxRepository, Ot as createBackgroundSessionSupervisor, Pi as openJsonlFeishuCardDeliveryLedger, Qr as createUuidRunIds, Si as createJsonFetchRequest, Ti as createSessionScheduler, Tn as readAutomationPresentation, Ui as createToolBroker, Un as createConfiguredFeishuTextReplySender, W as createProjectMemoryPromptPreparer, Wi as openJsonlToolOperationLedger, Wn as createConfiguredFeishuCardRolloverRuntime, Xt as createAgentHarnessPooledRuntime, Yi as writeAtomicTextFile, Yt as createFeishuDeploymentEndpoint, Zr as createSystemClock, an as createAgentsMdInstructionsProvider, bn as createJsonFileFeishuCardTargetRegistry, c as createConfiguredFeishuAutomationCardSender, d as createHumanInteractionToolApprovalGateway, en as openJsonlToolOperationLedger$1, f as createHumanInteractionEndpointRegistry, g as loadRivusDeploymentManifest, hr as createLazyFeishuWebSocketEventDispatcher, ht as resolveFeishuDeliveryChatId, ii as createAgentHarness, j as AUTOMATION_SUPPRESSION_PREFIX, jn as resolveLangfuseTelemetryConfig, jt as openJsonlBackgroundSessionRepository, k as openJsonAutomationTickRepository, l as createRoutedHumanInteractionToolApprovalService, mn as createFeishuCotPublisher, mt as createConfiguredFeishuBackgroundSessionDelivery, n as createConfiguredFeishuHumanInteractionPresenter, o as createJsonlHumanInteractionRepository, on as createWorkspaceRootHandle, qi as mergePiProviderBaseUrlOverride, t as createHumanInteractionService, tn as createToolBroker$1, tr as createConfiguredFeishuOpenApiClient, u as createHumanInteractionModelChangeApproval, wt as resolveBackgroundSessionSupervisorIntervalMs, z as openJsonlAgentMemoryService } from "../chunks/src.js";
|
|
3
4
|
import { I as resolveFeishuEndpointCredentials, J as migrateRivusModelManagementConfig, K as installRivusModelManagementCliLauncher, U as runDeploymentProcessEffect, X as loadMergedLocalEnvFile, Y as parseRivusModelManagementHomeConfig, q as installRivusRuntimeManagementSkill } from "../chunks/rivus-daemon-cli.js";
|
|
4
5
|
import { t as createSha256Digest } from "../chunks/sha256-digest.js";
|
|
5
6
|
import { a as toRivusModelManagementSubmission, i as projectRivusModelCliResponse, r as parseRivusModelManagementWireRequest, t as createRivusModelManagementFailure } from "../chunks/rivus-model-management-wire.js";
|
|
6
7
|
import { f as createBackgroundSessionStepSourceMessageId } from "../chunks/background-session-authority.js";
|
|
7
|
-
import { i as resolvePiSessionToolNames, l as validatePiSkillCommand, n as createPiToolProxyDefinitions, p as createPiSkillReadTools, r as createPiSessionResources, t as createPiToolNameResolver } from "../chunks/pi.js";
|
|
8
8
|
import { Effect, Either } from "effect";
|
|
9
9
|
import { createHash, randomUUID } from "node:crypto";
|
|
10
10
|
import { chmod, mkdir, open, readFile, realpath, rename, stat, unlink } from "node:fs/promises";
|
|
11
11
|
import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
12
|
-
import { ModelRuntime, SessionManager, SettingsManager, createAgentSession,
|
|
12
|
+
import { ModelRuntime, SessionManager, SettingsManager, createAgentSession, estimateTokens } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { Type } from "typebox";
|
|
@@ -155,26 +155,29 @@ function createPiManagedSessionContext(options) {
|
|
|
155
155
|
if (input.invocation?.kind === "feishu" && input.invocation.allowedActorOpenIds.length === 1) context = options.contexts.issue(input);
|
|
156
156
|
},
|
|
157
157
|
deactivate,
|
|
158
|
-
tool:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
...spawn
|
|
162
|
-
|
|
163
|
-
...
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
158
|
+
tool: createPiBashTool(options.cwd, {
|
|
159
|
+
...options.bashToolOptions,
|
|
160
|
+
spawnHook: (spawn) => {
|
|
161
|
+
const { RIVUS_MODEL_CONTEXT: _context, RIVUS_MODEL_SOCKET: _socket, ...baseEnv } = spawn.env;
|
|
162
|
+
const prepared = {
|
|
163
|
+
...spawn,
|
|
164
|
+
env: {
|
|
165
|
+
...baseEnv,
|
|
166
|
+
PATH: [
|
|
167
|
+
options.binDirectory,
|
|
168
|
+
dirname(options.nodeExecutable),
|
|
169
|
+
baseEnv.PATH
|
|
170
|
+
].filter(Boolean).join(delimiter),
|
|
171
|
+
RIVUS_MODEL_MANAGEMENT_ENABLED: "true",
|
|
172
|
+
RIVUS_MODEL_SOCKET: options.socketPath
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
return context ? createPiModelManagementBashSpawnHook({
|
|
176
|
+
context,
|
|
177
|
+
socketPath: options.socketPath
|
|
178
|
+
})(prepared) : prepared;
|
|
179
|
+
}
|
|
180
|
+
})
|
|
178
181
|
};
|
|
179
182
|
}
|
|
180
183
|
//#endregion
|
|
@@ -4054,13 +4057,6 @@ async function createRivusDeploymentAdapters(context) {
|
|
|
4054
4057
|
}) : void 0;
|
|
4055
4058
|
const sessionRegistry = createPiSessionRegistry({ createSession: async (firstInput) => {
|
|
4056
4059
|
let activeInput = firstInput;
|
|
4057
|
-
const managedContext = management?.enabled && runContexts && input.definition.runtimeToolGrantSet.toolIds.includes("bash") ? createPiManagedSessionContext({
|
|
4058
|
-
binDirectory: management.binDirectory,
|
|
4059
|
-
contexts: runContexts.registry,
|
|
4060
|
-
cwd: workingDirectory,
|
|
4061
|
-
nodeExecutable: process.execPath,
|
|
4062
|
-
socketPath: management.socketPath
|
|
4063
|
-
}) : void 0;
|
|
4064
4060
|
const resources = await createPiSessionResources({
|
|
4065
4061
|
agentDir: PI_AGENT_DIR,
|
|
4066
4062
|
appendSystemPromptOverride: () => [workspaceInstructions.content, skillRuntime.prompt].filter((content) => content.length > 0),
|
|
@@ -4069,8 +4065,17 @@ async function createRivusDeploymentAdapters(context) {
|
|
|
4069
4065
|
...input.projectSpace ? { projectSkillPaths: input.projectSpace.skillPaths } : {},
|
|
4070
4066
|
systemPromptOverride: () => input.definition.systemPrompt
|
|
4071
4067
|
});
|
|
4068
|
+
const managedContext = management?.enabled && runContexts && input.definition.runtimeToolGrantSet.toolIds.includes("bash") ? createPiManagedSessionContext({
|
|
4069
|
+
...resources.bashToolOptions ? { bashToolOptions: resources.bashToolOptions } : {},
|
|
4070
|
+
binDirectory: management.binDirectory,
|
|
4071
|
+
contexts: runContexts.registry,
|
|
4072
|
+
cwd: workingDirectory,
|
|
4073
|
+
nodeExecutable: process.execPath,
|
|
4074
|
+
socketPath: management.socketPath
|
|
4075
|
+
}) : void 0;
|
|
4076
|
+
const bashTool = input.definition.runtimeToolGrantSet.toolIds.includes("bash") ? managedContext?.tool ?? createPiBashTool(workingDirectory, resources.bashToolOptions) : void 0;
|
|
4072
4077
|
const customTools = [
|
|
4073
|
-
...
|
|
4078
|
+
...bashTool ? [bashTool] : [],
|
|
4074
4079
|
...createPiSkillReadTools({
|
|
4075
4080
|
cwd: workingDirectory,
|
|
4076
4081
|
runtimeToolIds: input.definition.runtimeToolGrantSet.toolIds,
|
|
@@ -4088,7 +4093,7 @@ async function createRivusDeploymentAdapters(context) {
|
|
|
4088
4093
|
}),
|
|
4089
4094
|
...skillRuntime.tool ? [skillRuntime.tool] : []
|
|
4090
4095
|
];
|
|
4091
|
-
const activeToolNames = resolvePiSessionToolNames(
|
|
4096
|
+
const activeToolNames = resolvePiSessionToolNames(bashTool ? input.definition.runtimeToolGrantSet.toolIds.filter((toolId) => toolId !== "bash") : input.definition.runtimeToolGrantSet.toolIds, customTools);
|
|
4092
4097
|
const result = await createAgentSession(resources.withSessionOptions({
|
|
4093
4098
|
...piOptions,
|
|
4094
4099
|
...management?.enabled ? management.runtime.getSessionOptions() : {},
|
|
@@ -4115,6 +4120,7 @@ async function createRivusDeploymentAdapters(context) {
|
|
|
4115
4120
|
} });
|
|
4116
4121
|
const unregisterModels = management?.enabled ? management.runtime.registerSessionRegistry(sessionRegistry) : void 0;
|
|
4117
4122
|
const loop = createPiAgentLoop({
|
|
4123
|
+
supportsSteering: true,
|
|
4118
4124
|
disposeSessionAfterRun: false,
|
|
4119
4125
|
...management?.enabled ? { runBoundary: management.boundary } : {},
|
|
4120
4126
|
...telemetry ? { modelContentObserver: telemetry.modelContentObserver } : {},
|
|
@@ -416,7 +416,8 @@ interface AgentLoopInput {
|
|
|
416
416
|
readonly steering?: AgentSteeringChannel;
|
|
417
417
|
}
|
|
418
418
|
interface AgentSteeringChannel {
|
|
419
|
-
next(): Promise<string>;
|
|
419
|
+
next(signal?: AbortSignal): Promise<string>;
|
|
420
|
+
close?(): Promise<readonly string[]>;
|
|
420
421
|
}
|
|
421
422
|
interface AgentLoop {
|
|
422
423
|
run(input: AgentLoopInput): Stream.Stream<AgentLoopEvent, unknown>;
|
|
@@ -117,14 +117,20 @@ function toEffectAgentLoopInput(input) {
|
|
|
117
117
|
const { steering, ...rest } = input;
|
|
118
118
|
return {
|
|
119
119
|
...rest,
|
|
120
|
-
...steering ? { steering: {
|
|
120
|
+
...steering ? { steering: {
|
|
121
|
+
...steering.close ? { close: () => Effect.promise(() => steering.close()) } : {},
|
|
122
|
+
next: () => Effect.promise((signal) => steering.next(signal))
|
|
123
|
+
} } : {}
|
|
121
124
|
};
|
|
122
125
|
}
|
|
123
126
|
function toCompatibilityAgentLoopInput(input) {
|
|
124
127
|
const { steering, ...rest } = input;
|
|
125
128
|
return {
|
|
126
129
|
...rest,
|
|
127
|
-
...steering ? { steering: {
|
|
130
|
+
...steering ? { steering: {
|
|
131
|
+
...steering.close ? { close: () => Effect.runPromise(steering.close()) } : {},
|
|
132
|
+
next: (signal) => Effect.runPromise(steering.next(), { signal })
|
|
133
|
+
} } : {}
|
|
128
134
|
};
|
|
129
135
|
}
|
|
130
136
|
function isPromiseLike(value) {
|
package/dist/chunks/index.d.ts
CHANGED
|
@@ -1808,6 +1808,7 @@ interface FeishuCardRolloverOptions {
|
|
|
1808
1808
|
readonly createSuccessor: (request: FeishuCardRolloverSuccessorRequest) => Effect.Effect<FeishuCardRolloverTarget, unknown>;
|
|
1809
1809
|
readonly leaseMs?: number;
|
|
1810
1810
|
readonly observe?: (event: FeishuCardRolloverEvent) => void;
|
|
1811
|
+
readonly progressDisplay?: ConversationProgressDisplay;
|
|
1811
1812
|
readonly publisher: FeishuCardRolloverStreamPublisher;
|
|
1812
1813
|
readonly store: FeishuCardPresentationStore;
|
|
1813
1814
|
}
|
|
@@ -2989,6 +2990,10 @@ interface PiSessionManager {
|
|
|
2989
2990
|
}
|
|
2990
2991
|
interface PiAgentSession {
|
|
2991
2992
|
abort?: () => Promise<void> | void;
|
|
2993
|
+
/** Native queue operations required when the loop opts into steering. */
|
|
2994
|
+
steer?(text: string): Promise<void> | void;
|
|
2995
|
+
readonly isStreaming?: boolean;
|
|
2996
|
+
clearQueue?(): unknown;
|
|
2992
2997
|
setModel?(model: unknown): Promise<void> | void;
|
|
2993
2998
|
setThinkingLevel?(level: string): void;
|
|
2994
2999
|
waitForIdle?(): Promise<void>;
|
|
@@ -3003,6 +3008,7 @@ interface PiCreateAgentSessionResult {
|
|
|
3003
3008
|
readonly session: PiAgentSession;
|
|
3004
3009
|
}
|
|
3005
3010
|
interface PiSdkAgentLoopOptions<TSessionOptions = unknown> {
|
|
3011
|
+
readonly supportsSteering?: boolean;
|
|
3006
3012
|
readonly createAgentSession: (options: TSessionOptions | undefined) => Promise<PiCreateAgentSessionResult> | PiCreateAgentSessionResult;
|
|
3007
3013
|
readonly sessionOptions?: TSessionOptions;
|
|
3008
3014
|
}
|
|
@@ -3018,6 +3024,7 @@ interface PiAgentSessionHandle {
|
|
|
3018
3024
|
readonly refreshResources?: () => Promise<void> | void;
|
|
3019
3025
|
}
|
|
3020
3026
|
interface PiAgentLoopOptions {
|
|
3027
|
+
readonly supportsSteering?: boolean;
|
|
3021
3028
|
readonly resolveSession: (input: AgentLoopInput) => Promise<PiAgentSessionHandle> | PiAgentSessionHandle;
|
|
3022
3029
|
readonly modelContentObserver?: AgentModelContentObserver;
|
|
3023
3030
|
readonly disposeSessionAfterRun?: boolean;
|
package/dist/chunks/pi.js
CHANGED
|
@@ -4,9 +4,39 @@ import { t as createSha256Digest } from "./sha256-digest.js";
|
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
6
6
|
import { isAbsolute, join, relative } from "node:path";
|
|
7
|
-
import { DefaultResourceLoader, SettingsManager, createReadToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { DefaultResourceLoader, SettingsManager, createBashToolDefinition, createReadToolDefinition, defineTool } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Unsafe } from "typebox";
|
|
9
9
|
import { realpathSync, statSync } from "node:fs";
|
|
10
|
+
//#region src/adapters/pi/tool-execution/pi-bash-tool.ts
|
|
11
|
+
const DEFAULT_TIMEOUT_SECONDS = 120;
|
|
12
|
+
/** Keep Pi's native execution, rendering and abort cleanup with a bounded default. */
|
|
13
|
+
function createPiBashTool(cwd, options) {
|
|
14
|
+
const { commandPrefix, shellPath, spawnHook } = options ?? {};
|
|
15
|
+
const tool = createBashToolDefinition(cwd, {
|
|
16
|
+
...commandPrefix === void 0 ? {} : { commandPrefix },
|
|
17
|
+
...shellPath === void 0 ? {} : { shellPath },
|
|
18
|
+
...spawnHook === void 0 ? {} : { spawnHook }
|
|
19
|
+
});
|
|
20
|
+
return defineTool({
|
|
21
|
+
...tool,
|
|
22
|
+
description: `${tool.description} Defaults to ${DEFAULT_TIMEOUT_SECONDS} seconds when timeout is omitted; set an explicit positive timeout for longer commands.`,
|
|
23
|
+
parameters: {
|
|
24
|
+
...tool.parameters,
|
|
25
|
+
properties: {
|
|
26
|
+
...tool.parameters.properties,
|
|
27
|
+
timeout: {
|
|
28
|
+
...tool.parameters.properties.timeout,
|
|
29
|
+
description: `Timeout in seconds (optional, defaults to ${DEFAULT_TIMEOUT_SECONDS})`
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
execute: (id, args, signal, onUpdate, context) => tool.execute(id, {
|
|
34
|
+
...args,
|
|
35
|
+
timeout: args.timeout === void 0 ? DEFAULT_TIMEOUT_SECONDS : args.timeout
|
|
36
|
+
}, signal, onUpdate, context)
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
10
40
|
//#region src/adapters/pi/skills/pi-skill-read-tool.ts
|
|
11
41
|
var ProjectSkillReadDenied = class extends Error {
|
|
12
42
|
name = "ProjectSkillReadDenied";
|
|
@@ -165,12 +195,19 @@ const PI_BEHAVIOR_SETTING_KEYS = Object.freeze([
|
|
|
165
195
|
]);
|
|
166
196
|
async function createPiSessionResources(options) {
|
|
167
197
|
const settingsManager = createSanitizedSettingsManager(options.cwd, options.agentDir, options.settingsOverrides);
|
|
198
|
+
const commandPrefix = settingsManager.getShellCommandPrefix();
|
|
199
|
+
const shellPath = settingsManager.getShellPath();
|
|
200
|
+
const bashToolOptions = commandPrefix === void 0 && shellPath === void 0 ? void 0 : Object.freeze({
|
|
201
|
+
...commandPrefix === void 0 ? {} : { commandPrefix },
|
|
202
|
+
...shellPath === void 0 ? {} : { shellPath }
|
|
203
|
+
});
|
|
168
204
|
let skillPaths = await resolveSkillPaths(options);
|
|
169
205
|
let currentResourceLoader = createResourceLoader(options, settingsManager, skillPaths);
|
|
170
206
|
await currentResourceLoader.reload();
|
|
171
207
|
let skillNames = validatePiSkillCatalog(currentResourceLoader.getSkills());
|
|
172
208
|
const resourceLoader = createResourceLoaderFacade(() => currentResourceLoader, settingsManager);
|
|
173
209
|
return {
|
|
210
|
+
...bashToolOptions === void 0 ? {} : { bashToolOptions },
|
|
174
211
|
get skillNames() {
|
|
175
212
|
return skillNames;
|
|
176
213
|
},
|
|
@@ -366,4 +403,4 @@ function createPiToolNameResolver(tools) {
|
|
|
366
403
|
return createPiToolNameResolver$1(tools);
|
|
367
404
|
}
|
|
368
405
|
//#endregion
|
|
369
|
-
export { InvalidPiSkillSource as a, validatePiSkillCatalog as c, createPiProjectSkillReadTool as d, createPiSkillReadTool as f, resolvePiSessionToolNames as i, validatePiSkillCommand as l, createPiToolProxyDefinitions as n, resolvePiSkillSources as o, createPiSkillReadTools as p, createPiSessionResources as r, InvalidPiSkillCatalog as s, createPiToolNameResolver as t, ProjectSkillReadDenied as u };
|
|
406
|
+
export { InvalidPiSkillSource as a, validatePiSkillCatalog as c, createPiProjectSkillReadTool as d, createPiSkillReadTool as f, resolvePiSessionToolNames as i, validatePiSkillCommand as l, createPiBashTool as m, createPiToolProxyDefinitions as n, resolvePiSkillSources as o, createPiSkillReadTools as p, createPiSessionResources as r, InvalidPiSkillCatalog as s, createPiToolNameResolver as t, ProjectSkillReadDenied as u };
|
|
@@ -2350,7 +2350,7 @@ function createProcessDeploymentAdapterPorts(factories, runEffect) {
|
|
|
2350
2350
|
runtimeFactory: { create: (input) => Effect.tryPromise({
|
|
2351
2351
|
try: () => Promise.resolve(factories.createRuntime(input)),
|
|
2352
2352
|
catch: toError$3
|
|
2353
|
-
}).pipe(Effect.map((runtime) =>
|
|
2353
|
+
}).pipe(Effect.map((runtime) => toEffectAgentRuntime(runtime, runEffect))) }
|
|
2354
2354
|
};
|
|
2355
2355
|
}
|
|
2356
2356
|
function adaptEndpointFactory(create, input, runEffect) {
|
|
@@ -2408,19 +2408,6 @@ function adaptBackgroundSessionFactory(create, input, runEffect) {
|
|
|
2408
2408
|
catch: toError$3
|
|
2409
2409
|
});
|
|
2410
2410
|
}
|
|
2411
|
-
function toDeploymentRuntime(runtime, runEffect) {
|
|
2412
|
-
const adapted = toEffectAgentRuntime(runtime, runEffect);
|
|
2413
|
-
const cancel = adapted.cancel?.bind(adapted);
|
|
2414
|
-
const dispose = adapted.dispose?.bind(adapted);
|
|
2415
|
-
const steer = adapted.steer?.bind(adapted);
|
|
2416
|
-
return {
|
|
2417
|
-
...adapted.concurrency ? { concurrency: adapted.concurrency } : {},
|
|
2418
|
-
...cancel ? { cancel: (input) => cancel(input).pipe(Effect.mapError(toError$3)) } : {},
|
|
2419
|
-
...dispose ? { dispose: () => dispose().pipe(Effect.mapError(toError$3)) } : {},
|
|
2420
|
-
run: (input) => adapted.run(input).pipe(Effect.mapError(toError$3)),
|
|
2421
|
-
...steer ? { steer: (input) => steer(input).pipe(Effect.mapError(toError$3)) } : {}
|
|
2422
|
-
};
|
|
2423
|
-
}
|
|
2424
2411
|
function toLifecycleAdapter(adapter) {
|
|
2425
2412
|
return {
|
|
2426
2413
|
running: () => adapter.running(),
|
package/dist/chunks/src.js
CHANGED
|
@@ -3423,10 +3423,17 @@ function toDomainEvent(runId, sessionKey, event, occurredAt) {
|
|
|
3423
3423
|
//#endregion
|
|
3424
3424
|
//#region src/core/application/agent-execution/execution/agent-harness.ts
|
|
3425
3425
|
function createSteeringChannel() {
|
|
3426
|
-
return Queue.unbounded().pipe(Effect.map((queue) =>
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3426
|
+
return Queue.unbounded().pipe(Effect.map((queue) => {
|
|
3427
|
+
let closed = false;
|
|
3428
|
+
return {
|
|
3429
|
+
next: () => Queue.take(queue),
|
|
3430
|
+
close: () => Effect.gen(function* () {
|
|
3431
|
+
closed = true;
|
|
3432
|
+
return Array.from(yield* Queue.takeAll(queue));
|
|
3433
|
+
}),
|
|
3434
|
+
push: (text) => Effect.sync(() => !closed && Queue.unsafeOffer(queue, text))
|
|
3435
|
+
};
|
|
3436
|
+
}));
|
|
3430
3437
|
}
|
|
3431
3438
|
function createAgentHarness$1(options) {
|
|
3432
3439
|
if (options.runTimeoutMs !== void 0 && (!Number.isSafeInteger(options.runTimeoutMs) || options.runTimeoutMs < 1)) throw new Error("Agent run timeout must be a positive integer");
|
|
@@ -3643,8 +3650,7 @@ function createAgentHarness$1(options) {
|
|
|
3643
3650
|
const requestSteering = (runId, text) => Effect.gen(function* () {
|
|
3644
3651
|
const normalized = text.trim();
|
|
3645
3652
|
if (!normalized || !options.loop.supportsSteering || !activeRun || activeRun.runId !== runId || !activeCancellation || activeCancellation.requested) return false;
|
|
3646
|
-
yield* activeCancellation.steering.push(normalized);
|
|
3647
|
-
return true;
|
|
3653
|
+
return yield* activeCancellation.steering.push(normalized);
|
|
3648
3654
|
});
|
|
3649
3655
|
const requestSessionCancellation = (sessionKey, runId, reason) => {
|
|
3650
3656
|
if (!activeRun || activeRun.sessionKey !== sessionKey) return Effect.succeed(false);
|
|
@@ -5588,9 +5594,207 @@ function cardPresentationLeaseDeadline(at, leaseMs) {
|
|
|
5588
5594
|
return new Date(atMs + validateCardPresentationLeaseMs(leaseMs)).toISOString();
|
|
5589
5595
|
}
|
|
5590
5596
|
//#endregion
|
|
5597
|
+
//#region src/adapters/feishu/run-presentation/feishu-run-presentation-renderer.ts
|
|
5598
|
+
function renderFeishuRunProgress(presentation) {
|
|
5599
|
+
const visible = presentation.steps.filter(isVisibleStep);
|
|
5600
|
+
const running = [...visible].reverse().find((step) => step.status === "running");
|
|
5601
|
+
const title = visible.length === 0 ? "执行过程 · 展开查看" : presentation.phase === "running" && running ? `执行过程 · ${runningLabel(running)}` : `执行过程 · ${visible.length} 段 · ${presentation.totalToolCallCount} 次工具调用`;
|
|
5602
|
+
return {
|
|
5603
|
+
markdown: [...presentation.omittedStepCount > 0 ? [`> 另有 ${presentation.omittedStepCount} 个较早步骤未展示`] : [], ...renderProgressSteps(visible)].join("\n\n") || "正在准备执行…",
|
|
5604
|
+
title
|
|
5605
|
+
};
|
|
5606
|
+
}
|
|
5607
|
+
function renderFeishuRunTimeline(presentation, options) {
|
|
5608
|
+
const visible = presentation.steps.filter(isVisibleStep);
|
|
5609
|
+
const elements = [];
|
|
5610
|
+
for (let index = 0; index < visible.length;) {
|
|
5611
|
+
const step = visible[index];
|
|
5612
|
+
if (step.kind !== "tool") {
|
|
5613
|
+
elements.push(...renderTimelineStep(step, options.expanded));
|
|
5614
|
+
index += 1;
|
|
5615
|
+
continue;
|
|
5616
|
+
}
|
|
5617
|
+
const tools = [];
|
|
5618
|
+
while (index < visible.length && visible[index]?.kind === "tool") {
|
|
5619
|
+
tools.push(visible[index]);
|
|
5620
|
+
index += 1;
|
|
5621
|
+
}
|
|
5622
|
+
elements.push(renderToolSequence(tools, options.expanded));
|
|
5623
|
+
}
|
|
5624
|
+
return {
|
|
5625
|
+
elements,
|
|
5626
|
+
inspectable: visible.some((step) => step.kind === "skill" || step.kind === "tool")
|
|
5627
|
+
};
|
|
5628
|
+
}
|
|
5629
|
+
function renderFeishuVisibleAnswer(presentation, currentText) {
|
|
5630
|
+
const committed = presentation?.steps.filter((step) => step.kind === "assistant").map((step) => step.text.trim()).filter(Boolean) ?? [];
|
|
5631
|
+
const current = currentText.trim();
|
|
5632
|
+
return [...committed, ...current ? [current] : []].join("\n\n");
|
|
5633
|
+
}
|
|
5634
|
+
function renderProgressSteps(steps) {
|
|
5635
|
+
const rendered = [];
|
|
5636
|
+
for (let index = 0; index < steps.length;) {
|
|
5637
|
+
const step = steps[index];
|
|
5638
|
+
if (step.kind !== "tool") {
|
|
5639
|
+
rendered.push(renderStep(step));
|
|
5640
|
+
index += 1;
|
|
5641
|
+
continue;
|
|
5642
|
+
}
|
|
5643
|
+
const tools = [];
|
|
5644
|
+
while (index < steps.length && steps[index]?.kind === "tool") {
|
|
5645
|
+
tools.push(steps[index]);
|
|
5646
|
+
index += 1;
|
|
5647
|
+
}
|
|
5648
|
+
rendered.push(tools.map(renderToolSummary).join("\n"));
|
|
5649
|
+
}
|
|
5650
|
+
return rendered;
|
|
5651
|
+
}
|
|
5652
|
+
function renderToolSequence(tools, expanded) {
|
|
5653
|
+
if (tools.length === 1) return {
|
|
5654
|
+
content: renderToolSummary(tools[0]),
|
|
5655
|
+
tag: "markdown"
|
|
5656
|
+
};
|
|
5657
|
+
return collapsiblePanel({
|
|
5658
|
+
content: tools.map(renderToolSummary).join("\n"),
|
|
5659
|
+
expanded: expanded || tools.some((tool) => tool.status === "running"),
|
|
5660
|
+
title: toolSequenceTitle(tools)
|
|
5661
|
+
});
|
|
5662
|
+
}
|
|
5663
|
+
function toolSequenceTitle(tools) {
|
|
5664
|
+
if ([...tools].reverse().find((tool) => tool.status === "running")) return `执行过程 · ${tools.length} 个工具 · 进行中`;
|
|
5665
|
+
if (tools.some((tool) => tool.status === "failed")) return `执行过程 · ${tools.length} 个工具 · 有失败`;
|
|
5666
|
+
const totalDuration = tools.reduce((total, tool) => total + (tool.durationMs ?? 0), 0);
|
|
5667
|
+
return totalDuration > 0 ? `执行过程 · ${tools.length} 个工具 · 已完成 · ${formatDuration$1(totalDuration)}` : `执行过程 · ${tools.length} 个工具 · 已完成`;
|
|
5668
|
+
}
|
|
5669
|
+
function renderToolSummary(step) {
|
|
5670
|
+
const skill = step.skillId ? ` · ${escapeInline(step.skillId)}` : "";
|
|
5671
|
+
const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
|
|
5672
|
+
return `- ${statusIcon(step.status)} **${escapeInline(toolAction(step.toolName))}**${skill}${duration}`;
|
|
5673
|
+
}
|
|
5674
|
+
function renderTimelineStep(step, expandCompleted) {
|
|
5675
|
+
switch (step.kind) {
|
|
5676
|
+
case "assistant": return [{
|
|
5677
|
+
content: step.text,
|
|
5678
|
+
margin: "0px",
|
|
5679
|
+
tag: "markdown",
|
|
5680
|
+
text_align: "left",
|
|
5681
|
+
text_size: "normal"
|
|
5682
|
+
}];
|
|
5683
|
+
case "skill": return [collapsiblePanel({
|
|
5684
|
+
content: skillDetails(step),
|
|
5685
|
+
expanded: step.status === "running" || expandCompleted,
|
|
5686
|
+
title: `${step.skillId} · ${step.status === "running" ? "读取中" : step.status === "failed" ? "读取失败" : "已读取"}`
|
|
5687
|
+
})];
|
|
5688
|
+
case "tool": return [{
|
|
5689
|
+
content: renderToolSummary(step),
|
|
5690
|
+
tag: "markdown"
|
|
5691
|
+
}];
|
|
5692
|
+
case "model":
|
|
5693
|
+
case "response": return [];
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
function collapsiblePanel(options) {
|
|
5697
|
+
return {
|
|
5698
|
+
background_color: options.expanded ? "blue-50" : "grey-50",
|
|
5699
|
+
elements: [{
|
|
5700
|
+
content: options.content,
|
|
5701
|
+
tag: "markdown",
|
|
5702
|
+
text_align: "left",
|
|
5703
|
+
text_size: "normal"
|
|
5704
|
+
}],
|
|
5705
|
+
expanded: options.expanded,
|
|
5706
|
+
header: {
|
|
5707
|
+
expanded_title: {
|
|
5708
|
+
content: options.title,
|
|
5709
|
+
tag: "plain_text",
|
|
5710
|
+
text_align: "left"
|
|
5711
|
+
},
|
|
5712
|
+
icon: {
|
|
5713
|
+
color: "grey",
|
|
5714
|
+
tag: "standard_icon",
|
|
5715
|
+
token: "down-small-ccm_outlined"
|
|
5716
|
+
},
|
|
5717
|
+
icon_expanded_angle: -180,
|
|
5718
|
+
icon_position: "right",
|
|
5719
|
+
padding: "8px 12px 8px 12px",
|
|
5720
|
+
title: {
|
|
5721
|
+
content: options.title,
|
|
5722
|
+
tag: "plain_text",
|
|
5723
|
+
text_align: "left"
|
|
5724
|
+
},
|
|
5725
|
+
width: "fill"
|
|
5726
|
+
},
|
|
5727
|
+
margin: "8px 0px 0px 0px",
|
|
5728
|
+
padding: "12px 12px 12px 12px",
|
|
5729
|
+
tag: "collapsible_panel"
|
|
5730
|
+
};
|
|
5731
|
+
}
|
|
5732
|
+
function skillDetails(step) {
|
|
5733
|
+
const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
|
|
5734
|
+
return [`**${statusLabel(step.status)}**${duration}`, `这一步:读取并应用技能 \`${escapeInline(step.skillId)}\` 的任务说明。`].join("\n\n");
|
|
5735
|
+
}
|
|
5736
|
+
function statusLabel(status) {
|
|
5737
|
+
switch (status) {
|
|
5738
|
+
case "completed": return "已完成";
|
|
5739
|
+
case "failed": return "未完成";
|
|
5740
|
+
case "running": return "进行中";
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
function toolAction(toolName) {
|
|
5744
|
+
const normalized = toolName.toLowerCase();
|
|
5745
|
+
const executable = normalized.split(/[\\/]/u).at(-1) ?? normalized;
|
|
5746
|
+
if (executable === "bash" || executable === "exec" || executable === "exec_command") return "执行命令";
|
|
5747
|
+
if (normalized.includes("read") && normalized.includes("file")) return "读取文件";
|
|
5748
|
+
if (normalized.includes("write") && normalized.includes("file")) return "写入文件";
|
|
5749
|
+
if (normalized.includes("search") || normalized === "rg") return "搜索内容";
|
|
5750
|
+
if (normalized.includes("fetch") || normalized.includes("http")) return "访问服务";
|
|
5751
|
+
return `调用 ${toolName.replace(/^.*[\\/]/u, "")}`;
|
|
5752
|
+
}
|
|
5753
|
+
function renderStep(step) {
|
|
5754
|
+
const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
|
|
5755
|
+
switch (step.kind) {
|
|
5756
|
+
case "assistant": return step.text;
|
|
5757
|
+
case "model": return `- ${statusIcon(step.status)} ${step.label}${duration}`;
|
|
5758
|
+
case "skill": return `- ${statusIcon(step.status)} ${step.label} \`${escapeInline(step.skillId)}\`${duration}`;
|
|
5759
|
+
case "response": return `- ${statusIcon(step.status)} ${step.label}${duration}`;
|
|
5760
|
+
case "tool": return renderToolSummary(step);
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
function isVisibleStep(step) {
|
|
5764
|
+
return step.kind === "assistant" || step.kind === "skill" || step.kind === "tool";
|
|
5765
|
+
}
|
|
5766
|
+
function runningLabel(step) {
|
|
5767
|
+
switch (step.kind) {
|
|
5768
|
+
case "assistant": return "正在说明进展";
|
|
5769
|
+
case "model": return "正在分析";
|
|
5770
|
+
case "skill": return "正在读取技能";
|
|
5771
|
+
case "tool": return `正在调用 ${step.toolName}`;
|
|
5772
|
+
case "response": return "正在整理回答";
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
function statusIcon(status) {
|
|
5776
|
+
switch (status) {
|
|
5777
|
+
case "completed": return "✅";
|
|
5778
|
+
case "failed": return "⚠️";
|
|
5779
|
+
case "running": return "⏳";
|
|
5780
|
+
}
|
|
5781
|
+
}
|
|
5782
|
+
function formatDuration$1(milliseconds) {
|
|
5783
|
+
if (milliseconds < 1e3) return `${milliseconds} 毫秒`;
|
|
5784
|
+
if (milliseconds < 6e4) {
|
|
5785
|
+
const seconds = milliseconds / 1e3;
|
|
5786
|
+
return `${Number(seconds.toFixed(seconds < 10 ? 1 : 0))} 秒`;
|
|
5787
|
+
}
|
|
5788
|
+
return `${Math.floor(milliseconds / 6e4)} 分 ${Math.floor(milliseconds % 6e4 / 1e3)} 秒`;
|
|
5789
|
+
}
|
|
5790
|
+
function escapeInline(value) {
|
|
5791
|
+
return value.replace(/`/g, "ˋ");
|
|
5792
|
+
}
|
|
5793
|
+
//#endregion
|
|
5591
5794
|
//#region src/adapters/feishu/card-presentation/rollover/feishu-card-rollover.ts
|
|
5592
5795
|
function createFeishuCardRollover(options) {
|
|
5593
5796
|
const leaseMs = validateCardPresentationLeaseMs(options.leaseMs ?? 51e4);
|
|
5797
|
+
const progressDisplay = options.progressDisplay ?? "collapsed";
|
|
5594
5798
|
const counters = createCounters();
|
|
5595
5799
|
const latestPresentationByRun = /* @__PURE__ */ new Map();
|
|
5596
5800
|
const latestTextByRun = /* @__PURE__ */ new Map();
|
|
@@ -5598,6 +5802,7 @@ function createFeishuCardRollover(options) {
|
|
|
5598
5802
|
const liveRuns = /* @__PURE__ */ new Map();
|
|
5599
5803
|
const semaphore = Effect.unsafeMakeSemaphore(1);
|
|
5600
5804
|
const exclusive = (effect) => semaphore.withPermits(1)(effect);
|
|
5805
|
+
const exclusiveDeferred = (factory) => semaphore.withPermits(1)(Effect.suspend(factory));
|
|
5601
5806
|
const record = (event, at) => {
|
|
5602
5807
|
counters[event.type] += 1;
|
|
5603
5808
|
options.observe?.({
|
|
@@ -5606,36 +5811,86 @@ function createFeishuCardRollover(options) {
|
|
|
5606
5811
|
});
|
|
5607
5812
|
};
|
|
5608
5813
|
const recordNow = (event) => options.clock.now.pipe(Effect.map((at) => record(event, at)));
|
|
5814
|
+
const failHandoffAndRecord = (input) => options.store.failHandoff({
|
|
5815
|
+
failedAt: input.failedAt,
|
|
5816
|
+
runId: input.runId
|
|
5817
|
+
}).pipe(Effect.catchAll((storeError) => recordNow({
|
|
5818
|
+
error: storeError,
|
|
5819
|
+
runId: input.runId,
|
|
5820
|
+
type: "presentation_write_failed"
|
|
5821
|
+
})), Effect.flatMap(() => recordNow({
|
|
5822
|
+
cardId: input.predecessor.cardId,
|
|
5823
|
+
error: input.error,
|
|
5824
|
+
generation: input.generation,
|
|
5825
|
+
presentationId: input.predecessor.presentationId,
|
|
5826
|
+
runId: input.runId,
|
|
5827
|
+
...input.successorCardId === void 0 ? {} : { successorCardId: input.successorCardId },
|
|
5828
|
+
type: input.type
|
|
5829
|
+
})), Effect.as({ error: input.error }));
|
|
5609
5830
|
const leaseDeadline = (at) => cardPresentationLeaseDeadline(at.toISOString(), leaseMs);
|
|
5610
5831
|
const presentationId = (runId, generation) => `${runId}#${generation}`;
|
|
5611
|
-
const publishProgress = (action) => Effect.
|
|
5832
|
+
const publishProgress = (action) => Effect.gen(function* () {
|
|
5612
5833
|
const chain = options.store.chain(action.runId);
|
|
5613
|
-
if (chain
|
|
5614
|
-
|
|
5615
|
-
else {
|
|
5616
|
-
latestPresentationByRun.set(action.runId, action.presentation);
|
|
5617
|
-
latestTextByRun.set(action.runId, action.presentation.answer.text);
|
|
5618
|
-
}
|
|
5619
|
-
const run = liveRuns.get(action.runId);
|
|
5620
|
-
if (!run) return options.publisher.publish(action);
|
|
5621
|
-
const baseline = generationBaselineByRun.get(action.runId);
|
|
5622
|
-
const text = projectText(latestTextByRun.get(action.runId), baseline?.text);
|
|
5623
|
-
const presentation = projectPresentation(latestPresentationByRun.get(action.runId), baseline);
|
|
5624
|
-
return options.publisher.publish({
|
|
5625
|
-
...presentation === void 0 ? {} : { presentation },
|
|
5834
|
+
if (!chain) {
|
|
5835
|
+
yield* recordNow({
|
|
5626
5836
|
runId: action.runId,
|
|
5627
|
-
|
|
5628
|
-
...text === void 0 ? {} : { text },
|
|
5629
|
-
type: "update_presentation"
|
|
5837
|
+
type: "stale_update_dropped"
|
|
5630
5838
|
});
|
|
5839
|
+
return;
|
|
5631
5840
|
}
|
|
5632
|
-
|
|
5633
|
-
|
|
5841
|
+
const previousPresentation = latestPresentationByRun.get(action.runId);
|
|
5842
|
+
const visibleChanged = hasVisibleIncrement(previousPresentation, latestTextByRun.get(action.runId), action.type === "update_progress" ? action.presentation : previousPresentation, action.type === "update_text" ? action.text : action.presentation.answer.text, progressDisplay);
|
|
5843
|
+
const run = liveRuns.get(action.runId);
|
|
5844
|
+
const acceptsProgress = acceptsCardPresentationProgress(chain);
|
|
5845
|
+
if (!acceptsProgress && !(run !== void 0)) {
|
|
5846
|
+
yield* recordNow({
|
|
5634
5847
|
generation: chain.activeGeneration,
|
|
5635
|
-
presentationId: chain.activePresentationId
|
|
5636
|
-
|
|
5848
|
+
presentationId: chain.activePresentationId,
|
|
5849
|
+
runId: action.runId,
|
|
5850
|
+
type: "stale_update_dropped"
|
|
5851
|
+
});
|
|
5852
|
+
return;
|
|
5853
|
+
}
|
|
5854
|
+
if (run && acceptsProgress) {
|
|
5855
|
+
const now = yield* options.clock.now;
|
|
5856
|
+
if (visibleChanged && isCardPresentationHandoffDue(chain, now.toISOString())) {
|
|
5857
|
+
if (yield* options.publisher.flush(action.runId).pipe(Effect.map(() => true), Effect.catchAll((error) => recordNow({
|
|
5858
|
+
error,
|
|
5859
|
+
runId: action.runId,
|
|
5860
|
+
type: "presentation_write_failed"
|
|
5861
|
+
}).pipe(Effect.as(false))))) yield* runHandoff(action.runId, true);
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5864
|
+
if (action.type === "update_text") latestTextByRun.set(action.runId, action.text);
|
|
5865
|
+
else {
|
|
5866
|
+
latestPresentationByRun.set(action.runId, action.presentation);
|
|
5867
|
+
latestTextByRun.set(action.runId, action.presentation.answer.text);
|
|
5868
|
+
}
|
|
5869
|
+
const currentChain = options.store.chain(action.runId);
|
|
5870
|
+
if (!currentChain || !acceptsCardPresentationProgress(currentChain)) {
|
|
5871
|
+
yield* recordNow({
|
|
5872
|
+
...currentChain ? {
|
|
5873
|
+
generation: currentChain.activeGeneration,
|
|
5874
|
+
presentationId: currentChain.activePresentationId
|
|
5875
|
+
} : {},
|
|
5876
|
+
runId: action.runId,
|
|
5877
|
+
type: "stale_update_dropped"
|
|
5878
|
+
});
|
|
5879
|
+
return;
|
|
5880
|
+
}
|
|
5881
|
+
if (!run) {
|
|
5882
|
+
yield* options.publisher.publish(action);
|
|
5883
|
+
return;
|
|
5884
|
+
}
|
|
5885
|
+
const baseline = generationBaselineByRun.get(action.runId);
|
|
5886
|
+
const text = projectText(latestTextByRun.get(action.runId), baseline?.text);
|
|
5887
|
+
const presentation = projectPresentation(latestPresentationByRun.get(action.runId), baseline);
|
|
5888
|
+
yield* options.publisher.publish({
|
|
5889
|
+
...presentation === void 0 ? {} : { presentation },
|
|
5637
5890
|
runId: action.runId,
|
|
5638
|
-
|
|
5891
|
+
sessionKey: run.sessionKey,
|
|
5892
|
+
...text === void 0 ? {} : { text },
|
|
5893
|
+
type: "update_presentation"
|
|
5639
5894
|
});
|
|
5640
5895
|
});
|
|
5641
5896
|
const publishTerminal = (action) => {
|
|
@@ -5675,7 +5930,7 @@ function createFeishuCardRollover(options) {
|
|
|
5675
5930
|
default: return publishTerminal(action);
|
|
5676
5931
|
}
|
|
5677
5932
|
};
|
|
5678
|
-
const runHandoff = (runId) => Effect.gen(function* () {
|
|
5933
|
+
const runHandoff = (runId, triggeredByVisibleProgress) => Effect.gen(function* () {
|
|
5679
5934
|
const run = liveRuns.get(runId);
|
|
5680
5935
|
if (!run) return {
|
|
5681
5936
|
reason: "not-live",
|
|
@@ -5687,6 +5942,10 @@ function createFeishuCardRollover(options) {
|
|
|
5687
5942
|
reason: "not-due",
|
|
5688
5943
|
status: "skipped"
|
|
5689
5944
|
};
|
|
5945
|
+
if (!triggeredByVisibleProgress) return {
|
|
5946
|
+
reason: "not-due",
|
|
5947
|
+
status: "skipped"
|
|
5948
|
+
};
|
|
5690
5949
|
const start = yield* options.store.beginHandoff({
|
|
5691
5950
|
handoffAt: startedAt.toISOString(),
|
|
5692
5951
|
runId
|
|
@@ -5714,27 +5973,20 @@ function createFeishuCardRollover(options) {
|
|
|
5714
5973
|
generation,
|
|
5715
5974
|
presentationId: successorId,
|
|
5716
5975
|
run
|
|
5717
|
-
}).pipe(Effect.map((target) => ({ target })), Effect.catchAll((error) =>
|
|
5718
|
-
failedAt: startedAt.toISOString(),
|
|
5719
|
-
runId
|
|
5720
|
-
}).pipe(Effect.catchAll((storeError) => recordNow({
|
|
5721
|
-
error: storeError,
|
|
5722
|
-
runId,
|
|
5723
|
-
type: "presentation_write_failed"
|
|
5724
|
-
})), Effect.flatMap(() => recordNow({
|
|
5725
|
-
cardId: predecessor.cardId,
|
|
5976
|
+
}).pipe(Effect.map((target) => ({ target })), Effect.catchAll((error) => failHandoffAndRecord({
|
|
5726
5977
|
error,
|
|
5978
|
+
failedAt: startedAt.toISOString(),
|
|
5727
5979
|
generation,
|
|
5728
|
-
|
|
5980
|
+
predecessor,
|
|
5729
5981
|
runId,
|
|
5730
5982
|
type: "handoff_failed"
|
|
5731
|
-
}))
|
|
5983
|
+
})));
|
|
5732
5984
|
if ("error" in created) return {
|
|
5733
5985
|
error: created.error,
|
|
5734
5986
|
status: "failed"
|
|
5735
5987
|
};
|
|
5736
5988
|
const adoptedAt = yield* options.clock.now;
|
|
5737
|
-
const
|
|
5989
|
+
const completed = yield* options.store.completeHandoff({
|
|
5738
5990
|
runId,
|
|
5739
5991
|
successor: {
|
|
5740
5992
|
cardId: created.target.cardId,
|
|
@@ -5743,7 +5995,20 @@ function createFeishuCardRollover(options) {
|
|
|
5743
5995
|
leaseDeadlineAt: leaseDeadline(adoptedAt),
|
|
5744
5996
|
presentationId: successorId
|
|
5745
5997
|
}
|
|
5746
|
-
})
|
|
5998
|
+
}).pipe(Effect.map((presentation) => ({ presentation })), Effect.catchAll((error) => failHandoffAndRecord({
|
|
5999
|
+
error,
|
|
6000
|
+
failedAt: adoptedAt.toISOString(),
|
|
6001
|
+
generation,
|
|
6002
|
+
predecessor,
|
|
6003
|
+
runId,
|
|
6004
|
+
successorCardId: created.target.cardId,
|
|
6005
|
+
type: "presentation_write_failed"
|
|
6006
|
+
})));
|
|
6007
|
+
if ("error" in completed) return {
|
|
6008
|
+
error: completed.error,
|
|
6009
|
+
status: "failed"
|
|
6010
|
+
};
|
|
6011
|
+
const presentation = completed.presentation;
|
|
5747
6012
|
record({
|
|
5748
6013
|
cardId: predecessor.cardId,
|
|
5749
6014
|
generation,
|
|
@@ -5787,8 +6052,8 @@ function createFeishuCardRollover(options) {
|
|
|
5787
6052
|
return chain !== void 0 && isCardPresentationHandoffDue(chain, nowIso);
|
|
5788
6053
|
});
|
|
5789
6054
|
},
|
|
5790
|
-
flush: (runId) =>
|
|
5791
|
-
handoff: (runId) => exclusive(runHandoff(runId)),
|
|
6055
|
+
flush: (runId) => exclusiveDeferred(() => options.publisher.flush(runId)),
|
|
6056
|
+
handoff: (runId) => exclusive(runHandoff(runId, false)),
|
|
5792
6057
|
observeStreamClosed: (cardId) => {
|
|
5793
6058
|
counters.stream_closed += 1;
|
|
5794
6059
|
options.observe?.({
|
|
@@ -5796,7 +6061,7 @@ function createFeishuCardRollover(options) {
|
|
|
5796
6061
|
type: "stream_closed"
|
|
5797
6062
|
});
|
|
5798
6063
|
},
|
|
5799
|
-
publish: (action) =>
|
|
6064
|
+
publish: (action) => exclusiveDeferred(() => publishAction(action)),
|
|
5800
6065
|
releaseRun: (runId) => Effect.sync(() => {
|
|
5801
6066
|
latestTextByRun.delete(runId);
|
|
5802
6067
|
latestPresentationByRun.delete(runId);
|
|
@@ -5830,16 +6095,26 @@ function projectText(text, baseline) {
|
|
|
5830
6095
|
if (text === baseline) return "";
|
|
5831
6096
|
return text.startsWith(baseline) ? text.slice(baseline.length) : text;
|
|
5832
6097
|
}
|
|
6098
|
+
function hasVisibleIncrement(previousPresentation, previousText, nextPresentation, nextText, progressDisplay) {
|
|
6099
|
+
const baseline = previousPresentation === void 0 && previousText === void 0 ? void 0 : {
|
|
6100
|
+
...previousPresentation === void 0 ? {} : { presentation: previousPresentation },
|
|
6101
|
+
...previousText === void 0 ? {} : { text: previousText }
|
|
6102
|
+
};
|
|
6103
|
+
const text = projectText(nextText, previousText) ?? "";
|
|
6104
|
+
const presentation = projectPresentation(nextPresentation, baseline);
|
|
6105
|
+
if (renderFeishuVisibleAnswer(presentation, text).trim().length > 0) return true;
|
|
6106
|
+
return progressDisplay !== "hidden" && (presentation?.steps.some((step) => step.kind === "skill" || step.kind === "tool") ?? false);
|
|
6107
|
+
}
|
|
5833
6108
|
function projectPresentation(presentation, baseline) {
|
|
5834
6109
|
if (!presentation || !baseline) return presentation;
|
|
5835
|
-
const previous = new Map(baseline.presentation?.steps.map((step) => [step.id,
|
|
6110
|
+
const previous = new Map(baseline.presentation?.steps.filter((step) => step.kind === "assistant" || step.kind === "skill" || step.kind === "tool").map((step) => [step.id, visibleStepSignature(step)]) ?? []);
|
|
5836
6111
|
let skippedCommittedBaselineText = false;
|
|
5837
6112
|
const steps = presentation.steps.filter((step) => {
|
|
5838
6113
|
if (!skippedCommittedBaselineText && step.kind === "assistant" && baseline.text?.trim() && step.text.trim() === baseline.text.trim() && !previous.has(step.id)) {
|
|
5839
6114
|
skippedCommittedBaselineText = true;
|
|
5840
6115
|
return false;
|
|
5841
6116
|
}
|
|
5842
|
-
return previous.get(step.id) !==
|
|
6117
|
+
return (step.kind === "assistant" || step.kind === "skill" || step.kind === "tool") && previous.get(step.id) !== visibleStepSignature(step);
|
|
5843
6118
|
});
|
|
5844
6119
|
const answer = projectText(presentation.answer.text, baseline.text) ?? "";
|
|
5845
6120
|
if (steps.length === 0 && answer.trim() === "") return void 0;
|
|
@@ -5855,6 +6130,25 @@ function projectPresentation(presentation, baseline) {
|
|
|
5855
6130
|
totalToolCallCount: steps.filter((step) => step.kind === "tool").length
|
|
5856
6131
|
};
|
|
5857
6132
|
}
|
|
6133
|
+
function visibleStepSignature(step) {
|
|
6134
|
+
if (step.kind === "assistant") return JSON.stringify({
|
|
6135
|
+
id: step.id,
|
|
6136
|
+
kind: step.kind,
|
|
6137
|
+
text: step.text
|
|
6138
|
+
});
|
|
6139
|
+
return step.kind === "skill" ? JSON.stringify({
|
|
6140
|
+
id: step.id,
|
|
6141
|
+
kind: step.kind,
|
|
6142
|
+
skillId: step.skillId,
|
|
6143
|
+
status: step.status
|
|
6144
|
+
}) : JSON.stringify({
|
|
6145
|
+
id: step.id,
|
|
6146
|
+
kind: step.kind,
|
|
6147
|
+
skillId: step.skillId,
|
|
6148
|
+
status: step.status,
|
|
6149
|
+
toolName: step.toolName
|
|
6150
|
+
});
|
|
6151
|
+
}
|
|
5858
6152
|
function projectTerminalAction(action, baseline) {
|
|
5859
6153
|
if (!baseline || action.type !== "finish" && action.type !== "fail" && action.type !== "cancel") return action;
|
|
5860
6154
|
const presentation = action.presentation ? projectPresentation(action.presentation, baseline) : void 0;
|
|
@@ -5923,20 +6217,20 @@ function createCoalescingFeishuPublisher(options) {
|
|
|
5923
6217
|
const pendingProgressByRun = /* @__PURE__ */ new Map();
|
|
5924
6218
|
const pendingPresentationByRun = /* @__PURE__ */ new Map();
|
|
5925
6219
|
const flushText = (runId, text) => Effect.gen(function* () {
|
|
5926
|
-
pendingTextByRun.delete(runId);
|
|
5927
6220
|
yield* options.publisher.publish({
|
|
5928
6221
|
runId,
|
|
5929
6222
|
text,
|
|
5930
6223
|
type: "update_text"
|
|
5931
6224
|
});
|
|
6225
|
+
if (pendingTextByRun.get(runId) === text) pendingTextByRun.delete(runId);
|
|
5932
6226
|
});
|
|
5933
6227
|
const flushProgress = (runId, presentation) => Effect.gen(function* () {
|
|
5934
|
-
pendingProgressByRun.delete(runId);
|
|
5935
6228
|
yield* options.publisher.publish({
|
|
5936
6229
|
presentation,
|
|
5937
6230
|
runId,
|
|
5938
6231
|
type: "update_progress"
|
|
5939
6232
|
});
|
|
6233
|
+
if (pendingProgressByRun.get(runId) === presentation) pendingProgressByRun.delete(runId);
|
|
5940
6234
|
});
|
|
5941
6235
|
const flushRun = (runId) => {
|
|
5942
6236
|
const text = pendingTextByRun.get(runId);
|
|
@@ -5944,10 +6238,10 @@ function createCoalescingFeishuPublisher(options) {
|
|
|
5944
6238
|
const presentation = pendingPresentationByRun.get(runId);
|
|
5945
6239
|
return Effect.gen(function* () {
|
|
5946
6240
|
if (presentation !== void 0) {
|
|
5947
|
-
pendingPresentationByRun.delete(runId);
|
|
5948
6241
|
yield* options.publisher.publish(presentation);
|
|
5949
|
-
|
|
5950
|
-
|
|
6242
|
+
if (pendingPresentationByRun.get(runId) === presentation) pendingPresentationByRun.delete(runId);
|
|
6243
|
+
if (pendingProgressByRun.get(runId) === progress) pendingProgressByRun.delete(runId);
|
|
6244
|
+
if (pendingTextByRun.get(runId) === text) pendingTextByRun.delete(runId);
|
|
5951
6245
|
return;
|
|
5952
6246
|
}
|
|
5953
6247
|
if (progress !== void 0) yield* flushProgress(runId, progress);
|
|
@@ -6077,203 +6371,6 @@ function createConfiguredFeishuOpenApiClient(options) {
|
|
|
6077
6371
|
});
|
|
6078
6372
|
}
|
|
6079
6373
|
//#endregion
|
|
6080
|
-
//#region src/adapters/feishu/run-presentation/feishu-run-presentation-renderer.ts
|
|
6081
|
-
function renderFeishuRunProgress(presentation) {
|
|
6082
|
-
const visible = presentation.steps.filter(isVisibleStep);
|
|
6083
|
-
const running = [...visible].reverse().find((step) => step.status === "running");
|
|
6084
|
-
const title = visible.length === 0 ? "执行过程 · 展开查看" : presentation.phase === "running" && running ? `执行过程 · ${runningLabel(running)}` : `执行过程 · ${visible.length} 段 · ${presentation.totalToolCallCount} 次工具调用`;
|
|
6085
|
-
return {
|
|
6086
|
-
markdown: [...presentation.omittedStepCount > 0 ? [`> 另有 ${presentation.omittedStepCount} 个较早步骤未展示`] : [], ...renderProgressSteps(visible)].join("\n\n") || "正在准备执行…",
|
|
6087
|
-
title
|
|
6088
|
-
};
|
|
6089
|
-
}
|
|
6090
|
-
function renderFeishuRunTimeline(presentation, options) {
|
|
6091
|
-
const visible = presentation.steps.filter(isVisibleStep);
|
|
6092
|
-
const elements = [];
|
|
6093
|
-
for (let index = 0; index < visible.length;) {
|
|
6094
|
-
const step = visible[index];
|
|
6095
|
-
if (step.kind !== "tool") {
|
|
6096
|
-
elements.push(...renderTimelineStep(step, options.expanded));
|
|
6097
|
-
index += 1;
|
|
6098
|
-
continue;
|
|
6099
|
-
}
|
|
6100
|
-
const tools = [];
|
|
6101
|
-
while (index < visible.length && visible[index]?.kind === "tool") {
|
|
6102
|
-
tools.push(visible[index]);
|
|
6103
|
-
index += 1;
|
|
6104
|
-
}
|
|
6105
|
-
elements.push(renderToolSequence(tools, options.expanded));
|
|
6106
|
-
}
|
|
6107
|
-
return {
|
|
6108
|
-
elements,
|
|
6109
|
-
inspectable: visible.some((step) => step.kind === "skill" || step.kind === "tool")
|
|
6110
|
-
};
|
|
6111
|
-
}
|
|
6112
|
-
function renderFeishuVisibleAnswer(presentation, currentText) {
|
|
6113
|
-
const committed = presentation?.steps.filter((step) => step.kind === "assistant").map((step) => step.text.trim()).filter(Boolean) ?? [];
|
|
6114
|
-
const current = currentText.trim();
|
|
6115
|
-
return [...committed, ...current ? [current] : []].join("\n\n");
|
|
6116
|
-
}
|
|
6117
|
-
function renderProgressSteps(steps) {
|
|
6118
|
-
const rendered = [];
|
|
6119
|
-
for (let index = 0; index < steps.length;) {
|
|
6120
|
-
const step = steps[index];
|
|
6121
|
-
if (step.kind !== "tool") {
|
|
6122
|
-
rendered.push(renderStep(step));
|
|
6123
|
-
index += 1;
|
|
6124
|
-
continue;
|
|
6125
|
-
}
|
|
6126
|
-
const tools = [];
|
|
6127
|
-
while (index < steps.length && steps[index]?.kind === "tool") {
|
|
6128
|
-
tools.push(steps[index]);
|
|
6129
|
-
index += 1;
|
|
6130
|
-
}
|
|
6131
|
-
rendered.push(tools.map(renderToolSummary).join("\n"));
|
|
6132
|
-
}
|
|
6133
|
-
return rendered;
|
|
6134
|
-
}
|
|
6135
|
-
function renderToolSequence(tools, expanded) {
|
|
6136
|
-
if (tools.length === 1) return {
|
|
6137
|
-
content: renderToolSummary(tools[0]),
|
|
6138
|
-
tag: "markdown"
|
|
6139
|
-
};
|
|
6140
|
-
return collapsiblePanel({
|
|
6141
|
-
content: tools.map(renderToolSummary).join("\n"),
|
|
6142
|
-
expanded: expanded || tools.some((tool) => tool.status === "running"),
|
|
6143
|
-
title: toolSequenceTitle(tools)
|
|
6144
|
-
});
|
|
6145
|
-
}
|
|
6146
|
-
function toolSequenceTitle(tools) {
|
|
6147
|
-
if ([...tools].reverse().find((tool) => tool.status === "running")) return `执行过程 · ${tools.length} 个工具 · 进行中`;
|
|
6148
|
-
if (tools.some((tool) => tool.status === "failed")) return `执行过程 · ${tools.length} 个工具 · 有失败`;
|
|
6149
|
-
const totalDuration = tools.reduce((total, tool) => total + (tool.durationMs ?? 0), 0);
|
|
6150
|
-
return totalDuration > 0 ? `执行过程 · ${tools.length} 个工具 · 已完成 · ${formatDuration$1(totalDuration)}` : `执行过程 · ${tools.length} 个工具 · 已完成`;
|
|
6151
|
-
}
|
|
6152
|
-
function renderToolSummary(step) {
|
|
6153
|
-
const skill = step.skillId ? ` · ${escapeInline(step.skillId)}` : "";
|
|
6154
|
-
const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
|
|
6155
|
-
return `- ${statusIcon(step.status)} **${escapeInline(toolAction(step.toolName))}**${skill}${duration}`;
|
|
6156
|
-
}
|
|
6157
|
-
function renderTimelineStep(step, expandCompleted) {
|
|
6158
|
-
switch (step.kind) {
|
|
6159
|
-
case "assistant": return [{
|
|
6160
|
-
content: step.text,
|
|
6161
|
-
margin: "0px",
|
|
6162
|
-
tag: "markdown",
|
|
6163
|
-
text_align: "left",
|
|
6164
|
-
text_size: "normal"
|
|
6165
|
-
}];
|
|
6166
|
-
case "skill": return [collapsiblePanel({
|
|
6167
|
-
content: skillDetails(step),
|
|
6168
|
-
expanded: step.status === "running" || expandCompleted,
|
|
6169
|
-
title: `${step.skillId} · ${step.status === "running" ? "读取中" : step.status === "failed" ? "读取失败" : "已读取"}`
|
|
6170
|
-
})];
|
|
6171
|
-
case "tool": return [{
|
|
6172
|
-
content: renderToolSummary(step),
|
|
6173
|
-
tag: "markdown"
|
|
6174
|
-
}];
|
|
6175
|
-
case "model":
|
|
6176
|
-
case "response": return [];
|
|
6177
|
-
}
|
|
6178
|
-
}
|
|
6179
|
-
function collapsiblePanel(options) {
|
|
6180
|
-
return {
|
|
6181
|
-
background_color: options.expanded ? "blue-50" : "grey-50",
|
|
6182
|
-
elements: [{
|
|
6183
|
-
content: options.content,
|
|
6184
|
-
tag: "markdown",
|
|
6185
|
-
text_align: "left",
|
|
6186
|
-
text_size: "normal"
|
|
6187
|
-
}],
|
|
6188
|
-
expanded: options.expanded,
|
|
6189
|
-
header: {
|
|
6190
|
-
expanded_title: {
|
|
6191
|
-
content: options.title,
|
|
6192
|
-
tag: "plain_text",
|
|
6193
|
-
text_align: "left"
|
|
6194
|
-
},
|
|
6195
|
-
icon: {
|
|
6196
|
-
color: "grey",
|
|
6197
|
-
tag: "standard_icon",
|
|
6198
|
-
token: "down-small-ccm_outlined"
|
|
6199
|
-
},
|
|
6200
|
-
icon_expanded_angle: -180,
|
|
6201
|
-
icon_position: "right",
|
|
6202
|
-
padding: "8px 12px 8px 12px",
|
|
6203
|
-
title: {
|
|
6204
|
-
content: options.title,
|
|
6205
|
-
tag: "plain_text",
|
|
6206
|
-
text_align: "left"
|
|
6207
|
-
},
|
|
6208
|
-
width: "fill"
|
|
6209
|
-
},
|
|
6210
|
-
margin: "8px 0px 0px 0px",
|
|
6211
|
-
padding: "12px 12px 12px 12px",
|
|
6212
|
-
tag: "collapsible_panel"
|
|
6213
|
-
};
|
|
6214
|
-
}
|
|
6215
|
-
function skillDetails(step) {
|
|
6216
|
-
const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
|
|
6217
|
-
return [`**${statusLabel(step.status)}**${duration}`, `这一步:读取并应用技能 \`${escapeInline(step.skillId)}\` 的任务说明。`].join("\n\n");
|
|
6218
|
-
}
|
|
6219
|
-
function statusLabel(status) {
|
|
6220
|
-
switch (status) {
|
|
6221
|
-
case "completed": return "已完成";
|
|
6222
|
-
case "failed": return "未完成";
|
|
6223
|
-
case "running": return "进行中";
|
|
6224
|
-
}
|
|
6225
|
-
}
|
|
6226
|
-
function toolAction(toolName) {
|
|
6227
|
-
const normalized = toolName.toLowerCase();
|
|
6228
|
-
const executable = normalized.split(/[\\/]/u).at(-1) ?? normalized;
|
|
6229
|
-
if (executable === "bash" || executable === "exec" || executable === "exec_command") return "执行命令";
|
|
6230
|
-
if (normalized.includes("read") && normalized.includes("file")) return "读取文件";
|
|
6231
|
-
if (normalized.includes("write") && normalized.includes("file")) return "写入文件";
|
|
6232
|
-
if (normalized.includes("search") || normalized === "rg") return "搜索内容";
|
|
6233
|
-
if (normalized.includes("fetch") || normalized.includes("http")) return "访问服务";
|
|
6234
|
-
return `调用 ${toolName.replace(/^.*[\\/]/u, "")}`;
|
|
6235
|
-
}
|
|
6236
|
-
function renderStep(step) {
|
|
6237
|
-
const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
|
|
6238
|
-
switch (step.kind) {
|
|
6239
|
-
case "assistant": return step.text;
|
|
6240
|
-
case "model": return `- ${statusIcon(step.status)} ${step.label}${duration}`;
|
|
6241
|
-
case "skill": return `- ${statusIcon(step.status)} ${step.label} \`${escapeInline(step.skillId)}\`${duration}`;
|
|
6242
|
-
case "response": return `- ${statusIcon(step.status)} ${step.label}${duration}`;
|
|
6243
|
-
case "tool": return renderToolSummary(step);
|
|
6244
|
-
}
|
|
6245
|
-
}
|
|
6246
|
-
function isVisibleStep(step) {
|
|
6247
|
-
return step.kind === "assistant" || step.kind === "skill" || step.kind === "tool";
|
|
6248
|
-
}
|
|
6249
|
-
function runningLabel(step) {
|
|
6250
|
-
switch (step.kind) {
|
|
6251
|
-
case "assistant": return "正在说明进展";
|
|
6252
|
-
case "model": return "正在分析";
|
|
6253
|
-
case "skill": return "正在读取技能";
|
|
6254
|
-
case "tool": return `正在调用 ${step.toolName}`;
|
|
6255
|
-
case "response": return "正在整理回答";
|
|
6256
|
-
}
|
|
6257
|
-
}
|
|
6258
|
-
function statusIcon(status) {
|
|
6259
|
-
switch (status) {
|
|
6260
|
-
case "completed": return "✅";
|
|
6261
|
-
case "failed": return "⚠️";
|
|
6262
|
-
case "running": return "⏳";
|
|
6263
|
-
}
|
|
6264
|
-
}
|
|
6265
|
-
function formatDuration$1(milliseconds) {
|
|
6266
|
-
if (milliseconds < 1e3) return `${milliseconds} 毫秒`;
|
|
6267
|
-
if (milliseconds < 6e4) {
|
|
6268
|
-
const seconds = milliseconds / 1e3;
|
|
6269
|
-
return `${Number(seconds.toFixed(seconds < 10 ? 1 : 0))} 秒`;
|
|
6270
|
-
}
|
|
6271
|
-
return `${Math.floor(milliseconds / 6e4)} 分 ${Math.floor(milliseconds % 6e4 / 1e3)} 秒`;
|
|
6272
|
-
}
|
|
6273
|
-
function escapeInline(value) {
|
|
6274
|
-
return value.replace(/`/g, "ˋ");
|
|
6275
|
-
}
|
|
6276
|
-
//#endregion
|
|
6277
6374
|
//#region src/adapters/feishu/run-presentation/feishu-agent-run-card.ts
|
|
6278
6375
|
const FEISHU_AGENT_CARD_ELEMENT_ID = "rivus_agent_answer";
|
|
6279
6376
|
const FEISHU_AGENT_CARD_PROGRESS_ELEMENT_ID = "rivus_agent_progress";
|
|
@@ -6704,6 +6801,7 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
6704
6801
|
}),
|
|
6705
6802
|
leaseMs,
|
|
6706
6803
|
...options.observe ? { observe: options.observe } : {},
|
|
6804
|
+
progressDisplay,
|
|
6707
6805
|
publisher,
|
|
6708
6806
|
store: options.cardTargets
|
|
6709
6807
|
});
|
|
@@ -6718,7 +6816,7 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
|
|
|
6718
6816
|
const streaming = createPeriodicEffectLoop({
|
|
6719
6817
|
intervalMs: options.flushIntervalMs ?? options.config.feishu.streamMinIntervalMs,
|
|
6720
6818
|
...options.onError ? { onError: options.onError } : {},
|
|
6721
|
-
run: () =>
|
|
6819
|
+
run: () => cardRollover.flush(),
|
|
6722
6820
|
sleep: options.sleep
|
|
6723
6821
|
});
|
|
6724
6822
|
return {
|
|
@@ -7654,22 +7752,114 @@ function resolveContentMode(value) {
|
|
|
7654
7752
|
throw new LangfuseTelemetryConfigError("RIVUS_TELEMETRY_CONTENT must be either \"metadata-only\" or \"redacted\"");
|
|
7655
7753
|
}
|
|
7656
7754
|
//#endregion
|
|
7755
|
+
//#region src/adapters/pi/execution/pi-session-prompts.ts
|
|
7756
|
+
/** One ordinary prompt writer, with ordered native steering while that prompt is streaming. */
|
|
7757
|
+
async function runPiSessionPrompts(options) {
|
|
7758
|
+
const { input, handle } = options;
|
|
7759
|
+
const { session } = handle;
|
|
7760
|
+
const prepare = (text) => handle.preparePrompt?.({
|
|
7761
|
+
...input,
|
|
7762
|
+
text
|
|
7763
|
+
}) ?? text;
|
|
7764
|
+
if (!options.supportsSteering) {
|
|
7765
|
+
const text = await prepare(input.text);
|
|
7766
|
+
if (!input.abortSignal.aborted) await session.prompt(text);
|
|
7767
|
+
return;
|
|
7768
|
+
}
|
|
7769
|
+
if (!session.steer || typeof session.isStreaming !== "boolean" || !session.clearQueue) throw new Error("Pi steering requires native steer, isStreaming and clearQueue capabilities");
|
|
7770
|
+
const mailbox = input.steering;
|
|
7771
|
+
const stop = new AbortController();
|
|
7772
|
+
const onAbort = () => stop.abort();
|
|
7773
|
+
input.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
7774
|
+
if (input.abortSignal.aborted) stop.abort();
|
|
7775
|
+
const close = () => Effect.runPromise(mailbox?.close?.() ?? Effect.succeed([]));
|
|
7776
|
+
const take = () => Effect.runPromise(mailbox?.next() ?? Effect.never, { signal: stop.signal }).then((text) => ({
|
|
7777
|
+
type: "steering",
|
|
7778
|
+
text
|
|
7779
|
+
}), (error) => {
|
|
7780
|
+
if (stop.signal.aborted) return { type: "closed" };
|
|
7781
|
+
throw error;
|
|
7782
|
+
});
|
|
7783
|
+
let prompt = Promise.resolve({ type: "completed" });
|
|
7784
|
+
const startPrompt = (text) => {
|
|
7785
|
+
prompt = session.prompt(text).then(() => ({ type: "completed" }), (error) => ({
|
|
7786
|
+
type: "failed",
|
|
7787
|
+
error
|
|
7788
|
+
}));
|
|
7789
|
+
};
|
|
7790
|
+
const settlePrompt = async () => {
|
|
7791
|
+
const outcome = await prompt;
|
|
7792
|
+
if (outcome.type === "failed") throw outcome.error;
|
|
7793
|
+
};
|
|
7794
|
+
let next;
|
|
7795
|
+
let succeeded = false;
|
|
7796
|
+
try {
|
|
7797
|
+
const text = await prepare(input.text);
|
|
7798
|
+
if (input.abortSignal.aborted) return;
|
|
7799
|
+
startPrompt(text);
|
|
7800
|
+
next = take();
|
|
7801
|
+
while (!input.abortSignal.aborted) {
|
|
7802
|
+
const outcome = await Promise.race([next, prompt]);
|
|
7803
|
+
if (outcome.type === "failed") throw outcome.error;
|
|
7804
|
+
if (outcome.type === "closed") break;
|
|
7805
|
+
if (outcome.type === "completed") {
|
|
7806
|
+
const remaining = await close();
|
|
7807
|
+
stop.abort();
|
|
7808
|
+
const claimed = await next;
|
|
7809
|
+
const accepted = claimed.type === "steering" ? [claimed.text, ...remaining] : remaining;
|
|
7810
|
+
for (const acceptedText of accepted) {
|
|
7811
|
+
const prepared = await prepare(acceptedText);
|
|
7812
|
+
if (input.abortSignal.aborted) return;
|
|
7813
|
+
startPrompt(prepared);
|
|
7814
|
+
await settlePrompt();
|
|
7815
|
+
}
|
|
7816
|
+
succeeded = true;
|
|
7817
|
+
return;
|
|
7818
|
+
}
|
|
7819
|
+
const prepared = await prepare(outcome.text);
|
|
7820
|
+
if (input.abortSignal.aborted) break;
|
|
7821
|
+
if (session.isStreaming) await session.steer(prepared);
|
|
7822
|
+
else {
|
|
7823
|
+
await settlePrompt();
|
|
7824
|
+
if (input.abortSignal.aborted) break;
|
|
7825
|
+
startPrompt(prepared);
|
|
7826
|
+
}
|
|
7827
|
+
next = take();
|
|
7828
|
+
}
|
|
7829
|
+
} finally {
|
|
7830
|
+
input.abortSignal.removeEventListener("abort", onAbort);
|
|
7831
|
+
await close();
|
|
7832
|
+
stop.abort();
|
|
7833
|
+
await next;
|
|
7834
|
+
const cancelledOrFailed = !succeeded || input.abortSignal.aborted;
|
|
7835
|
+
if (cancelledOrFailed) await options.abort();
|
|
7836
|
+
await prompt;
|
|
7837
|
+
if (cancelledOrFailed) session.clearQueue();
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7840
|
+
//#endregion
|
|
7657
7841
|
//#region src/adapters/pi/execution/pi-agent-loop.ts
|
|
7658
7842
|
function createPiAgentLoop$1(options) {
|
|
7659
|
-
return {
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7843
|
+
return {
|
|
7844
|
+
...options.supportsSteering ? { supportsSteering: true } : {},
|
|
7845
|
+
run: (input) => {
|
|
7846
|
+
const stream = Stream.fromAsyncIterable(runPiSession(input, options), (error) => error);
|
|
7847
|
+
if (!options.runBoundary) return stream;
|
|
7848
|
+
return Stream.unwrapScoped(Effect.acquireRelease(options.runBoundary.acquireRun({ signal: input.abortSignal }), (lease) => Effect.sync(lease.release)).pipe(Effect.as(stream)));
|
|
7849
|
+
}
|
|
7850
|
+
};
|
|
7664
7851
|
}
|
|
7665
7852
|
function createPiSdkAgentLoop$1(options) {
|
|
7666
|
-
return createPiAgentLoop$1({
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7853
|
+
return createPiAgentLoop$1({
|
|
7854
|
+
...options.supportsSteering ? { supportsSteering: true } : {},
|
|
7855
|
+
resolveSession: async () => {
|
|
7856
|
+
const result = await options.createAgentSession(options.sessionOptions);
|
|
7857
|
+
return {
|
|
7858
|
+
dispose: () => result.session.dispose?.(),
|
|
7859
|
+
session: result.session
|
|
7860
|
+
};
|
|
7861
|
+
}
|
|
7862
|
+
});
|
|
7673
7863
|
}
|
|
7674
7864
|
async function* runPiSession(input, options) {
|
|
7675
7865
|
const queue = createAsyncQueue();
|
|
@@ -7726,7 +7916,12 @@ async function* runPiSession(input, options) {
|
|
|
7726
7916
|
queue.offer(mapped);
|
|
7727
7917
|
}
|
|
7728
7918
|
});
|
|
7729
|
-
await
|
|
7919
|
+
await runPiSessionPrompts({
|
|
7920
|
+
input,
|
|
7921
|
+
handle,
|
|
7922
|
+
supportsSteering: options.supportsSteering === true,
|
|
7923
|
+
abort: abortSession
|
|
7924
|
+
});
|
|
7730
7925
|
complete(input.abortSignal.aborted || terminalErrorMessage === void 0 ? void 0 : new Error(terminalErrorMessage));
|
|
7731
7926
|
} catch (error) {
|
|
7732
7927
|
complete(input.abortSignal.aborted ? void 0 : error);
|
|
@@ -7986,6 +8181,7 @@ function createAsyncQueue() {
|
|
|
7986
8181
|
//#region src/adapters/compatibility/agent-execution/pi/pi-agent-loop.ts
|
|
7987
8182
|
function createPiAgentLoop(options) {
|
|
7988
8183
|
return fromEffectAgentLoop(createPiAgentLoop$1({
|
|
8184
|
+
...options.supportsSteering ? { supportsSteering: true } : {},
|
|
7989
8185
|
...options.disposeSessionAfterRun === void 0 ? {} : { disposeSessionAfterRun: options.disposeSessionAfterRun },
|
|
7990
8186
|
...options.modelContentObserver ? { modelContentObserver: options.modelContentObserver } : {},
|
|
7991
8187
|
...options.runBoundary ? { runBoundary: options.runBoundary } : {},
|
package/dist/pi.d.ts
CHANGED
|
@@ -66,6 +66,23 @@ interface PiNamedTool {
|
|
|
66
66
|
}
|
|
67
67
|
declare function resolvePiSessionToolNames(runtimeToolIds: ReadonlyArray<RivusRuntimeToolId>, customTools: ReadonlyArray<PiNamedTool>): ReadonlyArray<string>;
|
|
68
68
|
//#endregion
|
|
69
|
+
//#region src/adapters/pi/tool-execution/pi-bash-tool-options.d.ts
|
|
70
|
+
interface PiBashSpawnContext {
|
|
71
|
+
command: string;
|
|
72
|
+
cwd: string;
|
|
73
|
+
env: Record<string, string | undefined>;
|
|
74
|
+
}
|
|
75
|
+
/** Rivus's Bash configuration boundary, independent of provider UI/model types. */
|
|
76
|
+
interface PiBashToolOptions {
|
|
77
|
+
commandPrefix?: string;
|
|
78
|
+
shellPath?: string;
|
|
79
|
+
spawnHook?: (context: PiBashSpawnContext) => PiBashSpawnContext;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/adapters/pi/tool-execution/pi-bash-tool.d.ts
|
|
83
|
+
/** Keep Pi's native execution, rendering and abort cleanup with a bounded default. */
|
|
84
|
+
declare function createPiBashTool(cwd: string, options?: PiBashToolOptions): PiToolDefinition;
|
|
85
|
+
//#endregion
|
|
69
86
|
//#region src/adapters/pi/runtime/pi-session-resources.d.ts
|
|
70
87
|
interface CreatePiSessionResourcesOptions {
|
|
71
88
|
readonly agentDir: string;
|
|
@@ -77,6 +94,10 @@ interface CreatePiSessionResourcesOptions {
|
|
|
77
94
|
readonly systemPromptOverride?: (base: string | undefined) => string | undefined;
|
|
78
95
|
}
|
|
79
96
|
interface PiSessionResources {
|
|
97
|
+
readonly bashToolOptions?: {
|
|
98
|
+
readonly commandPrefix?: string;
|
|
99
|
+
readonly shellPath?: string;
|
|
100
|
+
};
|
|
80
101
|
readonly skillNames: ReadonlySet<string>;
|
|
81
102
|
readonly skillPaths: ReadonlyArray<string>;
|
|
82
103
|
readonly refresh: () => Promise<void>;
|
|
@@ -105,4 +126,4 @@ interface PiToolProxyOptions {
|
|
|
105
126
|
declare function createPiToolProxyDefinitions(options: PiToolProxyOptions): PiToolDefinition[];
|
|
106
127
|
declare function createPiToolNameResolver(tools: ReadonlyArray<Pick<RivusResolvedToolDescriptor, "id">>): (toolName: string) => string;
|
|
107
128
|
//#endregion
|
|
108
|
-
export { type CreatePiSessionResourcesOptions, InvalidPiSkillCatalog, InvalidPiSkillSource, type PiSessionResources, type PiSkillRuntime, type PiToolApprovalGateway, type PiToolApprovalRequest, type PiToolContent, type PiToolDefinition, type PiToolProxyOptions, type PiToolResult, ProjectSkillReadDenied, type ResolvePiSkillSourcesOptions, createPiProjectSkillReadTool, createPiSessionResources, createPiSkillReadTool, createPiSkillReadTools, createPiSkillRuntime, createPiToolNameResolver, createPiToolProxyDefinitions, resolvePiSessionToolNames, resolvePiSkillSources, validatePiSkillCatalog, validatePiSkillCommand };
|
|
129
|
+
export { type CreatePiSessionResourcesOptions, InvalidPiSkillCatalog, InvalidPiSkillSource, type PiBashToolOptions, type PiSessionResources, type PiSkillRuntime, type PiToolApprovalGateway, type PiToolApprovalRequest, type PiToolContent, type PiToolDefinition, type PiToolProxyOptions, type PiToolResult, ProjectSkillReadDenied, type ResolvePiSkillSourcesOptions, createPiBashTool, createPiProjectSkillReadTool, createPiSessionResources, createPiSkillReadTool, createPiSkillReadTools, createPiSkillRuntime, createPiToolNameResolver, createPiToolProxyDefinitions, resolvePiSessionToolNames, resolvePiSkillSources, validatePiSkillCatalog, validatePiSkillCommand };
|
package/dist/pi.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
+
import { a as InvalidPiSkillSource, c as validatePiSkillCatalog, d as createPiProjectSkillReadTool, f as createPiSkillReadTool, i as resolvePiSessionToolNames, l as validatePiSkillCommand, m as createPiBashTool, n as createPiToolProxyDefinitions, o as resolvePiSkillSources, p as createPiSkillReadTools, r as createPiSessionResources, s as InvalidPiSkillCatalog, t as createPiToolNameResolver, u as ProjectSkillReadDenied } from "./chunks/pi.js";
|
|
1
2
|
import { r as createPiSkillRuntime } from "./chunks/rivus-tool.js";
|
|
2
|
-
|
|
3
|
-
export { InvalidPiSkillCatalog, InvalidPiSkillSource, ProjectSkillReadDenied, createPiProjectSkillReadTool, createPiSessionResources, createPiSkillReadTool, createPiSkillReadTools, createPiSkillRuntime, createPiToolNameResolver, createPiToolProxyDefinitions, resolvePiSessionToolNames, resolvePiSkillSources, validatePiSkillCatalog, validatePiSkillCommand };
|
|
3
|
+
export { InvalidPiSkillCatalog, InvalidPiSkillSource, ProjectSkillReadDenied, createPiBashTool, createPiProjectSkillReadTool, createPiSessionResources, createPiSkillReadTool, createPiSkillReadTools, createPiSkillRuntime, createPiToolNameResolver, createPiToolProxyDefinitions, resolvePiSessionToolNames, resolvePiSkillSources, validatePiSkillCatalog, validatePiSkillCommand };
|
|
@@ -81,6 +81,7 @@ import {
|
|
|
81
81
|
type RivusThinkingLevel
|
|
82
82
|
} from "@rivus/agent";
|
|
83
83
|
import {
|
|
84
|
+
createPiBashTool,
|
|
84
85
|
createPiSkillReadTools,
|
|
85
86
|
createPiSessionResources,
|
|
86
87
|
createPiSkillRuntime,
|
|
@@ -532,9 +533,19 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
532
533
|
const sessionRegistry = createPiSessionRegistry({
|
|
533
534
|
createSession: async (firstInput) => {
|
|
534
535
|
let activeInput = firstInput;
|
|
536
|
+
const resources = await createPiSessionResources({
|
|
537
|
+
agentDir: PI_AGENT_DIR,
|
|
538
|
+
appendSystemPromptOverride: () =>
|
|
539
|
+
[workspaceInstructions.content, skillRuntime.prompt].filter((content) => content.length > 0),
|
|
540
|
+
cwd: workingDirectory,
|
|
541
|
+
homeDirectory: homedir(),
|
|
542
|
+
...(input.projectSpace ? { projectSkillPaths: input.projectSpace.skillPaths } : {}),
|
|
543
|
+
systemPromptOverride: () => input.definition.systemPrompt
|
|
544
|
+
});
|
|
535
545
|
const managedContext =
|
|
536
546
|
management?.enabled && runContexts && input.definition.runtimeToolGrantSet.toolIds.includes("bash")
|
|
537
547
|
? createPiManagedSessionContext({
|
|
548
|
+
...(resources.bashToolOptions ? { bashToolOptions: resources.bashToolOptions } : {}),
|
|
538
549
|
binDirectory: management.binDirectory,
|
|
539
550
|
contexts: runContexts.registry,
|
|
540
551
|
cwd: workingDirectory,
|
|
@@ -542,17 +553,11 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
542
553
|
socketPath: management.socketPath
|
|
543
554
|
})
|
|
544
555
|
: undefined;
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
[workspaceInstructions.content, skillRuntime.prompt].filter((content) => content.length > 0),
|
|
549
|
-
cwd: workingDirectory,
|
|
550
|
-
homeDirectory: homedir(),
|
|
551
|
-
...(input.projectSpace ? { projectSkillPaths: input.projectSpace.skillPaths } : {}),
|
|
552
|
-
systemPromptOverride: () => input.definition.systemPrompt
|
|
553
|
-
});
|
|
556
|
+
const bashTool = input.definition.runtimeToolGrantSet.toolIds.includes("bash")
|
|
557
|
+
? (managedContext?.tool ?? createPiBashTool(workingDirectory, resources.bashToolOptions))
|
|
558
|
+
: undefined;
|
|
554
559
|
const customTools = [
|
|
555
|
-
...(
|
|
560
|
+
...(bashTool ? [bashTool] : []),
|
|
556
561
|
...createPiSkillReadTools({
|
|
557
562
|
cwd: workingDirectory,
|
|
558
563
|
runtimeToolIds: input.definition.runtimeToolGrantSet.toolIds,
|
|
@@ -570,7 +575,7 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
570
575
|
}),
|
|
571
576
|
...(skillRuntime.tool ? [skillRuntime.tool] : [])
|
|
572
577
|
];
|
|
573
|
-
const nativeToolIds =
|
|
578
|
+
const nativeToolIds = bashTool
|
|
574
579
|
? input.definition.runtimeToolGrantSet.toolIds.filter((toolId) => toolId !== "bash")
|
|
575
580
|
: input.definition.runtimeToolGrantSet.toolIds;
|
|
576
581
|
const activeToolNames = resolvePiSessionToolNames(nativeToolIds, customTools);
|
|
@@ -605,6 +610,7 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
605
610
|
? management.runtime.registerSessionRegistry(sessionRegistry)
|
|
606
611
|
: undefined;
|
|
607
612
|
const loop = createPiAgentLoop({
|
|
613
|
+
supportsSteering: true,
|
|
608
614
|
disposeSessionAfterRun: false,
|
|
609
615
|
...(management?.enabled ? { runBoundary: management.boundary } : {}),
|
|
610
616
|
...(telemetry ? { modelContentObserver: telemetry.modelContentObserver } : {}),
|