@rivus/runtime 0.17.0 → 0.18.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.
@@ -1,5 +1,5 @@
1
1
  import { a as isValidUserInput } from "./agent-user-input.js";
2
- import { Effect, Either } from "effect";
2
+ import { Effect, Result } from "effect";
3
3
  //#region src/core/application/agent-execution/loop/agent-loop.ts
4
4
  function createAgentLoopTextDelta(delta) {
5
5
  return {
@@ -161,19 +161,19 @@ function renderImage(part, context) {
161
161
  size: part.size,
162
162
  index: images.length + 1
163
163
  })) return Effect.succeed(renderUnavailableNoticeAndRecord(part.attachmentId, "超出本次图片输入预算或当前模型不支持视觉", unavailable));
164
- return access.readBytes(part.attachmentId).pipe(Effect.either, Effect.map((result) => {
165
- if (Either.isLeft(result)) return renderUnavailableNoticeAndRecord(part.attachmentId, "图片在执行环境中不可读", unavailable);
166
- if (!withinImageBudget(result.right.byteLength) || result.right.byteLength === 0) return renderUnavailableNoticeAndRecord(part.attachmentId, "超出本次图片输入预算或图片为空", unavailable);
164
+ return access.readBytes(part.attachmentId).pipe(Effect.result, Effect.map((result) => {
165
+ if (Result.isFailure(result)) return renderUnavailableNoticeAndRecord(part.attachmentId, "图片在执行环境中不可读", unavailable);
166
+ if (!withinImageBudget(result.success.byteLength) || result.success.byteLength === 0) return renderUnavailableNoticeAndRecord(part.attachmentId, "超出本次图片输入预算或图片为空", unavailable);
167
167
  if (access.canProvideImage && !access.canProvideImage({
168
168
  mimeType: part.mimeType,
169
- size: result.right.byteLength,
169
+ size: result.success.byteLength,
170
170
  index: images.length + 1
171
171
  })) return renderUnavailableNoticeAndRecord(part.attachmentId, "超出本次图片输入预算或当前模型不支持视觉", unavailable);
172
172
  const imageIndex = images.length + 1;
173
173
  images.push({
174
174
  type: "image",
175
175
  attachmentId: part.attachmentId,
176
- data: Buffer.from(result.right).toString("base64"),
176
+ data: Buffer.from(result.success).toString("base64"),
177
177
  imageIndex,
178
178
  mimeType: part.mimeType
179
179
  });
@@ -183,9 +183,9 @@ function renderImage(part, context) {
183
183
  }
184
184
  function renderAttachment(part, access, markUsable, unavailable) {
185
185
  if (!access) return Effect.succeed(renderUnavailableNoticeAndRecord(part.fileName ?? part.attachmentId, "执行环境未提供文件读取能力", unavailable));
186
- return access.materialize(part.attachmentId).pipe(Effect.either, Effect.map((result) => {
187
- if (Either.isLeft(result)) return renderUnavailableNoticeAndRecord(part.fileName ?? part.attachmentId, "附件无法物化到执行环境", unavailable);
188
- const value = result.right;
186
+ return access.materialize(part.attachmentId).pipe(Effect.result, Effect.map((result) => {
187
+ if (Result.isFailure(result)) return renderUnavailableNoticeAndRecord(part.fileName ?? part.attachmentId, "附件无法物化到执行环境", unavailable);
188
+ const value = result.success;
189
189
  const canUnderstand = access.canUnderstand?.({
190
190
  mediaKind: part.mediaKind,
191
191
  mimeType: value.mimeType
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as createInvocationAuthority, i as InvalidInvocationAuthority, n as InvalidToolInput, o as resolveInvocationAuthority, r as createToolInputDigest, s as normalizeAgentInvocation, t as requiresToolApproval } from "./chunks/tool-risk-policy.js";
2
2
  import { i as isAgentSteeringRequest, n as cloneAgentSteeringInput, o as projectUserInputText, r as cloneUserInput, t as ATTACHMENT_RECOVERY_COMMAND } from "./chunks/agent-user-input.js";
3
- import { Deferred, Effect, Option, Queue, Stream } from "effect";
3
+ import { Deferred, Effect, Option, Queue, Semaphore, Stream } from "effect";
4
4
  import { unwrapExit } from "@rivus/platform/effect";
5
5
  import { createHash } from "node:crypto";
6
6
  import { appendFile, chmod, lstat, mkdir, readFile, realpath, stat } from "node:fs/promises";
@@ -2023,10 +2023,10 @@ function createAgentRunStreams(runPrompt) {
2023
2023
  };
2024
2024
  }
2025
2025
  function createProjectedRunStream(runPrompt, command, project) {
2026
- return Stream.asyncEffect((emit) => runPrompt(command, (event, state, previousState) => Option.match(project(event, state, previousState), {
2026
+ return Stream.callback((queue) => runPrompt(command, (event, state, previousState) => Option.match(project(event, state, previousState), {
2027
2027
  onNone: () => Effect.void,
2028
- onSome: (value) => Effect.promise(() => emit.single(value))
2029
- }), "stream interrupted").pipe(Effect.flatMap(() => Effect.promise(() => emit.end())), Effect.catchAll((error) => Effect.promise(() => emit.fail(error)))));
2028
+ onSome: (value) => Queue.offer(queue, value)
2029
+ }), "stream interrupted").pipe(Effect.flatMap(() => Queue.end(queue)), Effect.catchCause((cause) => Queue.failCause(queue, cause)), Effect.forkScoped, Effect.asVoid), { bufferSize: 16 });
2030
2030
  }
2031
2031
  //#endregion
2032
2032
  //#region src/core/application/agent-execution/execution/agent-loop-event-mapper.ts
@@ -2146,13 +2146,13 @@ function createSteeringChannel() {
2146
2146
  nextInput: () => Queue.take(queue),
2147
2147
  close: () => Effect.gen(function* () {
2148
2148
  closed = true;
2149
- return Array.from(yield* Queue.takeAll(queue)).map(toText);
2149
+ return Array.from(yield* Queue.clear(queue)).map(toText);
2150
2150
  }),
2151
2151
  closeInput: () => Effect.gen(function* () {
2152
2152
  closed = true;
2153
- return Array.from(yield* Queue.takeAll(queue));
2153
+ return Array.from(yield* Queue.clear(queue));
2154
2154
  }),
2155
- push: (input) => Effect.sync(() => !closed && Queue.unsafeOffer(queue, input))
2155
+ push: (input) => Effect.sync(() => !closed && Queue.offerUnsafe(queue, input))
2156
2156
  };
2157
2157
  }));
2158
2158
  }
@@ -2239,7 +2239,7 @@ function createAgentHarness(options) {
2239
2239
  for (const sink of options.eventSinks ?? []) yield* Effect.try({
2240
2240
  try: () => sink.append(event),
2241
2241
  catch: (error) => error
2242
- }).pipe(Effect.flatMap((appendEffect) => appendEffect), Effect.catchAll((error) => Effect.fail(new AgentEventSinkFailed(event.runId, event.type, error))));
2242
+ }).pipe(Effect.flatMap((appendEffect) => appendEffect), Effect.catch((error) => Effect.fail(new AgentEventSinkFailed(event.runId, event.type, error))));
2243
2243
  events.push(event);
2244
2244
  const update = queryStore.project(event);
2245
2245
  state = update.state;
@@ -2249,7 +2249,7 @@ function createAgentHarness(options) {
2249
2249
  });
2250
2250
  const record = (event) => Effect.gen(function* () {
2251
2251
  const update = yield* project(event);
2252
- yield* emit(update.event, update.state, update.previousState).pipe(Effect.catchAll((error) => Effect.fail(new AgentEventHandlerFailed(event.runId, event.type, error, state))));
2252
+ yield* emit(update.event, update.state, update.previousState).pipe(Effect.catch((error) => Effect.fail(new AgentEventHandlerFailed(event.runId, event.type, error, state))));
2253
2253
  });
2254
2254
  const recordWithoutEmit = (event) => project(event).pipe(Effect.asVoid);
2255
2255
  const recordInterruptedRun = Effect.gen(function* () {
@@ -2265,7 +2265,7 @@ function createAgentHarness(options) {
2265
2265
  occurredAt: interruptedAt,
2266
2266
  runId,
2267
2267
  type: "agent_run_cancelled"
2268
- }).pipe(Effect.catchAll(() => Effect.void));
2268
+ }).pipe(Effect.catch(() => Effect.void));
2269
2269
  settled = true;
2270
2270
  });
2271
2271
  return yield* Effect.gen(function* () {
@@ -2306,10 +2306,10 @@ function createAgentHarness(options) {
2306
2306
  const loop = Effect.try({
2307
2307
  try: () => options.loop.run(loopInput),
2308
2308
  catch: (error) => error
2309
- }).pipe(Effect.flatMap((loopStream) => Stream.runForEach(loopStream.pipe(Stream.interruptWhenDeferred(cancellation.deferred)), (event) => Effect.gen(function* () {
2309
+ }).pipe(Effect.flatMap((loopStream) => Stream.runForEach(loopStream.pipe(Stream.interruptWhen(Deferred.await(cancellation.deferred))), (event) => Effect.gen(function* () {
2310
2310
  const occurredAt = yield* options.clock.now;
2311
2311
  yield* record(toDomainEvent(runId, command.sessionKey, event, occurredAt));
2312
- }))), Effect.catchAll((error) => {
2312
+ }))), Effect.catch((error) => {
2313
2313
  if (error instanceof AgentEventSinkFailed || error instanceof AgentEventHandlerFailed) return Effect.fail(error);
2314
2314
  return Effect.gen(function* () {
2315
2315
  const occurredAt = yield* options.clock.now;
@@ -2324,16 +2324,16 @@ function createAgentHarness(options) {
2324
2324
  return yield* Effect.fail(new AgentLoopFailed(runId, errorMessage, error, state));
2325
2325
  });
2326
2326
  }));
2327
- yield* options.runTimeoutMs === void 0 ? loop : Effect.raceFirst(loop, Effect.sleep(options.runTimeoutMs).pipe(Effect.flatMap(() => {
2327
+ yield* options.runTimeoutMs === void 0 ? loop : Effect.raceFirst(loop, Effect.sleep(options.runTimeoutMs).pipe(Effect.flatMap(() => Effect.gen(function* () {
2328
2328
  const request = {
2329
2329
  reason: `run timed out after ${options.runTimeoutMs}ms`,
2330
2330
  runId,
2331
2331
  sessionKey: command.sessionKey
2332
2332
  };
2333
2333
  cancellation.requested = request;
2334
+ yield* Deferred.succeed(cancellation.deferred, request);
2334
2335
  cancellation.abortController.abort(request);
2335
- return Deferred.succeed(cancellation.deferred, request);
2336
- }), Effect.asVoid));
2336
+ }).pipe(Effect.uninterruptible)), Effect.asVoid));
2337
2337
  if (cancellation.requested) {
2338
2338
  const cancelledAt = yield* options.clock.now;
2339
2339
  yield* record({
@@ -2400,12 +2400,11 @@ function createAgentHarness(options) {
2400
2400
  runId: activeRun.runId,
2401
2401
  sessionKey: activeRun.sessionKey
2402
2402
  };
2403
- const completed = yield* Deferred.succeed(activeCancellation.deferred, request);
2404
- if (completed) {
2405
- activeCancellation.requested = request;
2406
- activeCancellation.abortController.abort(request);
2407
- }
2408
- return completed;
2403
+ const cancellation = activeCancellation;
2404
+ cancellation.requested = request;
2405
+ const requested = yield* Deferred.succeed(cancellation.deferred, request);
2406
+ cancellation.abortController.abort(request);
2407
+ return requested;
2409
2408
  });
2410
2409
  const requestSteering = (runId, text) => requestSteeringInput(runId, text);
2411
2410
  const requestSteeringInput = (runId, input) => Effect.gen(function* () {
@@ -2580,7 +2579,7 @@ function createRunAdmission(options) {
2580
2579
  const remaining = input.deadlineAt === void 0 ? void 0 : remainingUntil(options.clock, input.deadlineAt);
2581
2580
  if (remaining !== void 0 && remaining <= 0) return Effect.fail(new ModelChangeBoundaryDeadlineError("Run admission deadline has passed"));
2582
2581
  if (open) return Effect.sync(createLease);
2583
- const waiting = Effect.async((resume) => {
2582
+ const waiting = Effect.callback((resume) => {
2584
2583
  let settled = false;
2585
2584
  let waiter;
2586
2585
  let onAbort;
@@ -2608,9 +2607,9 @@ function createRunAdmission(options) {
2608
2607
  if (onAbort && signal) signal.addEventListener("abort", onAbort, { once: true });
2609
2608
  return Effect.sync(cleanup);
2610
2609
  });
2611
- return remaining === void 0 ? waiting : waiting.pipe(Effect.timeoutFail({
2610
+ return remaining === void 0 ? waiting : waiting.pipe(Effect.timeoutOrElse({
2612
2611
  duration: Math.min(remaining, 2147483647),
2613
- onTimeout: () => new ModelChangeBoundaryDeadlineError("Run admission deadline has passed")
2612
+ orElse: () => Effect.fail(new ModelChangeBoundaryDeadlineError("Run admission deadline has passed"))
2614
2613
  }));
2615
2614
  });
2616
2615
  const fence = () => {
@@ -2618,7 +2617,7 @@ function createRunAdmission(options) {
2618
2617
  };
2619
2618
  const whenDrained = () => Effect.suspend(() => {
2620
2619
  if (active === 0) return Effect.void;
2621
- return Effect.async((resume) => {
2620
+ return Effect.callback((resume) => {
2622
2621
  let settled = false;
2623
2622
  let waiter;
2624
2623
  const cleanup = () => {
@@ -2736,7 +2735,7 @@ function createInMemoryAgentMemoryRepository(options = {}) {
2736
2735
  bucket.set(restored.id, restored);
2737
2736
  records.set(namespace, bucket);
2738
2737
  }
2739
- const serial = Effect.unsafeMakeSemaphore(1);
2738
+ const serial = Semaphore.makeUnsafe(1);
2740
2739
  return {
2741
2740
  get: (binding, id) => Effect.sync(() => records.get(createMemoryNamespace(binding))?.get(id)),
2742
2741
  list: (binding) => Effect.sync(() => [...records.get(createMemoryNamespace(binding))?.values() ?? []]),
@@ -3182,7 +3181,7 @@ function createToolOperationLedger(options = {}) {
3182
3181
  if (records.has(operation.operationId)) throw new ToolOperationLedgerError(`duplicate initial tool operation: ${operation.operationId}`);
3183
3182
  records.set(operation.operationId, operation);
3184
3183
  }
3185
- const semaphore = Effect.unsafeMakeSemaphore(1);
3184
+ const semaphore = Semaphore.makeUnsafe(1);
3186
3185
  const exclusive = (effect) => semaphore.withPermits(1)(Effect.uninterruptible(effect));
3187
3186
  const save = (operation) => {
3188
3187
  const snapshot = operation.toSnapshot();
@@ -3338,9 +3337,9 @@ function createToolBroker(options) {
3338
3337
  })).execute(request.input, context);
3339
3338
  if (request.operationId) yield* operations.complete(request.operationId, binding, result);
3340
3339
  return result;
3341
- }).pipe(Effect.catchAll((error) => {
3340
+ }).pipe(Effect.catch((error) => {
3342
3341
  if (!request.operationId) return Effect.fail(error);
3343
- return (prepared.tool.risk === "observe" || error instanceof ToolExecutorInputRejected ? operations.abort(request.operationId, binding) : operations.requireReconciliation(request.operationId, binding, error instanceof Error ? error.message : String(error))).pipe(Effect.zipRight(Effect.fail(error)));
3342
+ return (prepared.tool.risk === "observe" || error instanceof ToolExecutorInputRejected ? operations.abort(request.operationId, binding) : operations.requireReconciliation(request.operationId, binding, error instanceof Error ? error.message : String(error))).pipe(Effect.andThen(Effect.fail(error)));
3344
3343
  }));
3345
3344
  }) };
3346
3345
  }
package/dist/pi.js CHANGED
@@ -312,7 +312,7 @@ function createPiAgentLoop(options) {
312
312
  run: (input) => {
313
313
  const stream = Stream.fromAsyncIterable(runPiSession(input, options), (error) => error);
314
314
  if (!options.runBoundary) return stream;
315
- return Stream.unwrapScoped(Effect.acquireRelease(options.runBoundary.acquireRun({ signal: input.abortSignal }), (lease) => Effect.sync(lease.release)).pipe(Effect.as(stream)));
315
+ return Stream.unwrap(Effect.acquireRelease(options.runBoundary.acquireRun({ signal: input.abortSignal }), (lease) => Effect.sync(lease.release)).pipe(Effect.as(stream)));
316
316
  }
317
317
  };
318
318
  }
@@ -1108,13 +1108,13 @@ function streamPaidProbeCall(options) {
1108
1108
  });
1109
1109
  },
1110
1110
  catch: (error) => error
1111
- }).pipe(Effect.catchAll((error) => {
1111
+ }).pipe(Effect.catch((error) => {
1112
1112
  const deadlineExceeded = callSignal.isDeadlineExceeded() || error instanceof PiModelCallDeadlineExceeded;
1113
1113
  const probeError = new PiModelProbeError(deadlineExceeded ? `${options.description} stream exceeded the model change deadline` : `${options.description} stream failed: ${error instanceof Error ? error.message : String(error)}`, "unknown", { cause: error }, deadlineExceeded ? "model_change_deadline_exceeded" : "model_change_probe_failed");
1114
1114
  return settle({
1115
1115
  outcome: "unknown",
1116
1116
  outputTokens: reservation.maxOutputTokens
1117
- }).pipe(Effect.zipRight(Effect.fail(probeError)));
1117
+ }).pipe(Effect.andThen(Effect.fail(probeError)));
1118
1118
  }));
1119
1119
  const outputTokens = readOutputTokens(message.usage);
1120
1120
  yield* settle({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivus/runtime",
3
- "version": "0.17.0",
3
+ "version": "0.18.1",
4
4
  "description": "Execution core, Pi/ACP adapters, memory, and tool brokerage for Rivus.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-only",
@@ -40,9 +40,9 @@
40
40
  "LICENSE"
41
41
  ],
42
42
  "dependencies": {
43
- "@rivus/platform": "0.17.0",
43
+ "@rivus/platform": "0.18.1",
44
44
  "@earendil-works/pi-coding-agent": "0.84.4",
45
- "effect": "^3.22.2",
45
+ "effect": "^4.0.0",
46
46
  "typebox": "^1.3.34",
47
47
  "@opentelemetry/api": "^1.9.1",
48
48
  "@opentelemetry/exporter-trace-otlp-proto": "^0.222.0",