@graphorin/core 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +14 -8
  3. package/dist/channels/channels.d.ts.map +1 -1
  4. package/dist/channels/index.d.ts +2 -2
  5. package/dist/channels/index.js +2 -2
  6. package/dist/channels/pause.d.ts +47 -2
  7. package/dist/channels/pause.d.ts.map +1 -1
  8. package/dist/channels/pause.js +62 -2
  9. package/dist/channels/pause.js.map +1 -1
  10. package/dist/contracts/checkpoint-store.d.ts +97 -1
  11. package/dist/contracts/checkpoint-store.d.ts.map +1 -1
  12. package/dist/contracts/checkpoint-store.js.map +1 -1
  13. package/dist/contracts/index.d.ts +5 -5
  14. package/dist/contracts/index.js +2 -1
  15. package/dist/contracts/memory-store.d.ts +59 -2
  16. package/dist/contracts/memory-store.d.ts.map +1 -1
  17. package/dist/contracts/provider.d.ts +20 -6
  18. package/dist/contracts/provider.d.ts.map +1 -1
  19. package/dist/contracts/session-store.d.ts +10 -2
  20. package/dist/contracts/session-store.d.ts.map +1 -1
  21. package/dist/contracts/tool.d.ts +75 -1
  22. package/dist/contracts/tool.d.ts.map +1 -1
  23. package/dist/contracts/tool.js +57 -0
  24. package/dist/contracts/tool.js.map +1 -0
  25. package/dist/contracts/tracer.d.ts +53 -5
  26. package/dist/contracts/tracer.d.ts.map +1 -1
  27. package/dist/contracts/tracer.js.map +1 -1
  28. package/dist/index.d.ts +14 -9
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +11 -7
  31. package/dist/index.js.map +1 -1
  32. package/dist/package.js +6 -0
  33. package/dist/package.js.map +1 -0
  34. package/dist/types/agent-event-wire.d.ts +74 -0
  35. package/dist/types/agent-event-wire.d.ts.map +1 -0
  36. package/dist/types/agent-event-wire.js +130 -0
  37. package/dist/types/agent-event-wire.js.map +1 -0
  38. package/dist/types/agent-event.d.ts +48 -6
  39. package/dist/types/agent-event.d.ts.map +1 -1
  40. package/dist/types/index.d.ts +3 -2
  41. package/dist/types/index.js +2 -1
  42. package/dist/types/memory.d.ts +11 -0
  43. package/dist/types/memory.d.ts.map +1 -1
  44. package/dist/types/run.d.ts +86 -4
  45. package/dist/types/run.d.ts.map +1 -1
  46. package/dist/types/run.js.map +1 -1
  47. package/dist/types/tool.d.ts +10 -0
  48. package/dist/types/tool.d.ts.map +1 -1
  49. package/dist/types/usage.d.ts +11 -1
  50. package/dist/types/usage.d.ts.map +1 -1
  51. package/dist/types/usage.js.map +1 -1
  52. package/dist/utils/binary-json.d.ts +165 -0
  53. package/dist/utils/binary-json.d.ts.map +1 -0
  54. package/dist/utils/binary-json.js +240 -0
  55. package/dist/utils/binary-json.js.map +1 -0
  56. package/dist/utils/index.d.ts +2 -1
  57. package/dist/utils/index.js +2 -1
  58. package/dist/utils/validation.d.ts +10 -1
  59. package/dist/utils/validation.d.ts.map +1 -1
  60. package/dist/utils/validation.js +1 -1
  61. package/dist/utils/validation.js.map +1 -1
  62. package/package.json +9 -7
  63. package/src/channels/channels.ts +206 -0
  64. package/src/channels/directive.ts +41 -0
  65. package/src/channels/dispatch.ts +37 -0
  66. package/src/channels/durable.ts +151 -0
  67. package/src/channels/index.ts +62 -0
  68. package/src/channels/pause.ts +216 -0
  69. package/src/contracts/auth-token-store.ts +42 -0
  70. package/src/contracts/checkpoint-store.ts +256 -0
  71. package/src/contracts/embedder.ts +42 -0
  72. package/src/contracts/eval-scorer.ts +44 -0
  73. package/src/contracts/index.ts +112 -0
  74. package/src/contracts/local-provider-trust.ts +33 -0
  75. package/src/contracts/logger.ts +61 -0
  76. package/src/contracts/memory-store.ts +187 -0
  77. package/src/contracts/oauth-server-store.ts +78 -0
  78. package/src/contracts/preferred-model.ts +56 -0
  79. package/src/contracts/provider.ts +316 -0
  80. package/src/contracts/reasoning-retention.ts +52 -0
  81. package/src/contracts/redaction-validator.ts +56 -0
  82. package/src/contracts/sandbox.ts +70 -0
  83. package/src/contracts/secret-ref.ts +22 -0
  84. package/src/contracts/secret-value.ts +117 -0
  85. package/src/contracts/secrets-store.ts +90 -0
  86. package/src/contracts/session-store.ts +163 -0
  87. package/src/contracts/token-counter.ts +23 -0
  88. package/src/contracts/tool.ts +397 -0
  89. package/src/contracts/tracer.ts +219 -0
  90. package/src/contracts/trigger-store.ts +40 -0
  91. package/src/index.ts +23 -0
  92. package/src/types/agent-event-wire.ts +193 -0
  93. package/src/types/agent-event.ts +579 -0
  94. package/src/types/handoff.ts +111 -0
  95. package/src/types/index.ts +148 -0
  96. package/src/types/memory.ts +427 -0
  97. package/src/types/message.ts +174 -0
  98. package/src/types/run.ts +312 -0
  99. package/src/types/sensitivity.ts +35 -0
  100. package/src/types/session-scope.ts +18 -0
  101. package/src/types/stop-condition.ts +108 -0
  102. package/src/types/tool-call.ts +24 -0
  103. package/src/types/tool.ts +324 -0
  104. package/src/types/usage.ts +120 -0
  105. package/src/types/workflow-event.ts +132 -0
  106. package/src/utils/assert-never.ts +24 -0
  107. package/src/utils/async-context.ts +55 -0
  108. package/src/utils/binary-json.ts +425 -0
  109. package/src/utils/hash.ts +122 -0
  110. package/src/utils/index.ts +57 -0
  111. package/src/utils/streams.ts +233 -0
  112. package/src/utils/validation.ts +82 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-json.js","names":["B64_LOOKUP: ReadonlyMap<string, number>"],"sources":["../../src/utils/binary-json.ts"],"sourcesContent":["/**\n * JSON-safe projection of binary-bearing message content (W-004).\n *\n * `Message` and `ToolResult.contentParts` carry `Uint8Array | URL`\n * payloads that a naive `JSON.stringify` silently corrupts: a\n * `Uint8Array` turns into an object with numeric keys and a `URL`\n * into `{}`. The wire types below replace those fields with explicit\n * {@link EncodedBytes} / {@link EncodedUrl} envelopes so a run state\n * (or any message transcript) survives `JSON.parse(JSON.stringify(x))`\n * byte-for-byte.\n *\n * The codec is runtime-neutral by design: base64 is hand-rolled (no\n * `Buffer`, no `btoa`) so the module works in browsers, workers and\n * edge runtimes exactly as it does under Node.\n *\n * @packageDocumentation\n */\n\nimport type {\n AssistantMessage,\n AudioContent,\n FileContent,\n ImageContent,\n Message,\n MessageContent,\n SystemMessage,\n ToolMessage,\n UserMessage,\n} from '../types/message.js';\nimport type { PendingSubRun, RunState, RunStep } from '../types/run.js';\nimport type { CompletedToolCall, ToolError, ToolResult } from '../types/tool.js';\n\n/**\n * Base64-encoded binary payload as it appears on the wire.\n *\n * @stable\n */\nexport interface EncodedBytes {\n readonly enc: 'base64';\n readonly data: string;\n}\n\n/**\n * URL reference as it appears on the wire (`URL` instances do not\n * survive `JSON.stringify`).\n *\n * @stable\n */\nexport interface EncodedUrl {\n readonly enc: 'url';\n readonly href: string;\n}\n\n/**\n * Wire form of a `Uint8Array | URL` binary field.\n *\n * @stable\n */\nexport type EncodedBinary = EncodedBytes | EncodedUrl;\n\n/** Wire twin of {@link ImageContent}. @stable */\nexport interface WireImageContent extends Omit<ImageContent, 'image'> {\n readonly image: EncodedBinary;\n}\n\n/** Wire twin of {@link AudioContent}. @stable */\nexport interface WireAudioContent extends Omit<AudioContent, 'audio'> {\n readonly audio: EncodedBinary;\n}\n\n/** Wire twin of {@link FileContent}. @stable */\nexport interface WireFileContent extends Omit<FileContent, 'file'> {\n readonly file: EncodedBinary;\n}\n\n/**\n * JSON-safe twin of {@link MessageContent}: binary-bearing variants\n * carry {@link EncodedBinary} envelopes, text/reasoning variants pass\n * through untouched.\n *\n * @stable\n */\nexport type WireMessageContent =\n | Exclude<MessageContent, ImageContent | AudioContent | FileContent>\n | WireImageContent\n | WireAudioContent\n | WireFileContent;\n\n/** Wire twin of {@link UserMessage}. @stable */\nexport interface WireUserMessage extends Omit<UserMessage, 'content'> {\n readonly content: string | readonly WireMessageContent[];\n}\n\n/** Wire twin of {@link AssistantMessage}. @stable */\nexport interface WireAssistantMessage extends Omit<AssistantMessage, 'content'> {\n readonly content: string | readonly WireMessageContent[];\n}\n\n/** Wire twin of {@link ToolMessage}. @stable */\nexport interface WireToolMessage extends Omit<ToolMessage, 'content'> {\n readonly content: string | readonly WireMessageContent[];\n}\n\n/**\n * JSON-safe twin of {@link Message}. System messages are plain strings\n * and pass through unchanged.\n *\n * @stable\n */\nexport type WireMessage = SystemMessage | WireUserMessage | WireAssistantMessage | WireToolMessage;\n\n/** Wire twin of {@link ToolResult}: `contentParts` are encoded. @stable */\nexport type WireToolResult<TOutput = unknown> = Omit<ToolResult<TOutput>, 'contentParts'> & {\n readonly contentParts?: readonly WireMessageContent[];\n};\n\n/** Wire twin of `ToolOutcome`. @stable */\nexport type WireToolOutcome<TOutput = unknown> = WireToolResult<TOutput> | ToolError;\n\n/** Wire twin of {@link CompletedToolCall}. @stable */\nexport type WireCompletedToolCall<TOutput = unknown> = Omit<\n CompletedToolCall<TOutput>,\n 'outcome'\n> & {\n readonly outcome: WireToolOutcome<TOutput>;\n};\n\n/** Wire twin of {@link RunStep}. @stable */\nexport type WireRunStep = Omit<RunStep, 'toolCalls'> & {\n readonly toolCalls: readonly WireCompletedToolCall[];\n};\n\n/** Wire twin of {@link PendingSubRun}: the child state recurses. @stable */\nexport type WirePendingSubRun = Omit<PendingSubRun, 'state'> & {\n readonly state: WireRunState;\n};\n\n/**\n * JSON-safe twin of {@link RunState}: `messages`, every\n * `steps[].toolCalls[].outcome.contentParts`, and each parked\n * `pendingSubRuns[].state` (recursively, W-001) are projected through\n * the binary codec. Everything else is structurally identical.\n *\n * `pendingApprovals[].args` and `ToolResult.output` are model-produced\n * JSON and are assumed JSON-safe already - the projection does not\n * walk them.\n *\n * @stable\n */\nexport type WireRunState = Omit<RunState, 'messages' | 'steps' | 'pendingSubRuns'> & {\n readonly messages: readonly WireMessage[];\n readonly steps: readonly WireRunStep[];\n readonly pendingSubRuns?: readonly WirePendingSubRun[];\n};\n\nconst B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\nconst B64_LOOKUP: ReadonlyMap<string, number> = new Map(\n [...B64_ALPHABET].map((ch, i) => [ch, i] as const),\n);\n\n/**\n * Encode bytes as standard (padded) base64 without relying on `Buffer`\n * or `btoa`.\n *\n * @stable\n */\nexport function bytesToBase64(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] as number;\n const b1 = i + 1 < bytes.length ? (bytes[i + 1] as number) : undefined;\n const b2 = i + 2 < bytes.length ? (bytes[i + 2] as number) : undefined;\n out += B64_ALPHABET[b0 >> 2];\n out += B64_ALPHABET[((b0 & 0x03) << 4) | ((b1 ?? 0) >> 4)];\n out += b1 === undefined ? '=' : B64_ALPHABET[((b1 & 0x0f) << 2) | ((b2 ?? 0) >> 6)];\n out += b2 === undefined ? '=' : B64_ALPHABET[b2 & 0x3f];\n }\n return out;\n}\n\n/**\n * Decode standard base64 (padding optional). Throws on characters\n * outside the base64 alphabet.\n *\n * @stable\n */\nexport function base64ToBytes(data: string): Uint8Array {\n // Strip trailing padding with a loop: /=+$/ backtracks polynomially\n // on '='-heavy adversarial input (CodeQL js/polynomial-redos).\n let end = data.length;\n while (end > 0 && data.charCodeAt(end - 1) === 0x3d /* '=' */) end -= 1;\n const stripped = data.slice(0, end);\n const out = new Uint8Array(Math.floor((stripped.length * 6) / 8));\n let offset = 0;\n let buffer = 0;\n let bits = 0;\n for (const ch of stripped) {\n const value = B64_LOOKUP.get(ch);\n if (value === undefined) {\n throw new Error(`base64ToBytes: invalid base64 character ${JSON.stringify(ch)}`);\n }\n buffer = (buffer << 6) | value;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[offset] = (buffer >> bits) & 0xff;\n offset += 1;\n }\n }\n return out;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction encodeBinary(value: Uint8Array | URL | EncodedBinary): EncodedBinary {\n if (value instanceof Uint8Array) return { enc: 'base64', data: bytesToBase64(value) };\n if (value instanceof URL) return { enc: 'url', href: value.href };\n // Already wire-encoded (idempotent re-projection).\n return value;\n}\n\n/**\n * Repair the exact corruption `JSON.stringify(Uint8Array)` produces: an\n * object whose keys are the dense indices `0..n-1` with byte values.\n * Applied ONLY to known binary fields of legacy (schema <= 1.1)\n * payloads; anything else is returned as-is.\n */\nfunction repairNumericKeyBytes(value: Record<string, unknown>): Uint8Array | undefined {\n const keys = Object.keys(value);\n if (keys.length === 0) return undefined;\n const out = new Uint8Array(keys.length);\n for (let i = 0; i < keys.length; i += 1) {\n const raw = value[String(i)];\n if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < 0 || raw > 255) {\n return undefined;\n }\n out[i] = raw;\n }\n return out;\n}\n\nfunction decodeBinary(value: unknown): Uint8Array | URL | unknown {\n if (value instanceof Uint8Array || value instanceof URL) return value;\n if (!isRecord(value)) return value;\n if (value.enc === 'base64' && typeof value.data === 'string') {\n return base64ToBytes(value.data);\n }\n if (value.enc === 'url' && typeof value.href === 'string') {\n return new URL(value.href);\n }\n // Legacy repair path: schema 1.0/1.1 checkpoints stringified the raw\n // Uint8Array into a numeric-key object. A corrupted URL serialized to\n // `{}` and is unrecoverable - it stays as-is.\n const repaired = repairNumericKeyBytes(value);\n return repaired ?? value;\n}\n\nfunction encodeContentPart(part: MessageContent | WireMessageContent): WireMessageContent {\n switch (part.type) {\n case 'image':\n return { ...part, image: encodeBinary(part.image) };\n case 'audio':\n return { ...part, audio: encodeBinary(part.audio) };\n case 'file':\n return { ...part, file: encodeBinary(part.file) };\n default:\n return part;\n }\n}\n\nfunction decodeContentPart(part: WireMessageContent | MessageContent): MessageContent {\n switch (part.type) {\n case 'image':\n return { ...part, image: decodeBinary(part.image) as Uint8Array | URL };\n case 'audio':\n return { ...part, audio: decodeBinary(part.audio) as Uint8Array | URL };\n case 'file':\n return { ...part, file: decodeBinary(part.file) as Uint8Array | URL };\n default:\n return part;\n }\n}\n\n/**\n * Project multimodal content parts into their JSON-safe wire form.\n * Text and reasoning parts pass through untouched.\n *\n * @stable\n */\nexport function toJsonSafeContentParts(\n parts: readonly MessageContent[],\n): readonly WireMessageContent[] {\n return parts.map(encodeContentPart);\n}\n\n/**\n * Inverse of {@link toJsonSafeContentParts}. Accepts legacy corrupted\n * payloads (numeric-key byte objects) and repairs them best-effort.\n *\n * @stable\n */\nexport function fromJsonSafeContentParts(\n parts: readonly WireMessageContent[],\n): readonly MessageContent[] {\n return parts.map(decodeContentPart);\n}\n\n/**\n * Project a {@link Message} into its JSON-safe wire form. Idempotent:\n * projecting an already-wire message returns an equivalent value.\n *\n * @stable\n */\nexport function toJsonSafeMessage(message: Message | WireMessage): WireMessage {\n if (message.role === 'system' || typeof message.content === 'string') {\n return message as WireMessage;\n }\n return { ...message, content: message.content.map(encodeContentPart) } as WireMessage;\n}\n\n/**\n * Inverse of {@link toJsonSafeMessage}.\n *\n * @stable\n */\nexport function fromJsonSafeMessage(message: WireMessage | Message): Message {\n if (message.role === 'system' || typeof message.content === 'string') {\n return message as Message;\n }\n return { ...message, content: message.content.map(decodeContentPart) } as Message;\n}\n\nfunction encodeOutcome(outcome: CompletedToolCall['outcome']): WireToolOutcome {\n // Defensive: tolerate loose historical/fixture shapes where the\n // outcome is missing or not an object.\n if (isRecord(outcome) && Array.isArray(outcome.contentParts)) {\n return {\n ...outcome,\n contentParts: toJsonSafeContentParts(outcome.contentParts as readonly MessageContent[]),\n } as WireToolOutcome;\n }\n return outcome as WireToolOutcome;\n}\n\nfunction decodeOutcome(outcome: WireToolOutcome): CompletedToolCall['outcome'] {\n if (isRecord(outcome) && Array.isArray(outcome.contentParts)) {\n return {\n ...outcome,\n contentParts: fromJsonSafeContentParts(outcome.contentParts as readonly WireMessageContent[]),\n } as CompletedToolCall['outcome'];\n }\n return outcome as CompletedToolCall['outcome'];\n}\n\nfunction encodeStep(step: RunStep | WireRunStep): WireRunStep {\n if (step.toolCalls.length === 0) return step as WireRunStep;\n return {\n ...step,\n toolCalls: step.toolCalls.map((tc) => ({\n ...tc,\n outcome: encodeOutcome(tc.outcome as CompletedToolCall['outcome']),\n })),\n };\n}\n\nfunction decodeStep(step: WireRunStep): RunStep {\n if (step.toolCalls.length === 0) return step as unknown as RunStep;\n return {\n ...step,\n toolCalls: step.toolCalls.map((tc) => ({ ...tc, outcome: decodeOutcome(tc.outcome) })),\n } as unknown as RunStep;\n}\n\n/**\n * Project a full {@link RunState} into its JSON-safe {@link WireRunState}\n * twin: `messages` and `steps[].toolCalls[].outcome.contentParts` go\n * through the binary codec, everything else is copied structurally.\n *\n * @stable\n */\nexport function toJsonSafeRunState(state: RunState | WireRunState): WireRunState {\n const subRuns = state.pendingSubRuns;\n return {\n ...state,\n messages: state.messages.map(toJsonSafeMessage),\n steps: state.steps.map(encodeStep),\n // W-001: parked child states carry binary-bearing messages of their\n // own - project them recursively (idempotent, like every codec leg).\n ...(subRuns !== undefined && subRuns.length > 0\n ? {\n pendingSubRuns: subRuns.map((sub) => ({\n ...sub,\n state: toJsonSafeRunState(sub.state),\n })),\n }\n : {}),\n } as WireRunState;\n}\n\n/**\n * Inverse of {@link toJsonSafeRunState}. Best-effort: legacy corrupted\n * binary fields (numeric-key byte objects from schema <= 1.1 payloads)\n * are repaired to `Uint8Array`; unrecoverable shapes are left as-is.\n *\n * @stable\n */\nexport function fromJsonSafeRunState(state: WireRunState): RunState {\n const subRuns = state.pendingSubRuns;\n return {\n ...state,\n messages: state.messages.map(fromJsonSafeMessage),\n steps: state.steps.map(decodeStep),\n ...(subRuns !== undefined && subRuns.length > 0\n ? {\n pendingSubRuns: subRuns.map((sub) => ({\n ...sub,\n state: fromJsonSafeRunState(sub.state),\n })),\n }\n : {}),\n } as RunState;\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { ValidationResult, ZodLikeError, ZodLikeSafeParseResult, ZodLikeSchema, validate, validateOrThrow } from "./validation.js";
2
+ import { EncodedBinary, EncodedBytes, EncodedUrl, WireAssistantMessage, WireAudioContent, WireCompletedToolCall, WireFileContent, WireImageContent, WireMessage, WireMessageContent, WireRunState, WireRunStep, WireToolMessage, WireToolOutcome, WireToolResult, WireUserMessage, base64ToBytes, bytesToBase64, fromJsonSafeContentParts, fromJsonSafeMessage, fromJsonSafeRunState, toJsonSafeContentParts, toJsonSafeMessage, toJsonSafeRunState } from "./binary-json.js";
2
3
  import { assertNever } from "./assert-never.js";
3
4
  import { AsyncContext, createAsyncContext } from "./async-context.js";
4
5
  import { md5, xxhash } from "./hash.js";
5
6
  import { collect, filter, mapStream, merge, take, takeWhile, withSignal } from "./streams.js";
6
- export { type AsyncContext, type ValidationResult, type ZodLikeError, type ZodLikeSafeParseResult, type ZodLikeSchema, assertNever, collect, createAsyncContext, filter, mapStream, md5, merge, take, takeWhile, validate, validateOrThrow, withSignal, xxhash };
7
+ export { type AsyncContext, type EncodedBinary, type EncodedBytes, type EncodedUrl, type ValidationResult, type WireAssistantMessage, type WireAudioContent, type WireCompletedToolCall, type WireFileContent, type WireImageContent, type WireMessage, type WireMessageContent, type WireRunState, type WireRunStep, type WireToolMessage, type WireToolOutcome, type WireToolResult, type WireUserMessage, type ZodLikeError, type ZodLikeSafeParseResult, type ZodLikeSchema, assertNever, base64ToBytes, bytesToBase64, collect, createAsyncContext, filter, fromJsonSafeContentParts, fromJsonSafeMessage, fromJsonSafeRunState, mapStream, md5, merge, take, takeWhile, toJsonSafeContentParts, toJsonSafeMessage, toJsonSafeRunState, validate, validateOrThrow, withSignal, xxhash };
@@ -1,7 +1,8 @@
1
+ import { base64ToBytes, bytesToBase64, fromJsonSafeContentParts, fromJsonSafeMessage, fromJsonSafeRunState, toJsonSafeContentParts, toJsonSafeMessage, toJsonSafeRunState } from "./binary-json.js";
1
2
  import { assertNever } from "./assert-never.js";
2
3
  import { createAsyncContext } from "./async-context.js";
3
4
  import { md5, xxhash } from "./hash.js";
4
5
  import { collect, filter, mapStream, merge, take, takeWhile, withSignal } from "./streams.js";
5
6
  import { validate, validateOrThrow } from "./validation.js";
6
7
 
7
- export { assertNever, collect, createAsyncContext, filter, mapStream, md5, merge, take, takeWhile, validate, validateOrThrow, withSignal, xxhash };
8
+ export { assertNever, base64ToBytes, bytesToBase64, collect, createAsyncContext, filter, fromJsonSafeContentParts, fromJsonSafeMessage, fromJsonSafeRunState, mapStream, md5, merge, take, takeWhile, toJsonSafeContentParts, toJsonSafeMessage, toJsonSafeRunState, validate, validateOrThrow, withSignal, xxhash };
@@ -28,7 +28,16 @@ interface ZodLikeError<_TInput = unknown> {
28
28
  readonly name: string;
29
29
  readonly message: string;
30
30
  readonly issues: ReadonlyArray<{
31
- readonly path: ReadonlyArray<string | number>;
31
+ /**
32
+ * W-013: `PropertyKey`, not `string | number` - zod 4 bases
33
+ * `$ZodIssue.path` on `PropertyKey`, and this shim must be a
34
+ * SUPERSET of both supported peer majors or the canonical
35
+ * `tool({ inputSchema: z.object({...}) })` fails to typecheck for
36
+ * every zod@4 consumer. Type-level widening of a produced position
37
+ * (breaking for downstream code assigning elements to
38
+ * `string | number`).
39
+ */
40
+ readonly path: ReadonlyArray<PropertyKey>;
32
41
  readonly message: string;
33
42
  }>;
34
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","names":[],"sources":["../../src/utils/validation.ts"],"sourcesContent":[],"mappings":";;AASA;;;;;;;;AASY,UATK,aASiB,CAAA,UAAA,OAAA,EAAA,SAAA,OAAA,CAAA,CAAA;EACW,KAAA,CAAA,IAAA,EAAA,OAAA,CAAA,EATrB,OASqB;EACe,SAAA,CAAA,IAAA,EAAA,OAAA,CAAA,EAThC,sBASgC,CATT,OASS,EATA,MASA,CAAA;EAAb;EAAY,SAAA,OAAA,CAAA,EAPtC,OAOsC;EAG1C,SAAA,MAAY,CAAA,EATT,MASS;AAgB7B;AAWA;AAAkD,KAhCtC,sBAgCsC,CAAA,OAAA,EAAA,MAAA,CAAA,GAAA;EAAd,SAAA,OAAA,EAAA,IAAA;EAAmD,SAAA,IAAA,EA/B1C,OA+B0C;CAAjB,GAAA;EAAgB,SAAA,OAAA,EAAA,KAAA;EAatE,SAAA,KAAA,EA3C+B,YA2ChB,CA3C6B,MA2C7B,CAAA;CAA0B;;AAAmC,UAxC3E,YAwC2E,CAAA,UAAA,OAAA,CAAA,CAAA;EAAC,SAAA,IAAA,EAAA,MAAA;;mBArC1E;mBACA;;;;;;;;;;;KAYP;;kBAC6B;;;kBACC;;;;;;;;;iBAS1B,oBAAoB,cAAc,oBAAoB,iBAAiB;;;;;;;;iBAavE,2BAA2B,cAAc,mCAAmC"}
1
+ {"version":3,"file":"validation.d.ts","names":[],"sources":["../../src/utils/validation.ts"],"sourcesContent":[],"mappings":";;AASA;;;;;;;;AASY,UATK,aASiB,CAAA,UAAA,OAAA,EAAA,SAAA,OAAA,CAAA,CAAA;EACW,KAAA,CAAA,IAAA,EAAA,OAAA,CAAA,EATrB,OASqB;EACe,SAAA,CAAA,IAAA,EAAA,OAAA,CAAA,EAThC,sBASgC,CATT,OASS,EATA,MASA,CAAA;EAAb;EAAY,SAAA,OAAA,CAAA,EAPtC,OAOsC;EAG1C,SAAA,MAAY,CAAA,EATT,MASS;;;AAGV,KARP,sBAQO,CAAA,OAAA,EAAA,MAAA,CAAA,GAAA;EAAa,SAAA,OAAA,EAAA,IAAA;EAsBpB,SAAA,IAAA,EA7BiC,OA6BjB;AAW5B,CAAA,GAAgB;EAAkC,SAAA,OAAA,EAAA,KAAA;EAAd,SAAA,KAAA,EAvCW,YAuCX,CAvCwB,MAuCxB,CAAA;CAAmD;;AAAD,UApCrE,YAoCqE,CAAA,UAAA,OAAA,CAAA,CAAA;EAatE,SAAA,IAAA,EAAA,MAAe;EAA0B,SAAA,OAAA,EAAA,MAAA;EAAd,SAAA,MAAA,EA9CxB,aA8CwB,CAAA;IAAiD;;;;;;;;;mBApCzE,cAAc;;;;;;;;;;;KAYrB;;kBAC6B;;;kBACC;;;;;;;;;iBAS1B,oBAAoB,cAAc,oBAAoB,iBAAiB;;;;;;;;iBAavE,2BAA2B,cAAc,mCAAmC"}
@@ -27,7 +27,7 @@ function validate(schema, data) {
27
27
  function validateOrThrow(schema, data, what) {
28
28
  const r = validate(schema, data);
29
29
  if (r.ok) return r.value;
30
- const summary = r.error.issues.map((i) => `${i.path.join(".") || "."}: ${i.message}`).join("; ");
30
+ const summary = r.error.issues.map((i) => `${i.path.map(String).join(".") || "."}: ${i.message}`).join("; ");
31
31
  throw new TypeError(`graphorin: validation failed${what ? ` for ${what}` : ""}: ${summary}`);
32
32
  }
33
33
 
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","names":[],"sources":["../../src/utils/validation.ts"],"sourcesContent":["/**\n * Type-only Zod compatibility shim. We declare a structural type for the\n * subset of `zod` we depend on so that `@graphorin/core` can be type-\n * checked without importing zod directly. Consumers that do `import {\n * z } from 'zod'` get the real types via the user's `zod` install (the\n * peer dependency).\n *\n * @stable\n */\nexport interface ZodLikeSchema<TOutput = unknown, TInput = unknown> {\n parse(data: unknown): TOutput;\n safeParse(data: unknown): ZodLikeSafeParseResult<TOutput, TInput>;\n /** Internal phantom used by Zod for inference. We don't dereference it. */\n readonly _output?: TOutput;\n readonly _input?: TInput;\n}\n\n/** @stable */\nexport type ZodLikeSafeParseResult<TOutput, TInput> =\n | { readonly success: true; readonly data: TOutput }\n | { readonly success: false; readonly error: ZodLikeError<TInput> };\n\n/** @stable */\nexport interface ZodLikeError<_TInput = unknown> {\n readonly name: string;\n readonly message: string;\n readonly issues: ReadonlyArray<{\n readonly path: ReadonlyArray<string | number>;\n readonly message: string;\n }>;\n}\n\n/**\n * Validate `data` against `schema` and return a `Result` instead of\n * throwing. Use this in code paths where you want explicit\n * pattern-matching over success / failure.\n *\n * @stable\n */\nexport type ValidationResult<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: ZodLikeError };\n\n/**\n * Synchronous validation wrapper. Does **not** swallow errors thrown by\n * the schema's transformations - only normalizes the success / failure\n * signal.\n *\n * @stable\n */\nexport function validate<T>(schema: ZodLikeSchema<T>, data: unknown): ValidationResult<T> {\n const result = schema.safeParse(data);\n if (result.success) return { ok: true, value: result.data };\n return { ok: false, error: result.error };\n}\n\n/**\n * Throwing variant of `validate(...)` that surfaces a `TypeError` carrying\n * a stable, parser-style message. Useful at module-boundary entry points\n * where a thrown error is the natural failure mode.\n *\n * @stable\n */\nexport function validateOrThrow<T>(schema: ZodLikeSchema<T>, data: unknown, what?: string): T {\n const r = validate(schema, data);\n if (r.ok) return r.value;\n const summary = r.error.issues.map((i) => `${i.path.join('.') || '.'}: ${i.message}`).join('; ');\n throw new TypeError(`graphorin: validation failed${what ? ` for ${what}` : ''}: ${summary}`);\n}\n"],"mappings":";;;;;;;;AAkDA,SAAgB,SAAY,QAA0B,MAAoC;CACxF,MAAM,SAAS,OAAO,UAAU,KAAK;AACrC,KAAI,OAAO,QAAS,QAAO;EAAE,IAAI;EAAM,OAAO,OAAO;EAAM;AAC3D,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO;EAAO;;;;;;;;;AAU3C,SAAgB,gBAAmB,QAA0B,MAAe,MAAkB;CAC5F,MAAM,IAAI,SAAS,QAAQ,KAAK;AAChC,KAAI,EAAE,GAAI,QAAO,EAAE;CACnB,MAAM,UAAU,EAAE,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK;AAChG,OAAM,IAAI,UAAU,+BAA+B,OAAO,QAAQ,SAAS,GAAG,IAAI,UAAU"}
1
+ {"version":3,"file":"validation.js","names":[],"sources":["../../src/utils/validation.ts"],"sourcesContent":["/**\n * Type-only Zod compatibility shim. We declare a structural type for the\n * subset of `zod` we depend on so that `@graphorin/core` can be type-\n * checked without importing zod directly. Consumers that do `import {\n * z } from 'zod'` get the real types via the user's `zod` install (the\n * peer dependency).\n *\n * @stable\n */\nexport interface ZodLikeSchema<TOutput = unknown, TInput = unknown> {\n parse(data: unknown): TOutput;\n safeParse(data: unknown): ZodLikeSafeParseResult<TOutput, TInput>;\n /** Internal phantom used by Zod for inference. We don't dereference it. */\n readonly _output?: TOutput;\n readonly _input?: TInput;\n}\n\n/** @stable */\nexport type ZodLikeSafeParseResult<TOutput, TInput> =\n | { readonly success: true; readonly data: TOutput }\n | { readonly success: false; readonly error: ZodLikeError<TInput> };\n\n/** @stable */\nexport interface ZodLikeError<_TInput = unknown> {\n readonly name: string;\n readonly message: string;\n readonly issues: ReadonlyArray<{\n /**\n * W-013: `PropertyKey`, not `string | number` - zod 4 bases\n * `$ZodIssue.path` on `PropertyKey`, and this shim must be a\n * SUPERSET of both supported peer majors or the canonical\n * `tool({ inputSchema: z.object({...}) })` fails to typecheck for\n * every zod@4 consumer. Type-level widening of a produced position\n * (breaking for downstream code assigning elements to\n * `string | number`).\n */\n readonly path: ReadonlyArray<PropertyKey>;\n readonly message: string;\n }>;\n}\n\n/**\n * Validate `data` against `schema` and return a `Result` instead of\n * throwing. Use this in code paths where you want explicit\n * pattern-matching over success / failure.\n *\n * @stable\n */\nexport type ValidationResult<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: ZodLikeError };\n\n/**\n * Synchronous validation wrapper. Does **not** swallow errors thrown by\n * the schema's transformations - only normalizes the success / failure\n * signal.\n *\n * @stable\n */\nexport function validate<T>(schema: ZodLikeSchema<T>, data: unknown): ValidationResult<T> {\n const result = schema.safeParse(data);\n if (result.success) return { ok: true, value: result.data };\n return { ok: false, error: result.error };\n}\n\n/**\n * Throwing variant of `validate(...)` that surfaces a `TypeError` carrying\n * a stable, parser-style message. Useful at module-boundary entry points\n * where a thrown error is the natural failure mode.\n *\n * @stable\n */\nexport function validateOrThrow<T>(schema: ZodLikeSchema<T>, data: unknown, what?: string): T {\n const r = validate(schema, data);\n if (r.ok) return r.value;\n // W-013: `Array.prototype.join` THROWS on symbol elements - map to\n // String first (zod 4 paths are PropertyKey-based).\n const summary = r.error.issues\n .map((i) => `${i.path.map(String).join('.') || '.'}: ${i.message}`)\n .join('; ');\n throw new TypeError(`graphorin: validation failed${what ? ` for ${what}` : ''}: ${summary}`);\n}\n"],"mappings":";;;;;;;;AA2DA,SAAgB,SAAY,QAA0B,MAAoC;CACxF,MAAM,SAAS,OAAO,UAAU,KAAK;AACrC,KAAI,OAAO,QAAS,QAAO;EAAE,IAAI;EAAM,OAAO,OAAO;EAAM;AAC3D,QAAO;EAAE,IAAI;EAAO,OAAO,OAAO;EAAO;;;;;;;;;AAU3C,SAAgB,gBAAmB,QAA0B,MAAe,MAAkB;CAC5F,MAAM,IAAI,SAAS,QAAQ,KAAK;AAChC,KAAI,EAAE,GAAI,QAAO,EAAE;CAGnB,MAAM,UAAU,EAAE,MAAM,OACrB,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,IAAI,IAAI,IAAI,EAAE,UAAU,CAClE,KAAK,KAAK;AACb,OAAM,IAAI,UAAU,+BAA+B,OAAO,QAAQ,SAAS,GAAG,IAAI,UAAU"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphorin/core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Core types, contracts, and utilities for the Graphorin framework. Dependency-free root package every other @graphorin/* package depends on.",
5
5
  "license": "MIT",
6
6
  "author": "Oleksiy Stepurenko",
@@ -22,8 +22,9 @@
22
22
  "contracts"
23
23
  ],
24
24
  "type": "module",
25
+ "sideEffects": false,
25
26
  "engines": {
26
- "node": ">=22.0.0"
27
+ "node": ">=22.12.0"
27
28
  },
28
29
  "main": "./dist/index.js",
29
30
  "module": "./dist/index.js",
@@ -31,28 +32,29 @@
31
32
  "exports": {
32
33
  ".": {
33
34
  "types": "./dist/index.d.ts",
34
- "import": "./dist/index.js"
35
+ "default": "./dist/index.js"
35
36
  },
36
37
  "./types": {
37
38
  "types": "./dist/types/index.d.ts",
38
- "import": "./dist/types/index.js"
39
+ "default": "./dist/types/index.js"
39
40
  },
40
41
  "./contracts": {
41
42
  "types": "./dist/contracts/index.d.ts",
42
- "import": "./dist/contracts/index.js"
43
+ "default": "./dist/contracts/index.js"
43
44
  },
44
45
  "./utils": {
45
46
  "types": "./dist/utils/index.d.ts",
46
- "import": "./dist/utils/index.js"
47
+ "default": "./dist/utils/index.js"
47
48
  },
48
49
  "./channels": {
49
50
  "types": "./dist/channels/index.d.ts",
50
- "import": "./dist/channels/index.js"
51
+ "default": "./dist/channels/index.js"
51
52
  },
52
53
  "./package.json": "./package.json"
53
54
  },
54
55
  "files": [
55
56
  "dist",
57
+ "src",
56
58
  "README.md",
57
59
  "CHANGELOG.md",
58
60
  "LICENSE"
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Workflow channel kinds. Every state field declared on a workflow's
3
+ * `stateSchema` is bound to a channel that decides the merge strategy
4
+ * applied when multiple writers update the same field within a single
5
+ * execution step.
6
+ *
7
+ * The names are **Graphorin's own design** and must not be aliased to
8
+ * terms from other workflow libraries. A dedicated lint rule lands later
9
+ * in the release line to enforce this.
10
+ *
11
+ * @stable
12
+ */
13
+ export type ChannelKind =
14
+ | 'latest-value'
15
+ | 'any-value'
16
+ | 'reducer'
17
+ | 'list-aggregate'
18
+ | 'stream'
19
+ | 'barrier'
20
+ | 'ephemeral';
21
+
22
+ /**
23
+ * Discriminated union of every channel descriptor.
24
+ *
25
+ * Channels are a *description* of the merge strategy, not a runtime
26
+ * value: the engine reads the `kind` field plus optional auxiliary
27
+ * fields (`reduce`, `from`, `unique`) to decide how to combine writes.
28
+ *
29
+ * @stable
30
+ */
31
+ export type Channel<T = unknown> =
32
+ | LatestValue<T>
33
+ | AnyValue<T>
34
+ | Reducer<T>
35
+ | ListAggregate<T>
36
+ | Stream<T>
37
+ | Barrier<T>
38
+ | Ephemeral<T>;
39
+
40
+ /**
41
+ * Overwrite-on-write. Multiple writes within the same execution step
42
+ * raise `MultiWriteError` (use `AnyValue` if collisions are acceptable).
43
+ *
44
+ * @stable
45
+ */
46
+ export interface LatestValue<T = unknown> {
47
+ readonly kind: 'latest-value';
48
+ readonly default?: T;
49
+ }
50
+
51
+ /**
52
+ * Overwrite-on-write - collisions are silently allowed (last-write-wins
53
+ * semantics within a step).
54
+ *
55
+ * @stable
56
+ */
57
+ export interface AnyValue<T = unknown> {
58
+ readonly kind: 'any-value';
59
+ readonly default?: T;
60
+ }
61
+
62
+ /**
63
+ * Fold writes via a user-provided `reduce` function. The reducer is
64
+ * invoked left-to-right over the writes collected within an execution
65
+ * step.
66
+ *
67
+ * @stable
68
+ */
69
+ export interface Reducer<T = unknown> {
70
+ readonly kind: 'reducer';
71
+ readonly default?: T;
72
+ readonly reduce: (prev: T, next: T) => T;
73
+ }
74
+
75
+ /**
76
+ * Specialization of `Reducer<T[]>` that appends each write to a list.
77
+ *
78
+ * @stable
79
+ */
80
+ export interface ListAggregate<T = unknown> {
81
+ readonly kind: 'list-aggregate';
82
+ readonly default?: ReadonlyArray<T>;
83
+ }
84
+
85
+ /**
86
+ * Append-only queue. Used for dynamic task creation via `Dispatch(...)`
87
+ * and for application-defined event streams.
88
+ *
89
+ * @stable
90
+ */
91
+ export interface Stream<T = unknown> {
92
+ readonly kind: 'stream';
93
+ readonly unique?: boolean;
94
+ readonly default?: ReadonlyArray<T>;
95
+ }
96
+
97
+ /**
98
+ * Barrier - completes when every writer in `from` has produced a value.
99
+ *
100
+ * @stable
101
+ */
102
+ export interface Barrier<T = unknown> {
103
+ readonly kind: 'barrier';
104
+ readonly from: ReadonlyArray<string>;
105
+ readonly default?: T;
106
+ }
107
+
108
+ /**
109
+ * Value scoped to a single execution step - discarded when the step
110
+ * ends.
111
+ *
112
+ * @stable
113
+ */
114
+ export interface Ephemeral<T = unknown> {
115
+ readonly kind: 'ephemeral';
116
+ readonly default?: T;
117
+ }
118
+
119
+ /**
120
+ * Construct a `LatestValue` channel.
121
+ *
122
+ * @stable
123
+ */
124
+ export function latestValue<T>(opts?: { readonly default?: T }): LatestValue<T> {
125
+ return opts !== undefined && 'default' in opts && opts.default !== undefined
126
+ ? { kind: 'latest-value', default: opts.default }
127
+ : { kind: 'latest-value' };
128
+ }
129
+
130
+ /**
131
+ * Construct an `AnyValue` channel.
132
+ *
133
+ * @stable
134
+ */
135
+ export function anyValue<T>(opts?: { readonly default?: T }): AnyValue<T> {
136
+ return opts !== undefined && 'default' in opts && opts.default !== undefined
137
+ ? { kind: 'any-value', default: opts.default }
138
+ : { kind: 'any-value' };
139
+ }
140
+
141
+ /**
142
+ * Construct a `Reducer` channel.
143
+ *
144
+ * @stable
145
+ */
146
+ export function reducer<T>(
147
+ reduce: (prev: T, next: T) => T,
148
+ opts?: { readonly default?: T },
149
+ ): Reducer<T> {
150
+ return opts !== undefined && 'default' in opts && opts.default !== undefined
151
+ ? { kind: 'reducer', reduce, default: opts.default }
152
+ : { kind: 'reducer', reduce };
153
+ }
154
+
155
+ /**
156
+ * Construct a `ListAggregate` channel.
157
+ *
158
+ * @stable
159
+ */
160
+ export function listAggregate<T>(opts?: { readonly default?: ReadonlyArray<T> }): ListAggregate<T> {
161
+ return opts !== undefined && 'default' in opts && opts.default !== undefined
162
+ ? { kind: 'list-aggregate', default: opts.default }
163
+ : { kind: 'list-aggregate' };
164
+ }
165
+
166
+ /**
167
+ * Construct a `Stream` channel.
168
+ *
169
+ * @stable
170
+ */
171
+ export function stream<T>(opts?: {
172
+ readonly unique?: boolean;
173
+ readonly default?: ReadonlyArray<T>;
174
+ }): Stream<T> {
175
+ const out: { kind: 'stream'; unique?: boolean; default?: ReadonlyArray<T> } = {
176
+ kind: 'stream',
177
+ };
178
+ if (opts?.unique !== undefined) out.unique = opts.unique;
179
+ if (opts?.default !== undefined) out.default = opts.default;
180
+ return out;
181
+ }
182
+
183
+ /**
184
+ * Construct a `Barrier` channel.
185
+ *
186
+ * @stable
187
+ */
188
+ export function barrier<T>(
189
+ from: ReadonlyArray<string>,
190
+ opts?: { readonly default?: T },
191
+ ): Barrier<T> {
192
+ return opts !== undefined && 'default' in opts && opts.default !== undefined
193
+ ? { kind: 'barrier', from, default: opts.default }
194
+ : { kind: 'barrier', from };
195
+ }
196
+
197
+ /**
198
+ * Construct an `Ephemeral` channel.
199
+ *
200
+ * @stable
201
+ */
202
+ export function ephemeral<T>(opts?: { readonly default?: T }): Ephemeral<T> {
203
+ return opts !== undefined && 'default' in opts && opts.default !== undefined
204
+ ? { kind: 'ephemeral', default: opts.default }
205
+ : { kind: 'ephemeral' };
206
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Workflow control-flow primitive: a single value handed to
3
+ * `Workflow.resume(threadId, directive?)` (or returned from a node's
4
+ * `pause(...)` resolution) carrying any combination of:
5
+ *
6
+ * - `goto` - jump to a named node, bypassing the edge graph.
7
+ * **Destructive (workflow-09):** the restored frontier is discarded -
8
+ * surviving dynamic tasks, completed-but-unwalked nodes, and every
9
+ * pending pause record (including already-delivered pause answers)
10
+ * are dropped in favour of the single goto task. Use it as an
11
+ * operator escape hatch, not routine control flow.
12
+ * - `resume` - value supplied to the `pause(value)` call that suspended.
13
+ * - `update` - additional channel writes applied before the next step.
14
+ *
15
+ * The shape is **Graphorin's own design** (the name `Directive` is part
16
+ * of the public API).
17
+ *
18
+ * @stable
19
+ */
20
+ export class Directive<TUpdate = Record<string, unknown>, TResume = unknown> {
21
+ readonly goto?: string;
22
+ readonly resume?: TResume;
23
+ readonly update?: TUpdate;
24
+
25
+ constructor(opts: DirectiveOptions<TUpdate, TResume>) {
26
+ if (opts.goto !== undefined) this.goto = opts.goto;
27
+ if ('resume' in opts && opts.resume !== undefined) this.resume = opts.resume;
28
+ if (opts.update !== undefined) this.update = opts.update;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Constructor parameters for `Directive`.
34
+ *
35
+ * @stable
36
+ */
37
+ export interface DirectiveOptions<TUpdate = Record<string, unknown>, TResume = unknown> {
38
+ readonly goto?: string;
39
+ readonly resume?: TResume;
40
+ readonly update?: TUpdate;
41
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Workflow dynamic-task primitive. A node returns one or more
3
+ * `Dispatch(nodeName, args)` values to schedule additional tasks in the
4
+ * next execution step.
5
+ *
6
+ * The class is intentionally tiny - the engine inspects only the public
7
+ * `nodeName` and `args` fields. The shape is **Graphorin's own design**
8
+ * (the name `Dispatch` is part of the public API).
9
+ *
10
+ * @stable
11
+ */
12
+ export class Dispatch<TArgs = unknown> {
13
+ /**
14
+ * Cross-realm brand (workflow-13): the engine's structural fallback
15
+ * requires this marker so a plain state object that happens to carry
16
+ * `nodeName` + `args` keys is treated as channel WRITES, never
17
+ * silently swallowed as a dispatch. A plain own property (not a
18
+ * symbol) so it survives `structuredClone` across worker boundaries.
19
+ */
20
+ readonly __graphorinDispatch: true = true;
21
+ readonly nodeName: string;
22
+ readonly args: TArgs;
23
+
24
+ constructor(nodeName: string, args: TArgs) {
25
+ this.nodeName = nodeName;
26
+ this.args = args;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Convenience factory equivalent to `new Dispatch(nodeName, args)`.
32
+ *
33
+ * @stable
34
+ */
35
+ export function dispatch<TArgs>(nodeName: string, args: TArgs): Dispatch<TArgs> {
36
+ return new Dispatch(nodeName, args);
37
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Durable workflow primitives (D1) built on the `pause(...)` substrate:
3
+ *
4
+ * - {@link sleepUntil} / {@link sleepFor} - **durable timers**: the node
5
+ * suspends with a persisted wake-at timestamp; `workflow.tick(threadId)`
6
+ * (or any scheduler calling it) resumes the thread once the deadline
7
+ * passes. The timer survives process restarts because it lives in the
8
+ * checkpointed frontier, not in a `setTimeout`.
9
+ * - {@link awaitExternal} - **durable promises (awakeables)**: the node
10
+ * suspends under a caller-chosen name; an external system resolves it
11
+ * later via `workflow.resolveAwakeable(threadId, name, value)` and the
12
+ * node receives `value` as the call's return.
13
+ * - {@link requestApproval} - **first-class persisted approvals**: an
14
+ * awakeable specialized for human sign-off, resolved via
15
+ * `workflow.approve(threadId, name, decision)`.
16
+ *
17
+ * Each helper throws the same branded `PauseSignal` that `pause(...)`
18
+ * throws, carrying a reserved, JSON-safe pause value the engine
19
+ * recognises (`kind: 'graphorin.timer' | 'graphorin.awakeable' |
20
+ * 'graphorin.approval'`) to stamp `wakeAt` / `name` onto the persisted
21
+ * pause record. On resume the body re-executes from the top and the
22
+ * already-delivered values replay in order (WF-2), exactly like plain
23
+ * `pause(...)`.
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+
28
+ import { pause } from './pause.js';
29
+
30
+ /** Reserved pause-value kind for durable timers (D1). */
31
+ export const TIMER_PAUSE_KIND = 'graphorin.timer' as const;
32
+ /** Reserved pause-value kind for awakeables / durable promises (D1). */
33
+ export const AWAKEABLE_PAUSE_KIND = 'graphorin.awakeable' as const;
34
+ /** Reserved pause-value kind for persisted approvals (D1). */
35
+ export const APPROVAL_PAUSE_KIND = 'graphorin.approval' as const;
36
+
37
+ /** Pause value carried by a durable-timer suspension. */
38
+ export interface TimerPauseValue {
39
+ readonly kind: typeof TIMER_PAUSE_KIND;
40
+ /** Epoch milliseconds at which the timer becomes due. */
41
+ readonly wakeAt: number;
42
+ }
43
+
44
+ /** Pause value carried by an awakeable suspension. */
45
+ export interface AwakeablePauseValue {
46
+ readonly kind: typeof AWAKEABLE_PAUSE_KIND;
47
+ /** Caller-chosen name targeted by `workflow.resolveAwakeable(...)`. */
48
+ readonly name: string;
49
+ }
50
+
51
+ /** Pause value carried by a persisted-approval suspension. */
52
+ export interface ApprovalPauseValue {
53
+ readonly kind: typeof APPROVAL_PAUSE_KIND;
54
+ /** Caller-chosen name targeted by `workflow.approve(...)`. */
55
+ readonly name: string;
56
+ /** Free-form payload surfaced to the approver (what is being approved). */
57
+ readonly payload?: unknown;
58
+ }
59
+
60
+ /** Type guard for {@link TimerPauseValue}. @stable */
61
+ export function isTimerPauseValue(value: unknown): value is TimerPauseValue {
62
+ return (
63
+ typeof value === 'object' &&
64
+ value !== null &&
65
+ (value as { kind?: unknown }).kind === TIMER_PAUSE_KIND &&
66
+ typeof (value as { wakeAt?: unknown }).wakeAt === 'number'
67
+ );
68
+ }
69
+
70
+ /** Type guard for {@link AwakeablePauseValue}. @stable */
71
+ export function isAwakeablePauseValue(value: unknown): value is AwakeablePauseValue {
72
+ return (
73
+ typeof value === 'object' &&
74
+ value !== null &&
75
+ (value as { kind?: unknown }).kind === AWAKEABLE_PAUSE_KIND &&
76
+ typeof (value as { name?: unknown }).name === 'string'
77
+ );
78
+ }
79
+
80
+ /** Type guard for {@link ApprovalPauseValue}. @stable */
81
+ export function isApprovalPauseValue(value: unknown): value is ApprovalPauseValue {
82
+ return (
83
+ typeof value === 'object' &&
84
+ value !== null &&
85
+ (value as { kind?: unknown }).kind === APPROVAL_PAUSE_KIND &&
86
+ typeof (value as { name?: unknown }).name === 'string'
87
+ );
88
+ }
89
+
90
+ /**
91
+ * Durably sleep until an absolute instant. Suspends the workflow thread
92
+ * with a persisted wake-at timestamp; `workflow.tick(threadId)` resumes
93
+ * it once due. Returns nothing on resume.
94
+ *
95
+ * @stable
96
+ */
97
+ export function sleepUntil(at: string | number | Date): void {
98
+ const wakeAt = typeof at === 'number' ? at : at instanceof Date ? at.getTime() : Date.parse(at);
99
+ if (!Number.isFinite(wakeAt)) {
100
+ throw new TypeError(
101
+ `[graphorin/core] sleepUntil(...) got an unparseable instant: ${String(at)}`,
102
+ );
103
+ }
104
+ pause<TimerPauseValue, unknown>({ kind: TIMER_PAUSE_KIND, wakeAt });
105
+ }
106
+
107
+ /**
108
+ * Durably sleep for a relative duration (sugar over {@link sleepUntil}).
109
+ *
110
+ * @stable
111
+ */
112
+ export function sleepFor(ms: number): void {
113
+ if (!Number.isFinite(ms) || ms < 0) {
114
+ throw new TypeError(`[graphorin/core] sleepFor(...) needs a non-negative duration, got ${ms}`);
115
+ }
116
+ sleepUntil(Date.now() + ms);
117
+ }
118
+
119
+ /**
120
+ * Suspend on a named durable promise. The thread stays suspended (and
121
+ * survives restarts) until an external caller resolves it via
122
+ * `workflow.resolveAwakeable(threadId, name, value)`; that `value` is
123
+ * returned here.
124
+ *
125
+ * @stable
126
+ */
127
+ export function awaitExternal<TResume = unknown>(name: string): TResume {
128
+ if (typeof name !== 'string' || name.length === 0) {
129
+ throw new TypeError('[graphorin/core] awaitExternal(name) needs a non-empty name');
130
+ }
131
+ return pause<AwakeablePauseValue, TResume>({ kind: AWAKEABLE_PAUSE_KIND, name });
132
+ }
133
+
134
+ /**
135
+ * Suspend on a named persisted approval. Resolved by
136
+ * `workflow.approve(threadId, name, decision)`; the decision is returned
137
+ * here. The optional payload is surfaced on the pending pause record so
138
+ * an approval UI can show what is being approved.
139
+ *
140
+ * @stable
141
+ */
142
+ export function requestApproval<TDecision = unknown>(name: string, payload?: unknown): TDecision {
143
+ if (typeof name !== 'string' || name.length === 0) {
144
+ throw new TypeError('[graphorin/core] requestApproval(name) needs a non-empty name');
145
+ }
146
+ return pause<ApprovalPauseValue, TDecision>({
147
+ kind: APPROVAL_PAUSE_KIND,
148
+ name,
149
+ ...(payload !== undefined ? { payload } : {}),
150
+ });
151
+ }