stitchkit 0.94.0 → 0.94.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/CHANGELOG.md +64 -0
- package/dist/agent-runtime/coding-shell-process.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-listing.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -1
- package/dist/agent-runtime/contained-darwin.d.ts +38 -0
- package/dist/agent-runtime/contained-darwin.d.ts.map +1 -0
- package/dist/agent-runtime/contained-files.d.ts +11 -26
- package/dist/agent-runtime/contained-files.d.ts.map +1 -1
- package/dist/agent-runtime/contained-scan.d.ts +21 -0
- package/dist/agent-runtime/contained-scan.d.ts.map +1 -0
- package/dist/agent-runtime/contained-write.d.ts +9 -0
- package/dist/agent-runtime/contained-write.d.ts.map +1 -0
- package/dist/agent-runtime/deferred-tool-types.d.ts +3 -2
- package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
- package/dist/agent-runtime/harness-control.d.ts +0 -1
- package/dist/agent-runtime/harness-control.d.ts.map +1 -1
- package/dist/agent-runtime/harness.d.ts +0 -5
- package/dist/agent-runtime/harness.d.ts.map +1 -1
- package/dist/agent-runtime/managed-tools.d.ts +3 -2
- package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +1 -1
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schedule-records.d.ts +34 -0
- package/dist/agent-runtime/schedule-records.d.ts.map +1 -0
- package/dist/agent-runtime/schedules.d.ts +34 -0
- package/dist/agent-runtime/schedules.d.ts.map +1 -1
- package/dist/agent-runtime/store-conformance-lifecycle.d.ts +434 -0
- package/dist/agent-runtime/store-conformance-lifecycle.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance-ordering.d.ts +18 -0
- package/dist/agent-runtime/store-conformance-ordering.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance-support.d.ts +130 -0
- package/dist/agent-runtime/store-conformance-support.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance-terminal.d.ts +12 -0
- package/dist/agent-runtime/store-conformance-terminal.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance.d.ts.map +1 -1
- package/dist/application/admission-rate.d.ts +21 -0
- package/dist/application/admission-rate.d.ts.map +1 -0
- package/dist/application/admission-run.d.ts +8 -0
- package/dist/application/admission-run.d.ts.map +1 -0
- package/dist/application/admission.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-lock.d.ts +9 -34
- package/dist/application/diagnostic-journal-lock.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-manager.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-storage.d.ts.map +1 -1
- package/dist/application/kernel-resource-context.d.ts.map +1 -1
- package/dist/application/notification-outbox.d.ts.map +1 -1
- package/dist/application/schedule.d.ts +0 -1
- package/dist/application/schedule.d.ts.map +1 -1
- package/dist/application/watch-hub-core.d.ts +122 -0
- package/dist/application/watch-hub-core.d.ts.map +1 -0
- package/dist/application/watch-hub.d.ts +2 -2
- package/dist/application/watch-hub.d.ts.map +1 -1
- package/dist/browser/client-executors.d.ts +14 -0
- package/dist/browser/client-executors.d.ts.map +1 -0
- package/dist/browser/client-multipart.d.ts +1 -1
- package/dist/browser/client-multipart.d.ts.map +1 -1
- package/dist/browser/client.d.ts +3 -2
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/http-fetch.d.ts +22 -0
- package/dist/browser/http-fetch.d.ts.map +1 -0
- package/dist/browser/http.d.ts +1 -3
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/live-state-core.d.ts +63 -0
- package/dist/browser/live-state-core.d.ts.map +1 -0
- package/dist/browser/live-state.d.ts.map +1 -1
- package/dist/browser/realtime-client.d.ts +26 -0
- package/dist/browser/realtime-client.d.ts.map +1 -0
- package/dist/browser/socket-io-connection.d.ts +82 -0
- package/dist/browser/socket-io-connection.d.ts.map +1 -0
- package/dist/browser/socket-io-peer.d.ts +20 -0
- package/dist/browser/socket-io-peer.d.ts.map +1 -0
- package/dist/browser/socket-io-trace.d.ts +31 -0
- package/dist/browser/socket-io-trace.d.ts.map +1 -0
- package/dist/browser/socket-io.d.ts +2 -18
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/contract/client-types.d.ts +134 -0
- package/dist/contract/client-types.d.ts.map +1 -0
- package/dist/contract/define-validation.d.ts +30 -0
- package/dist/contract/define-validation.d.ts.map +1 -0
- package/dist/contract/define.d.ts +3 -357
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/runtime-context.d.ts +131 -0
- package/dist/contract/runtime-context.d.ts.map +1 -0
- package/dist/contract/tool-options.d.ts +105 -0
- package/dist/contract/tool-options.d.ts.map +1 -0
- package/dist/contract/tool-view.d.ts +15 -2
- package/dist/contract/tool-view.d.ts.map +1 -1
- package/dist/durability/contract.d.ts +83 -0
- package/dist/durability/contract.d.ts.map +1 -1
- package/dist/durability/effect.d.ts +24 -0
- package/dist/durability/effect.d.ts.map +1 -0
- package/dist/durability/engine.d.ts +2 -2
- package/dist/durability/engine.d.ts.map +1 -1
- package/dist/durability/events.d.ts +2 -0
- package/dist/durability/events.d.ts.map +1 -1
- package/dist/durability/ledger.d.ts +11 -0
- package/dist/durability/ledger.d.ts.map +1 -1
- package/dist/durability/parks.d.ts +22 -0
- package/dist/durability/parks.d.ts.map +1 -0
- package/dist/durability/scheduler.d.ts +1 -0
- package/dist/durability/scheduler.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/coding-tools.js +4 -4
- package/dist/entrypoints/agent-runtime/harness.js +127 -116
- package/dist/entrypoints/agent-runtime/sandbox.js +4 -4
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +3 -3
- package/dist/entrypoints/agent-runtime/sqlite/node.js +3 -3
- package/dist/entrypoints/agent-runtime/testing.js +246 -214
- package/dist/entrypoints/agent-runtime.js +224 -203
- package/dist/entrypoints/application/diagnostic-journal.js +63 -203
- package/dist/entrypoints/application/opentelemetry.js +1 -1
- package/dist/entrypoints/application.js +361 -324
- package/dist/entrypoints/cli.d.ts +4 -1
- package/dist/entrypoints/cli.d.ts.map +1 -1
- package/dist/entrypoints/cli.js +57 -35
- package/dist/entrypoints/contract.d.ts +5 -2
- package/dist/entrypoints/contract.d.ts.map +1 -1
- package/dist/entrypoints/contract.js +3 -2
- package/dist/entrypoints/files.d.ts +2 -0
- package/dist/entrypoints/files.d.ts.map +1 -1
- package/dist/entrypoints/files.js +98 -2
- package/dist/entrypoints/index.d.ts +4 -2
- package/dist/entrypoints/index.d.ts.map +1 -1
- package/dist/entrypoints/index.js +348 -282
- package/dist/entrypoints/live.js +89 -71
- package/dist/entrypoints/node.d.ts +2 -1
- package/dist/entrypoints/node.d.ts.map +1 -1
- package/dist/entrypoints/node.js +13 -12
- package/dist/entrypoints/react.js +1 -1
- package/dist/entrypoints/remote.js +4 -4
- package/dist/entrypoints/server.d.ts +4 -2
- package/dist/entrypoints/server.d.ts.map +1 -1
- package/dist/entrypoints/server.js +293 -79
- package/dist/entrypoints/testing.d.ts +2 -1
- package/dist/entrypoints/testing.d.ts.map +1 -1
- package/dist/entrypoints/testing.js +288 -281
- package/dist/entrypoints/tools/connections.js +2 -2
- package/dist/entrypoints/tools/invoker.js +4 -4
- package/dist/entrypoints/tools.d.ts +6 -3
- package/dist/entrypoints/tools.d.ts.map +1 -1
- package/dist/entrypoints/tools.js +245 -202
- package/dist/entrypoints/tracking.js +325 -274
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files/file-io.d.ts +14 -0
- package/dist/files/file-io.d.ts.map +1 -0
- package/dist/index-1j8pm5t0.js +412 -0
- package/dist/{index-kvj7f4hq.js → index-2bxbyab0.js} +10 -2
- package/dist/{index-xnj9bgxe.js → index-2gxzjvyr.js} +2 -162
- package/dist/index-4gcnakhx.js +82 -0
- package/dist/{index-j05z5gn4.js → index-55hkhm67.js} +136 -119
- package/dist/{index-cq4qfycf.js → index-79z7m7dt.js} +227 -59
- package/dist/{index-97ac4gph.js → index-7f68ghdn.js} +3 -3
- package/dist/{index-r7nmqpsg.js → index-7p1f0vp2.js} +1 -1
- package/dist/{index-dcpdvjsc.js → index-863ea9jb.js} +4 -4
- package/dist/index-8p52kd5p.js +242 -0
- package/dist/{index-2qzskm8h.js → index-dce5wxcq.js} +1 -1
- package/dist/index-e01r054k.js +105 -0
- package/dist/{index-sees8yf8.js → index-ensd0r74.js} +118 -111
- package/dist/{index-1nd48tm9.js → index-gxrt5h6v.js} +2 -1
- package/dist/{index-e87kakm4.js → index-ham5sz8m.js} +112 -110
- package/dist/{index-kj2wbeta.js → index-het2he5g.js} +280 -196
- package/dist/{index-vcvkq30w.js → index-hmp4800j.js} +3 -3
- package/dist/{index-gyr48nzg.js → index-k30h66yd.js} +71 -151
- package/dist/{index-2cr1cqhn.js → index-khtgb21r.js} +2 -2
- package/dist/{index-x4799c19.js → index-ndddxtyx.js} +11 -11
- package/dist/{index-dydjcp8z.js → index-np4m8hd3.js} +38 -36
- package/dist/{index-p81267c2.js → index-p3vsv1fb.js} +621 -357
- package/dist/{index-mp5sn43f.js → index-p9e0w8hs.js} +2 -2
- package/dist/{index-rewkc2hj.js → index-p9sh0xx4.js} +3 -3
- package/dist/{index-70yj2ebz.js → index-paz6sgdp.js} +107 -103
- package/dist/{index-bn2cjajt.js → index-px2gm2jp.js} +26 -102
- package/dist/index-s812ycat.js +65 -0
- package/dist/{index-xsqjkha9.js → index-sp1tzwnb.js} +2 -2
- package/dist/{index-n6hxg8as.js → index-t9rhjr9f.js} +615 -566
- package/dist/{index-zeemqhqx.js → index-v260df89.js} +88 -80
- package/dist/{index-fdp6hdxn.js → index-w906953s.js} +5 -1
- package/dist/{index-mdzh02yc.js → index-wdkt63w3.js} +1 -1
- package/dist/{index-58k4fp34.js → index-wsm5w4ee.js} +1 -1
- package/dist/{index-v38qyerb.js → index-xw3s75nj.js} +1 -1
- package/dist/internal/atomic-file.d.ts +23 -2
- package/dist/internal/atomic-file.d.ts.map +1 -1
- package/dist/internal/exclusive-lock.d.ts +51 -0
- package/dist/internal/exclusive-lock.d.ts.map +1 -0
- package/dist/internal/process-identity.d.ts +47 -0
- package/dist/internal/process-identity.d.ts.map +1 -0
- package/dist/internal/with-exclusive-lock.d.ts +67 -0
- package/dist/internal/with-exclusive-lock.d.ts.map +1 -0
- package/dist/live/watch-client.d.ts.map +1 -1
- package/dist/observability/audit.d.ts +2 -2
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +2 -1
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/primitives/export-operation.d.ts +1 -1
- package/dist/primitives/export-operation.d.ts.map +1 -1
- package/dist/realtime/registry.d.ts +0 -5
- package/dist/realtime/registry.d.ts.map +1 -1
- package/dist/server/bun.d.ts +0 -1
- package/dist/server/bun.d.ts.map +1 -1
- package/dist/server/context-contribution.d.ts +1 -1
- package/dist/server/context-contribution.d.ts.map +1 -1
- package/dist/server/context.d.ts +1 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/endpoint-response.d.ts +2 -1
- package/dist/server/endpoint-response.d.ts.map +1 -1
- package/dist/server/error-dispatch.d.ts +1 -1
- package/dist/server/error-dispatch.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +1 -1
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/handler-state.d.ts +1 -1
- package/dist/server/handler-state.d.ts.map +1 -1
- package/dist/server/implement-registry.d.ts +54 -0
- package/dist/server/implement-registry.d.ts.map +1 -0
- package/dist/server/implement.d.ts +9 -55
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +1 -31
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/jwt.d.ts +31 -0
- package/dist/server/middleware/jwt.d.ts.map +1 -0
- package/dist/server/multipart-reader.d.ts +22 -0
- package/dist/server/multipart-reader.d.ts.map +1 -0
- package/dist/server/multipart.d.ts +1 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/server/oauth/cimd.d.ts +88 -0
- package/dist/server/oauth/cimd.d.ts.map +1 -0
- package/dist/server/oauth/endpoints.d.ts +39 -0
- package/dist/server/oauth/endpoints.d.ts.map +1 -0
- package/dist/server/oauth/provider.d.ts +1 -68
- package/dist/server/oauth/provider.d.ts.map +1 -1
- package/dist/server/streaming-pump.d.ts +66 -0
- package/dist/server/streaming-pump.d.ts.map +1 -0
- package/dist/server/streaming-route.d.ts +12 -5
- package/dist/server/streaming-route.d.ts.map +1 -1
- package/dist/server/types.d.ts +4 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/unix-client-bun.d.ts.map +1 -1
- package/dist/server/unix-client-http1.d.ts +76 -0
- package/dist/server/unix-client-http1.d.ts.map +1 -0
- package/dist/server/unix-client.d.ts.map +1 -1
- package/dist/testing/handler-clients.d.ts +2 -1
- package/dist/testing/handler-clients.d.ts.map +1 -1
- package/dist/testing/realtime-probe.d.ts +33 -0
- package/dist/testing/realtime-probe.d.ts.map +1 -0
- package/dist/testing/surface-conformance.d.ts +0 -31
- package/dist/testing/surface-conformance.d.ts.map +1 -1
- package/dist/testing/surface-manifest-digest.d.ts +44 -0
- package/dist/testing/surface-manifest-digest.d.ts.map +1 -0
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/tools/agent.d.ts +1 -1
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/cli/args-fields.d.ts +31 -0
- package/dist/tools/cli/args-fields.d.ts.map +1 -0
- package/dist/tools/cli/args-route.d.ts +42 -0
- package/dist/tools/cli/args-route.d.ts.map +1 -0
- package/dist/tools/cli/args-view.d.ts +32 -0
- package/dist/tools/cli/args-view.d.ts.map +1 -0
- package/dist/tools/cli/args.d.ts +2 -65
- package/dist/tools/cli/args.d.ts.map +1 -1
- package/dist/tools/cli/argument-error.d.ts +5 -0
- package/dist/tools/cli/argument-error.d.ts.map +1 -0
- package/dist/tools/cli/checkpoint.d.ts.map +1 -1
- package/dist/tools/cli/command.d.ts +1 -1
- package/dist/tools/cli/command.d.ts.map +1 -1
- package/dist/tools/cli/config.d.ts +1 -1
- package/dist/tools/cli/config.d.ts.map +1 -1
- package/dist/tools/cli/format.d.ts +2 -1
- package/dist/tools/cli/format.d.ts.map +1 -1
- package/dist/tools/cli/invoke.d.ts +2 -1
- package/dist/tools/cli/invoke.d.ts.map +1 -1
- package/dist/tools/cli/managed-surface.d.ts +1 -1
- package/dist/tools/cli/managed-surface.d.ts.map +1 -1
- package/dist/tools/cli/policy.d.ts +1 -1
- package/dist/tools/cli/policy.d.ts.map +1 -1
- package/dist/tools/cli/prepare.d.ts.map +1 -1
- package/dist/tools/cli/run-managed.d.ts.map +1 -1
- package/dist/tools/cli/session.d.ts +1 -1
- package/dist/tools/cli/session.d.ts.map +1 -1
- package/dist/tools/cli/update.d.ts +19 -0
- package/dist/tools/cli/update.d.ts.map +1 -1
- package/dist/tools/cli/view.d.ts +1 -1
- package/dist/tools/cli/view.d.ts.map +1 -1
- package/dist/tools/cli/wait.d.ts +1 -1
- package/dist/tools/cli/wait.d.ts.map +1 -1
- package/dist/tools/connections/runtime.d.ts +1 -1
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/execute-args.d.ts +23 -0
- package/dist/tools/execute-args.d.ts.map +1 -0
- package/dist/tools/execute-hooks.d.ts +146 -0
- package/dist/tools/execute-hooks.d.ts.map +1 -0
- package/dist/tools/execute-result.d.ts +40 -0
- package/dist/tools/execute-result.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +4 -193
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +2 -1
- package/dist/tools/internal/surface-projector.d.ts.map +1 -1
- package/dist/tools/invoker.d.ts +2 -1
- package/dist/tools/invoker.d.ts.map +1 -1
- package/dist/tools/lifecycle.d.ts +1 -1
- package/dist/tools/lifecycle.d.ts.map +1 -1
- package/dist/tools/mcp/mount.d.ts +1 -1
- package/dist/tools/mcp/mount.d.ts.map +1 -1
- package/dist/tools/mcp/prepare.d.ts +2 -1
- package/dist/tools/mcp/prepare.d.ts.map +1 -1
- package/dist/tools/mcp/progress.d.ts +1 -1
- package/dist/tools/mcp/progress.d.ts.map +1 -1
- package/dist/tools/mcp/register.d.ts +3 -2
- package/dist/tools/mcp/register.d.ts.map +1 -1
- package/dist/tools/mcp/round-context.d.ts +38 -0
- package/dist/tools/mcp/round-context.d.ts.map +1 -0
- package/dist/tools/mcp/round-policy.d.ts +1 -1
- package/dist/tools/mcp/round-policy.d.ts.map +1 -1
- package/dist/tools/mcp/round.d.ts +3 -11
- package/dist/tools/mcp/round.d.ts.map +1 -1
- package/dist/tools/mcp/runtime-tools.d.ts +2 -1
- package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +3 -1
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/native-definition.d.ts +2 -1
- package/dist/tools/native-definition.d.ts.map +1 -1
- package/dist/tools/operations/async-operation-runtime-tools.d.ts +2 -1
- package/dist/tools/operations/async-operation-runtime-tools.d.ts.map +1 -1
- package/dist/tools/operations/async-operation-runtime-types.d.ts +2 -1
- package/dist/tools/operations/async-operation-runtime-types.d.ts.map +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +3 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/tool-logger.d.ts +1 -1
- package/dist/tools/tool-logger.d.ts.map +1 -1
- package/dist/tracking/client-listeners.d.ts +30 -0
- package/dist/tracking/client-listeners.d.ts.map +1 -0
- package/dist/tracking/client-visit.d.ts +61 -0
- package/dist/tracking/client-visit.d.ts.map +1 -0
- package/dist/tracking/client.d.ts +3 -2
- package/dist/tracking/client.d.ts.map +1 -1
- package/llms/application.2.txt +266 -2
- package/llms/application.txt +8 -266
- package/llms/cli.2.txt +80 -0
- package/llms/cli.txt +45 -74
- package/llms/contract.txt +3 -3
- package/llms/files.txt +3 -0
- package/llms/tools.2.txt +88 -279
- package/llms/tools.3.txt +288 -0
- package/llms/tools.txt +27 -65
- package/llms-full.txt +108 -12
- package/llms.txt +5 -5
- package/package.json +1 -1
- package/dist/index-e2z3ahw9.js +0 -325
- package/dist/index-mkm15s49.js +0 -394
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
var DURABILITY_STEP_EVENT_KIND = "durability/step";
|
|
4
4
|
var DURABILITY_PARK_EVENT_KIND = "durability/park";
|
|
5
5
|
var DURABILITY_EVENT_EVENT_KIND = "durability/event";
|
|
6
|
+
var DURABILITY_EFFECT_EVENT_KIND = "durability/effect";
|
|
6
7
|
var StepEventPayloadSchema = z.object({
|
|
7
8
|
runId: z.string().min(1),
|
|
8
9
|
stepName: z.string().min(1),
|
|
@@ -30,6 +31,17 @@ var DeliveredEventPayloadSchema = z.object({
|
|
|
30
31
|
id: z.string().min(1),
|
|
31
32
|
encoded: z.string()
|
|
32
33
|
});
|
|
34
|
+
var EffectIdentity = { runId: z.string().min(1), effectName: z.string().min(1) };
|
|
35
|
+
var EffectEventPayloadSchema = z.discriminatedUnion("phase", [
|
|
36
|
+
z.object({ ...EffectIdentity, phase: z.literal("intent") }),
|
|
37
|
+
z.object({
|
|
38
|
+
...EffectIdentity,
|
|
39
|
+
phase: z.literal("accepted"),
|
|
40
|
+
encoded: z.string(),
|
|
41
|
+
via: z.enum(["run", "reconcile"])
|
|
42
|
+
}),
|
|
43
|
+
z.object({ ...EffectIdentity, phase: z.literal("uncertain") })
|
|
44
|
+
]);
|
|
33
45
|
|
|
34
46
|
class StepResultDecodeError extends Error {
|
|
35
47
|
constructor(message, options) {
|
|
@@ -54,6 +66,17 @@ class StepAbortedError extends Error {
|
|
|
54
66
|
}
|
|
55
67
|
}
|
|
56
68
|
|
|
69
|
+
class EffectUnresolvedError extends Error {
|
|
70
|
+
effectName;
|
|
71
|
+
reason;
|
|
72
|
+
constructor(effectName, reason, options) {
|
|
73
|
+
super(`Effect "${effectName}" is unresolved (${reason}); its intent stands`, options);
|
|
74
|
+
this.effectName = effectName;
|
|
75
|
+
this.reason = reason;
|
|
76
|
+
this.name = "EffectUnresolvedError";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
57
80
|
class ParkRecordDecodeError extends Error {
|
|
58
81
|
constructor(message, options) {
|
|
59
82
|
super(message, options);
|
|
@@ -113,6 +136,9 @@ var DurableJsonSchema = z2.custom((value) => losslessJson(value), "Expected loss
|
|
|
113
136
|
function stepKey(conversationId, runId, stepName) {
|
|
114
137
|
return JSON.stringify([conversationId, runId, stepName]);
|
|
115
138
|
}
|
|
139
|
+
function effectKey(conversationId, runId, effectName) {
|
|
140
|
+
return JSON.stringify([conversationId, runId, "effect", effectName]);
|
|
141
|
+
}
|
|
116
142
|
function sleepParkKey(conversationId, runId, name) {
|
|
117
143
|
return JSON.stringify([conversationId, runId, "sleep", name]);
|
|
118
144
|
}
|
|
@@ -183,8 +209,25 @@ function decodeDeliveredResult(payload, event) {
|
|
|
183
209
|
throw new ParkRecordDecodeError(`Delivered payload for "${payload.event}#${payload.id}" (run ${payload.runId}, ` + `event ${event.eventId}) is not decodable JSON`, { cause });
|
|
184
210
|
}
|
|
185
211
|
}
|
|
212
|
+
function decodeEffect(event) {
|
|
213
|
+
const parsed = EffectEventPayloadSchema.safeParse(event.payload);
|
|
214
|
+
if (!parsed.success) {
|
|
215
|
+
throw new StepResultDecodeError(`Recorded effect event ${event.eventId} in conversation ${event.conversationId} is malformed`, { cause: parsed.error });
|
|
216
|
+
}
|
|
217
|
+
const payload = parsed.data;
|
|
218
|
+
if (payload.phase === "intent")
|
|
219
|
+
return { payload, record: { outcome: undefined } };
|
|
220
|
+
if (payload.phase === "uncertain")
|
|
221
|
+
return { payload, record: { outcome: "uncertain" } };
|
|
222
|
+
try {
|
|
223
|
+
const proof = DurableJsonSchema.parse(JSON.parse(payload.encoded));
|
|
224
|
+
return { payload, record: { outcome: "accepted", proof } };
|
|
225
|
+
} catch (cause) {
|
|
226
|
+
throw new StepResultDecodeError(`Recorded proof for effect "${payload.effectName}" (run ${payload.runId}, ` + `event ${event.eventId}) is not decodable JSON`, { cause });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
186
229
|
async function readDurabilityLedger(store, conversationId, view) {
|
|
187
|
-
const { steps, parks, deliveries } = view;
|
|
230
|
+
const { steps, effects, parks, deliveries } = view;
|
|
188
231
|
let fromSeq = view.nextSeq;
|
|
189
232
|
for (;; ) {
|
|
190
233
|
const page = await store.readEvents({
|
|
@@ -199,6 +242,11 @@ async function readDurabilityLedger(store, conversationId, view) {
|
|
|
199
242
|
} else if (event.kind === DURABILITY_PARK_EVENT_KIND) {
|
|
200
243
|
const payload = decodeParkPayload(event);
|
|
201
244
|
parks.set(parkEventKey(event.conversationId, payload), payload);
|
|
245
|
+
} else if (event.kind === DURABILITY_EFFECT_EVENT_KIND) {
|
|
246
|
+
const { payload, record } = decodeEffect(event);
|
|
247
|
+
const key = effectKey(event.conversationId, payload.runId, payload.effectName);
|
|
248
|
+
if (effects.get(key)?.outcome === undefined)
|
|
249
|
+
effects.set(key, record);
|
|
202
250
|
} else if (event.kind === DURABILITY_EVENT_EVENT_KIND) {
|
|
203
251
|
const payload = decodeDeliveredPayload(event);
|
|
204
252
|
const key = waitParkKey(event.conversationId, payload.runId, payload.event, payload.id);
|
|
@@ -216,6 +264,95 @@ async function readDurabilityLedger(store, conversationId, view) {
|
|
|
216
264
|
return view;
|
|
217
265
|
}
|
|
218
266
|
|
|
267
|
+
// src/durability/effect.ts
|
|
268
|
+
var DEFAULT_RECONCILE_TIMEOUT_MS = 30000;
|
|
269
|
+
var MAX_PROOF_BYTES = 64 * 1024;
|
|
270
|
+
function encodeProof(name, proof) {
|
|
271
|
+
let encoded;
|
|
272
|
+
try {
|
|
273
|
+
encoded = encodeStepResult(name, proof);
|
|
274
|
+
} catch (cause) {
|
|
275
|
+
throw new EffectUnresolvedError(name, "proof-rejected", { cause });
|
|
276
|
+
}
|
|
277
|
+
const bytes = new TextEncoder().encode(encoded).byteLength;
|
|
278
|
+
if (bytes > MAX_PROOF_BYTES) {
|
|
279
|
+
throw new EffectUnresolvedError(name, "proof-rejected", {
|
|
280
|
+
cause: new RangeError(`the proof is ${bytes} bytes; at most ${MAX_PROOF_BYTES} are kept`)
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return encoded;
|
|
284
|
+
}
|
|
285
|
+
function outcomeOf(encoded) {
|
|
286
|
+
return { outcome: "accepted", proof: JSON.parse(encoded) };
|
|
287
|
+
}
|
|
288
|
+
async function append(context, payload, name) {
|
|
289
|
+
await context.store.appendEvent({
|
|
290
|
+
conversationId: context.conversationId,
|
|
291
|
+
kind: DURABILITY_EFFECT_EVENT_KIND,
|
|
292
|
+
payload: { runId: context.runId, effectName: name, ...payload }
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
async function reconcile(context, name, handlers, timeoutMs) {
|
|
296
|
+
const controller = new AbortController;
|
|
297
|
+
let timer;
|
|
298
|
+
const deadline = new Promise((resolve) => {
|
|
299
|
+
timer = setTimeout(() => {
|
|
300
|
+
controller.abort(new Error(`reconcile did not finish within ${timeoutMs} ms`));
|
|
301
|
+
resolve("timeout");
|
|
302
|
+
}, timeoutMs);
|
|
303
|
+
});
|
|
304
|
+
let found;
|
|
305
|
+
try {
|
|
306
|
+
found = await Promise.race([
|
|
307
|
+
Promise.resolve().then(() => handlers.reconcile(controller.signal)),
|
|
308
|
+
deadline
|
|
309
|
+
]);
|
|
310
|
+
} catch (cause) {
|
|
311
|
+
throw new EffectUnresolvedError(name, "reconcile-failed", { cause });
|
|
312
|
+
} finally {
|
|
313
|
+
clearTimeout(timer);
|
|
314
|
+
}
|
|
315
|
+
if (found === "timeout") {
|
|
316
|
+
throw new EffectUnresolvedError(name, "reconcile-timeout", {
|
|
317
|
+
cause: controller.signal.reason
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (found === null) {
|
|
321
|
+
await append(context, { phase: "uncertain" }, name);
|
|
322
|
+
return { outcome: "uncertain" };
|
|
323
|
+
}
|
|
324
|
+
const encoded = encodeProof(name, found);
|
|
325
|
+
await append(context, { phase: "accepted", encoded, via: "reconcile" }, name);
|
|
326
|
+
return outcomeOf(encoded);
|
|
327
|
+
}
|
|
328
|
+
async function executeEffect(context, name, handlers, options) {
|
|
329
|
+
const timeoutMs = options?.reconcileTimeoutMs ?? DEFAULT_RECONCILE_TIMEOUT_MS;
|
|
330
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
331
|
+
throw new TypeError(`reconcileTimeoutMs must be a positive finite number, got ${timeoutMs}`);
|
|
332
|
+
}
|
|
333
|
+
const key = effectKey(context.conversationId, context.runId, name);
|
|
334
|
+
const recorded = (await context.readLedger()).effects.get(key);
|
|
335
|
+
if (recorded?.outcome === "uncertain")
|
|
336
|
+
return { outcome: "uncertain" };
|
|
337
|
+
if (recorded?.outcome === "accepted") {
|
|
338
|
+
return outcomeOf(JSON.stringify(recorded.proof));
|
|
339
|
+
}
|
|
340
|
+
if (options?.signal?.aborted || context.signal?.aborted)
|
|
341
|
+
throw new StepAbortedError(name);
|
|
342
|
+
if (recorded)
|
|
343
|
+
return reconcile(context, name, handlers, timeoutMs);
|
|
344
|
+
await append(context, { phase: "intent" }, name);
|
|
345
|
+
let proof;
|
|
346
|
+
try {
|
|
347
|
+
proof = await handlers.run();
|
|
348
|
+
} catch (cause) {
|
|
349
|
+
throw new EffectUnresolvedError(name, "run-failed", { cause });
|
|
350
|
+
}
|
|
351
|
+
const encoded = encodeProof(name, proof);
|
|
352
|
+
await append(context, { phase: "accepted", encoded, via: "run" }, name);
|
|
353
|
+
return outcomeOf(encoded);
|
|
354
|
+
}
|
|
355
|
+
|
|
219
356
|
// src/durability/scheduler.ts
|
|
220
357
|
var systemClock = {
|
|
221
358
|
now: () => Date.now(),
|
|
@@ -300,6 +437,64 @@ function createParkWaiter(parkWaiters, key, signal) {
|
|
|
300
437
|
return { outcome, cancel: cleanup };
|
|
301
438
|
}
|
|
302
439
|
|
|
440
|
+
// src/durability/parks.ts
|
|
441
|
+
async function executeSleep(park, name, seconds, runOptions) {
|
|
442
|
+
const key = sleepParkKey(park.conversationId, park.runId, name);
|
|
443
|
+
const signal = park.signal && runOptions?.signal ? AbortSignal.any([park.signal, runOptions.signal]) : runOptions?.signal ?? park.signal;
|
|
444
|
+
const ledger = await park.readLedger();
|
|
445
|
+
const existing = ledger.parks.get(key);
|
|
446
|
+
let deadline;
|
|
447
|
+
if (existing) {
|
|
448
|
+
if (existing.kind !== "sleep") {
|
|
449
|
+
throw new ParkRecordDecodeError(`Recorded park "${name}" is not a sleep record`);
|
|
450
|
+
}
|
|
451
|
+
deadline = existing.deadline;
|
|
452
|
+
} else {
|
|
453
|
+
deadline = park.clock.now() + seconds * 1000;
|
|
454
|
+
if (!Number.isSafeInteger(Math.ceil(deadline)))
|
|
455
|
+
throw new TypeError("Sleep deadline exceeds the supported clock range");
|
|
456
|
+
await park.store.appendEvent({
|
|
457
|
+
conversationId: park.conversationId,
|
|
458
|
+
kind: DURABILITY_PARK_EVENT_KIND,
|
|
459
|
+
payload: { runId: park.runId, kind: "sleep", name, deadline }
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (signal?.aborted)
|
|
463
|
+
throw new ParkAbortedError(name);
|
|
464
|
+
await waitForDeadline(park.clock, deadline, name, signal);
|
|
465
|
+
}
|
|
466
|
+
async function executeWait(park, eventName, id, runOptions) {
|
|
467
|
+
const key = waitParkKey(park.conversationId, park.runId, eventName, id);
|
|
468
|
+
const label = `${eventName}#${id}`;
|
|
469
|
+
const signal = park.signal && runOptions?.signal ? AbortSignal.any([park.signal, runOptions.signal]) : runOptions?.signal ?? park.signal;
|
|
470
|
+
for (;; ) {
|
|
471
|
+
if (signal?.aborted)
|
|
472
|
+
throw new ParkAbortedError(label);
|
|
473
|
+
const waiter = createParkWaiter(park.waiters, key, signal);
|
|
474
|
+
let unsubscribe;
|
|
475
|
+
try {
|
|
476
|
+
unsubscribe = park.subscribe?.(() => notifyWaiters(park.waiters, key));
|
|
477
|
+
const ledger = await park.readLedger();
|
|
478
|
+
if (ledger.deliveries.has(key)) {
|
|
479
|
+
return JSON.parse(JSON.stringify(ledger.deliveries.get(key)));
|
|
480
|
+
}
|
|
481
|
+
if (!ledger.parks.has(key)) {
|
|
482
|
+
await park.store.appendEvent({
|
|
483
|
+
conversationId: park.conversationId,
|
|
484
|
+
kind: DURABILITY_PARK_EVENT_KIND,
|
|
485
|
+
payload: { runId: park.runId, kind: "wait", event: eventName, id }
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
const outcome = await waiter.outcome;
|
|
489
|
+
if (outcome === "aborted")
|
|
490
|
+
throw new ParkAbortedError(label);
|
|
491
|
+
} finally {
|
|
492
|
+
waiter.cancel();
|
|
493
|
+
unsubscribe?.();
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
303
498
|
// src/durability/engine.ts
|
|
304
499
|
function releaseSettled(registry, key, pending) {
|
|
305
500
|
const release = () => {
|
|
@@ -315,14 +510,21 @@ function createLocalStepDurability(options) {
|
|
|
315
510
|
const { store, conversationId, runId } = options;
|
|
316
511
|
const inFlight = stores.get(store) ?? {
|
|
317
512
|
steps: new Map,
|
|
513
|
+
effects: new Map,
|
|
318
514
|
parks: new Map,
|
|
319
515
|
waiters: new Map
|
|
320
516
|
};
|
|
321
517
|
stores.set(store, inFlight);
|
|
322
|
-
const {
|
|
518
|
+
const {
|
|
519
|
+
steps: inFlightSteps,
|
|
520
|
+
effects: inFlightEffects,
|
|
521
|
+
parks: inFlightParks,
|
|
522
|
+
waiters: parkWaiters
|
|
523
|
+
} = inFlight;
|
|
323
524
|
const clock = options.clock ?? systemClock;
|
|
324
525
|
const view = {
|
|
325
526
|
steps: new Map,
|
|
527
|
+
effects: new Map,
|
|
326
528
|
parks: new Map,
|
|
327
529
|
deliveries: new Map,
|
|
328
530
|
nextSeq: 1
|
|
@@ -332,6 +534,16 @@ function createLocalStepDurability(options) {
|
|
|
332
534
|
reading = reading.catch(() => view).then(() => readDurabilityLedger(store, conversationId, view));
|
|
333
535
|
return reading;
|
|
334
536
|
};
|
|
537
|
+
const park = {
|
|
538
|
+
store,
|
|
539
|
+
conversationId,
|
|
540
|
+
runId,
|
|
541
|
+
signal: options.signal,
|
|
542
|
+
subscribe: options.subscribe,
|
|
543
|
+
clock,
|
|
544
|
+
waiters: parkWaiters,
|
|
545
|
+
readLedger
|
|
546
|
+
};
|
|
335
547
|
const readRecordedStep = async (name) => {
|
|
336
548
|
const key = stepKey(conversationId, runId, name);
|
|
337
549
|
const records = await readLedger();
|
|
@@ -365,30 +577,16 @@ function createLocalStepDurability(options) {
|
|
|
365
577
|
releaseSettled(inFlightSteps, key, pending);
|
|
366
578
|
return detachedResult(pending);
|
|
367
579
|
};
|
|
368
|
-
const
|
|
369
|
-
const key =
|
|
370
|
-
const
|
|
371
|
-
const ledger = await readLedger();
|
|
372
|
-
const existing = ledger.parks.get(key);
|
|
373
|
-
let deadline;
|
|
580
|
+
const effect = (name, handlers, runOptions) => {
|
|
581
|
+
const key = effectKey(conversationId, runId, name);
|
|
582
|
+
const existing = inFlightEffects.get(key);
|
|
374
583
|
if (existing) {
|
|
375
|
-
|
|
376
|
-
throw new ParkRecordDecodeError(`Recorded park "${name}" is not a sleep record`);
|
|
377
|
-
}
|
|
378
|
-
deadline = existing.deadline;
|
|
379
|
-
} else {
|
|
380
|
-
deadline = clock.now() + seconds * 1000;
|
|
381
|
-
if (!Number.isSafeInteger(Math.ceil(deadline)))
|
|
382
|
-
throw new TypeError("Sleep deadline exceeds the supported clock range");
|
|
383
|
-
await store.appendEvent({
|
|
384
|
-
conversationId,
|
|
385
|
-
kind: DURABILITY_PARK_EVENT_KIND,
|
|
386
|
-
payload: { runId, kind: "sleep", name, deadline }
|
|
387
|
-
});
|
|
584
|
+
return detachedResult(existing);
|
|
388
585
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
586
|
+
const pending = executeEffect({ store, conversationId, runId, signal: options.signal, readLedger }, name, handlers, runOptions);
|
|
587
|
+
inFlightEffects.set(key, pending);
|
|
588
|
+
releaseSettled(inFlightEffects, key, pending);
|
|
589
|
+
return detachedResult(pending);
|
|
392
590
|
};
|
|
393
591
|
const sleep = (input, runOptions) => {
|
|
394
592
|
const { seconds } = input;
|
|
@@ -401,49 +599,18 @@ function createLocalStepDurability(options) {
|
|
|
401
599
|
if (existing) {
|
|
402
600
|
return existing;
|
|
403
601
|
}
|
|
404
|
-
const pending = executeSleep(name, seconds, runOptions);
|
|
602
|
+
const pending = executeSleep(park, name, seconds, runOptions);
|
|
405
603
|
inFlightParks.set(key, pending);
|
|
406
604
|
releaseSettled(inFlightParks, key, pending);
|
|
407
605
|
return pending;
|
|
408
606
|
};
|
|
409
|
-
const executeWait = async (eventName, id, runOptions) => {
|
|
410
|
-
const key = waitParkKey(conversationId, runId, eventName, id);
|
|
411
|
-
const park = `${eventName}#${id}`;
|
|
412
|
-
const signal = options.signal && runOptions?.signal ? AbortSignal.any([options.signal, runOptions.signal]) : runOptions?.signal ?? options.signal;
|
|
413
|
-
for (;; ) {
|
|
414
|
-
if (signal?.aborted)
|
|
415
|
-
throw new ParkAbortedError(park);
|
|
416
|
-
const waiter = createParkWaiter(parkWaiters, key, signal);
|
|
417
|
-
let unsubscribe;
|
|
418
|
-
try {
|
|
419
|
-
unsubscribe = options.subscribe?.(() => notifyWaiters(parkWaiters, key));
|
|
420
|
-
const ledger = await readLedger();
|
|
421
|
-
if (ledger.deliveries.has(key)) {
|
|
422
|
-
return JSON.parse(JSON.stringify(ledger.deliveries.get(key)));
|
|
423
|
-
}
|
|
424
|
-
if (!ledger.parks.has(key)) {
|
|
425
|
-
await store.appendEvent({
|
|
426
|
-
conversationId,
|
|
427
|
-
kind: DURABILITY_PARK_EVENT_KIND,
|
|
428
|
-
payload: { runId, kind: "wait", event: eventName, id }
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
const outcome = await waiter.outcome;
|
|
432
|
-
if (outcome === "aborted")
|
|
433
|
-
throw new ParkAbortedError(park);
|
|
434
|
-
} finally {
|
|
435
|
-
waiter.cancel();
|
|
436
|
-
unsubscribe?.();
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
607
|
const waitFor = (input, runOptions) => {
|
|
441
608
|
const key = waitParkKey(conversationId, runId, input.event, input.id);
|
|
442
609
|
const existing = inFlightParks.get(key);
|
|
443
610
|
if (existing) {
|
|
444
611
|
return detachedResult(existing);
|
|
445
612
|
}
|
|
446
|
-
const pending = executeWait(input.event, input.id, runOptions);
|
|
613
|
+
const pending = executeWait(park, input.event, input.id, runOptions);
|
|
447
614
|
inFlightParks.set(key, pending);
|
|
448
615
|
releaseSettled(inFlightParks, key, pending);
|
|
449
616
|
return detachedResult(pending);
|
|
@@ -467,7 +634,8 @@ function createLocalStepDurability(options) {
|
|
|
467
634
|
hasRecordedStep: async (name) => await readRecordedStep(name) !== undefined,
|
|
468
635
|
sleep,
|
|
469
636
|
waitFor,
|
|
470
|
-
deliver
|
|
637
|
+
deliver,
|
|
638
|
+
effect
|
|
471
639
|
};
|
|
472
640
|
}
|
|
473
641
|
|
|
@@ -504,4 +672,4 @@ function resolveToolDurability(context, callId, signal) {
|
|
|
504
672
|
return contexts.get(context)?.(callId, signal);
|
|
505
673
|
}
|
|
506
674
|
|
|
507
|
-
export { DURABILITY_STEP_EVENT_KIND, StepResultDecodeError, StepResultNotSerializableError, StepAbortedError, ParkRecordDecodeError, ParkAbortedError, createLocalStepDurability, AgentToolError, isAgentToolError, createToolDurabilityContext, resolveToolDurability };
|
|
675
|
+
export { DURABILITY_STEP_EVENT_KIND, StepResultDecodeError, StepResultNotSerializableError, StepAbortedError, EffectUnresolvedError, ParkRecordDecodeError, ParkAbortedError, createLocalStepDurability, AgentToolError, isAgentToolError, createToolDurabilityContext, resolveToolDurability };
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
createToolChronology,
|
|
17
17
|
isAssistantHistoryEvidence,
|
|
18
18
|
isCompleteAgentHistoryTurn
|
|
19
|
-
} from "./index-
|
|
19
|
+
} from "./index-gxrt5h6v.js";
|
|
20
20
|
import {
|
|
21
21
|
AgentAssistantPlaceholderSchema,
|
|
22
22
|
AgentJsonObjectSchema,
|
|
@@ -33,11 +33,11 @@ import {
|
|
|
33
33
|
createLocalStepDurability,
|
|
34
34
|
createToolDurabilityContext,
|
|
35
35
|
isAgentToolError
|
|
36
|
-
} from "./index-
|
|
36
|
+
} from "./index-79z7m7dt.js";
|
|
37
37
|
import {
|
|
38
38
|
ToolExecutionControlError,
|
|
39
39
|
isToolExecutionControlError
|
|
40
|
-
} from "./index-
|
|
40
|
+
} from "./index-55hkhm67.js";
|
|
41
41
|
import {
|
|
42
42
|
isRecord,
|
|
43
43
|
transportResult
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentToolError,
|
|
3
3
|
resolveToolDurability
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-79z7m7dt.js";
|
|
5
5
|
import {
|
|
6
6
|
collectToolSurface
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-hmp4800j.js";
|
|
8
8
|
import {
|
|
9
9
|
createToolRunner,
|
|
10
10
|
formatToolError
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-khtgb21r.js";
|
|
12
12
|
import {
|
|
13
13
|
isToolExecutionControlError,
|
|
14
14
|
toolCauseFromResult,
|
|
15
15
|
toolResultFromError
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-55hkhm67.js";
|
|
17
17
|
import {
|
|
18
18
|
isRecord
|
|
19
19
|
} from "./index-hffjpmq9.js";
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRecord
|
|
3
|
+
} from "./index-hffjpmq9.js";
|
|
4
|
+
|
|
5
|
+
// src/internal/exclusive-lock.ts
|
|
6
|
+
import { constants } from "node:fs";
|
|
7
|
+
import { lstat, open, unlink } from "node:fs/promises";
|
|
8
|
+
import { hostname } from "node:os";
|
|
9
|
+
|
|
10
|
+
// src/internal/process-identity.ts
|
|
11
|
+
import { execFile } from "node:child_process";
|
|
12
|
+
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { platform } from "node:os";
|
|
14
|
+
import { promisify } from "node:util";
|
|
15
|
+
var detectedIdentity;
|
|
16
|
+
async function firstReadableLine(paths) {
|
|
17
|
+
for (const path of paths) {
|
|
18
|
+
try {
|
|
19
|
+
const text = (await readFile(path, "utf8")).trim();
|
|
20
|
+
if (text.length > 0)
|
|
21
|
+
return text;
|
|
22
|
+
} catch {}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
var run = promisify(execFile);
|
|
27
|
+
function parsePlatformUuid(dump) {
|
|
28
|
+
return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(dump)?.[1] ?? null;
|
|
29
|
+
}
|
|
30
|
+
async function readDarwinPlatformUuid(command = "ioreg") {
|
|
31
|
+
try {
|
|
32
|
+
const { stdout } = await run(command, ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
33
|
+
timeout: 2000,
|
|
34
|
+
maxBuffer: 1 << 20,
|
|
35
|
+
encoding: "utf8"
|
|
36
|
+
});
|
|
37
|
+
return parsePlatformUuid(stdout);
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function machineIdentity(declared) {
|
|
43
|
+
if (declared !== undefined)
|
|
44
|
+
return declared;
|
|
45
|
+
if (detectedIdentity !== undefined)
|
|
46
|
+
return detectedIdentity;
|
|
47
|
+
detectedIdentity = platform() === "darwin" ? await readDarwinPlatformUuid() : await firstReadableLine(["/etc/machine-id", "/var/lib/dbus/machine-id"]);
|
|
48
|
+
return detectedIdentity;
|
|
49
|
+
}
|
|
50
|
+
async function probeLiveness(pid) {
|
|
51
|
+
try {
|
|
52
|
+
process.kill(pid, 0);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return isRecord(error) && error.code === "ESRCH" ? "gone" : "alive";
|
|
55
|
+
}
|
|
56
|
+
return await isZombieProcess(pid) ? "gone" : "alive";
|
|
57
|
+
}
|
|
58
|
+
async function isZombieProcess(pid, seam = {}) {
|
|
59
|
+
const { procRoot = "/proc", psCommand = "ps" } = seam;
|
|
60
|
+
try {
|
|
61
|
+
const stat = await readFile(`${procRoot}/${pid}/stat`, "utf8");
|
|
62
|
+
return stat.slice(stat.lastIndexOf(")") + 1).trimStart().startsWith("Z");
|
|
63
|
+
} catch {}
|
|
64
|
+
try {
|
|
65
|
+
const { stdout } = await run(psCommand, ["-o", "state=", "-p", String(pid)], {
|
|
66
|
+
timeout: 2000,
|
|
67
|
+
maxBuffer: 1 << 16,
|
|
68
|
+
encoding: "utf8"
|
|
69
|
+
});
|
|
70
|
+
return stdout.trim().startsWith("Z");
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/internal/exclusive-lock.ts
|
|
77
|
+
function isCode(error, code) {
|
|
78
|
+
return isRecord(error) && error.code === code;
|
|
79
|
+
}
|
|
80
|
+
function readOwner(text) {
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
parsed = JSON.parse(text);
|
|
84
|
+
} catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!isRecord(parsed))
|
|
88
|
+
return;
|
|
89
|
+
const { pid, host, acquiredAt, machine } = parsed;
|
|
90
|
+
if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid <= 0)
|
|
91
|
+
return;
|
|
92
|
+
if (typeof host !== "string" || host.length === 0)
|
|
93
|
+
return;
|
|
94
|
+
if (typeof acquiredAt !== "string")
|
|
95
|
+
return;
|
|
96
|
+
const identified = typeof machine === "string" && machine.length > 0 ? { machine } : {};
|
|
97
|
+
return { pid, host, acquiredAt, ...identified };
|
|
98
|
+
}
|
|
99
|
+
function attribute(owner, identity) {
|
|
100
|
+
if (identity !== null && owner.machine !== undefined) {
|
|
101
|
+
return owner.machine === identity ? "this-machine" : "another-machine";
|
|
102
|
+
}
|
|
103
|
+
return owner.host === hostname() ? "this-machine" : "unattributable";
|
|
104
|
+
}
|
|
105
|
+
async function diagnose(owner, declaredIdentity) {
|
|
106
|
+
if (!owner)
|
|
107
|
+
return { attribution: "unattributable", liveness: "not-probed", owner: null };
|
|
108
|
+
const attribution = attribute(owner, await machineIdentity(declaredIdentity));
|
|
109
|
+
if (attribution !== "this-machine")
|
|
110
|
+
return { attribution, liveness: "not-probed", owner };
|
|
111
|
+
return { attribution, liveness: await probeLiveness(owner.pid), owner };
|
|
112
|
+
}
|
|
113
|
+
async function readLockFile(path) {
|
|
114
|
+
let handle;
|
|
115
|
+
try {
|
|
116
|
+
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
117
|
+
const info = await handle.stat();
|
|
118
|
+
return {
|
|
119
|
+
owner: readOwner(await handle.readFile("utf8")),
|
|
120
|
+
ino: info.ino,
|
|
121
|
+
mtimeMs: info.mtimeMs
|
|
122
|
+
};
|
|
123
|
+
} catch {
|
|
124
|
+
return;
|
|
125
|
+
} finally {
|
|
126
|
+
await handle?.close().catch(() => {
|
|
127
|
+
return;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function unlinkIfSame(path, ino) {
|
|
132
|
+
try {
|
|
133
|
+
if ((await lstat(path)).ino !== ino)
|
|
134
|
+
return;
|
|
135
|
+
await unlink(path);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (!isCode(error, "ENOENT"))
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function createOwned(path, options, reclaimed) {
|
|
142
|
+
const identity = await machineIdentity(options.machineIdentity);
|
|
143
|
+
const handle = await open(path, "wx", options.mode);
|
|
144
|
+
const owner = {
|
|
145
|
+
pid: process.pid,
|
|
146
|
+
host: hostname(),
|
|
147
|
+
acquiredAt: new Date().toISOString(),
|
|
148
|
+
...identity !== null && { machine: identity }
|
|
149
|
+
};
|
|
150
|
+
let ino;
|
|
151
|
+
try {
|
|
152
|
+
await handle.writeFile(`${JSON.stringify(owner)}
|
|
153
|
+
`, "utf8");
|
|
154
|
+
ino = (await handle.stat()).ino;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
await handle.close().catch(() => {
|
|
157
|
+
return;
|
|
158
|
+
});
|
|
159
|
+
await unlink(path).catch(() => {
|
|
160
|
+
return;
|
|
161
|
+
});
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
let released = false;
|
|
165
|
+
return {
|
|
166
|
+
path,
|
|
167
|
+
owner,
|
|
168
|
+
reclaimed,
|
|
169
|
+
async release() {
|
|
170
|
+
if (released)
|
|
171
|
+
return;
|
|
172
|
+
released = true;
|
|
173
|
+
try {
|
|
174
|
+
await handle.close();
|
|
175
|
+
} finally {
|
|
176
|
+
await unlinkIfSame(path, ino);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function reclaimable(state, diagnosis, graceMs) {
|
|
182
|
+
if (state.owner)
|
|
183
|
+
return diagnosis.liveness === "gone";
|
|
184
|
+
return graceMs !== undefined && Date.now() - state.mtimeMs >= graceMs;
|
|
185
|
+
}
|
|
186
|
+
async function reclaimUnderGuard(path, options) {
|
|
187
|
+
const guardPath = `${path}.reclaim`;
|
|
188
|
+
let guard;
|
|
189
|
+
try {
|
|
190
|
+
guard = await createOwned(guardPath, options, false);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (!isCode(error, "EEXIST"))
|
|
193
|
+
throw error;
|
|
194
|
+
const stale = await readLockFile(guardPath);
|
|
195
|
+
if (stale) {
|
|
196
|
+
const verdict = await diagnose(stale.owner, options.machineIdentity);
|
|
197
|
+
if (reclaimable(stale, verdict, options.ownerlessGraceMs ?? 5000)) {
|
|
198
|
+
await unlinkIfSame(guardPath, stale.ino);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const state = await readLockFile(path);
|
|
205
|
+
if (state) {
|
|
206
|
+
const diagnosis = await diagnose(state.owner, options.machineIdentity);
|
|
207
|
+
if (!reclaimable(state, diagnosis, options.ownerlessGraceMs))
|
|
208
|
+
return "refused";
|
|
209
|
+
await unlinkIfSame(path, state.ino);
|
|
210
|
+
}
|
|
211
|
+
return await createOwned(path, options, state !== undefined).catch((error) => {
|
|
212
|
+
if (isCode(error, "EEXIST"))
|
|
213
|
+
return "refused";
|
|
214
|
+
throw error;
|
|
215
|
+
});
|
|
216
|
+
} finally {
|
|
217
|
+
await guard.release().catch(() => {
|
|
218
|
+
return;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function attemptExclusiveLock(path, options) {
|
|
223
|
+
try {
|
|
224
|
+
return { held: await createOwned(path, options, false) };
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (!isCode(error, "EEXIST"))
|
|
227
|
+
throw error;
|
|
228
|
+
if (!options.reclaim)
|
|
229
|
+
return { error };
|
|
230
|
+
const state = await readLockFile(path);
|
|
231
|
+
const diagnosis = await diagnose(state?.owner, options.machineIdentity);
|
|
232
|
+
if (!state || !reclaimable(state, diagnosis, options.ownerlessGraceMs)) {
|
|
233
|
+
return { error, diagnosis };
|
|
234
|
+
}
|
|
235
|
+
const reclaimed = await reclaimUnderGuard(path, options);
|
|
236
|
+
if (reclaimed !== undefined && reclaimed !== "refused")
|
|
237
|
+
return { held: reclaimed };
|
|
238
|
+
return { error, diagnosis };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export { attemptExclusiveLock };
|