@dxos/compute 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/AgentService.mjs.map +1 -1
- package/dist/lib/Operation.mjs +41 -14
- package/dist/lib/Operation.mjs.map +1 -1
- package/dist/lib/OperationHandlerSet.mjs +1 -1
- package/dist/lib/Process.mjs +34 -2
- package/dist/lib/Process.mjs.map +1 -1
- package/dist/lib/ShellService.mjs +24 -0
- package/dist/lib/ShellService.mjs.map +1 -0
- package/dist/lib/chunk-SqlService.mjs +2 -2
- package/dist/lib/errors.mjs +7 -1
- package/dist/lib/errors.mjs.map +1 -1
- package/dist/lib/index.mjs +2 -1
- package/dist/lib/types/Project.mjs +8 -0
- package/dist/lib/types/Project.mjs.map +1 -1
- package/dist/lib/types/Skill.mjs +7 -1
- package/dist/lib/types/Skill.mjs.map +1 -1
- package/dist/lib/types/Trigger.mjs +6 -5
- package/dist/lib/types/Trigger.mjs.map +1 -1
- package/dist/types/src/AgentService.d.ts +2 -1
- package/dist/types/src/AgentService.d.ts.map +1 -1
- package/dist/types/src/Operation.d.ts +64 -11
- package/dist/types/src/Operation.d.ts.map +1 -1
- package/dist/types/src/Process.d.ts +53 -31
- package/dist/types/src/Process.d.ts.map +1 -1
- package/dist/types/src/ShellService.d.ts +85 -0
- package/dist/types/src/ShellService.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +31 -0
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/types/Project.d.ts +2 -0
- package/dist/types/src/types/Project.d.ts.map +1 -1
- package/dist/types/src/types/Skill.d.ts +8 -0
- package/dist/types/src/types/Skill.d.ts.map +1 -1
- package/dist/types/src/types/Trigger.d.ts +11 -9
- package/dist/types/src/types/Trigger.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -15
- package/src/AgentService.ts +2 -1
- package/src/DurableOperation.tst.ts +155 -0
- package/src/Operation.ts +121 -46
- package/src/Process.ts +99 -41
- package/src/ShellService.ts +71 -0
- package/src/errors.ts +7 -0
- package/src/index.ts +1 -0
- package/src/types/Project.ts +11 -0
- package/src/types/Skill.ts +9 -0
- package/src/types/Trigger.ts +9 -7
package/dist/lib/Process.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Process.mjs","names":[],"sources":["../../src/Process.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport type * as Exit from 'effect/Exit';\nimport * as Option from 'effect/Option';\nimport type * as Atom from 'effect/reactivity/Atom';\nimport type * as Rpc from 'effect/rpc/Rpc';\nimport type * as RpcClient from 'effect/rpc/RpcClient';\nimport * as Schema from 'effect/Schema';\nimport type * as Stream from 'effect/Stream';\n\nimport { Annotation, type Database } from '@dxos/echo';\nimport { type SpaceId, URI } from '@dxos/keys';\nimport type { SerializedError } from '@dxos/protocols';\n\nimport * as Operation from './Operation.ts';\nimport * as Trace from './Trace.ts';\n\nexport { RUN_AGAIN_ERROR_CODE, RUN_AGAIN_MESSAGE, RunAgainError } from './errors.ts';\n\n//\n// Process.\n//\n\n/** Opaque process id (arbitrary string). */\nexport const ID = Schema.String.pipe(Schema.brand('ProcessId'));\nexport type ID = Schema.Schema.Type<typeof ID>;\n\n/**\n * Generic parameters for a all processes.\n */\nexport interface Params {\n /**\n * Process name for debugging purposes.\n */\n readonly name: string | null;\n\n /**\n * User-defined annotations for the process.\n * Can only be set when the process is spawned.\n */\n readonly annotations: Annotation.Dictionary;\n}\n\n/**\n * What the process is running on behalf of, fixed at spawn and inherited by child processes.\n *\n * Determines which services the runtime can provide (a space-scoped database, a conversation's harness).\n */\nexport interface Environment {\n /** Space the process is scoped to; absent for app-level work. */\n readonly space?: SpaceId;\n\n /** URI of the conversation feed (queue) the process is serving; absent outside a conversation. */\n readonly conversation?: URI.URI;\n}\n\n/**\n * The environment the running process was spawned with, for a handler that hands its work to another\n * runtime (e.g. EDGE dispatching an operation to its operation worker), which must carry the conversation along.\n */\nexport class EnvironmentService extends Context.Service<EnvironmentService, Environment>()(\n '@dxos/compute/Process.EnvironmentService',\n) {}\n\n/** The running process's environment; empty outside a process, so a handler can read it wherever it runs. */\nexport const currentEnvironment: Effect.Effect<Environment> = Effect.serviceOption(EnvironmentService).pipe(\n Effect.map(Option.getOrElse((): Environment => ({}))),\n);\n\n/**\n * Attaches the process to a target object.\n */\nexport const TargetAnnotation = Annotation.make({\n id: 'org.dxos.process.target',\n schema: URI.Schema,\n});\n\n/**\n * Notification descriptor for surfacing process lifecycle events to the user.\n */\nexport const NotifyAnnotation = Annotation.make({\n id: 'org.dxos.process.notify',\n schema: Operation.NotifyOptions,\n});\n\n/**\n * Marks a process as the harness host for its conversation (discovery substrate).\n */\nexport const HarnessHostAnnotation = Annotation.make({\n id: 'org.dxos.process.harnessHost',\n schema: Schema.Boolean,\n});\n\n/** Whether `info` is a conversation's agent process (stamped {@link HarnessHostAnnotation} at spawn). */\nexport const isHarnessHost = (info: Pick<Process, 'params'>): boolean =>\n Option.getOrElse(Annotation.getDictionary(info.params.annotations, HarnessHostAnnotation), () => false);\n\n/**\n * Runtime state of a process.\n */\nexport enum State {\n // Command to spawn the process has been accepted locally but the runtime hosting it has not yet\n // acknowledged it. Only ever reported by a client queueing commands for a remote runtime\n // (`RemoteCommandQueue`); a process the local runtime owns is never in this state.\n STARTING = 'STARTING',\n\n // Process is actively running.\n RUNNING = 'RUNNING',\n\n // Process is waiting for a child process to complete or an alarm to trigger.\n HYBERNATING = 'HYBERNATING',\n\n // Process is waiting for input. It will only resume when input is submitted.\n IDLE = 'IDLE',\n\n // Process is terminating and will transition to TERMINATED state.\n // TODO(dmaretskyi): Consider removing.\n TERMINATING = 'TERMINATING',\n\n // Process has been externally terminated.\n TERMINATED = 'TERMINATED',\n\n // Process has completed successfully.\n SUCCEEDED = 'SUCCEEDED',\n\n // Process has failed.\n FAILED = 'FAILED',\n}\n\n/**\n * Whether a process in `state` has exited: SUCCEEDED, FAILED or TERMINATED. A runtime never moves a\n * process out of these states, which is what makes them final for a host.\n */\nexport const isExited = (state: State): boolean =>\n state === State.SUCCEEDED || state === State.FAILED || state === State.TERMINATED;\n\n/**\n * Whether a process in `state` will never run another turn: {@link isExited}, or TERMINATING.\n * TERMINATING counts because the handle is already finished, so a caller adopting it would submit\n * input that is dropped and wait on a process that will never run again.\n */\nexport const isTerminal = (state: State): boolean => isExited(state) || state === State.TERMINATING;\n\n/** Filters for {@link Manager.list}; an absent field matches everything. */\nexport interface Filter {\n readonly key?: string;\n /** Target object the process was spawned against ({@link TargetAnnotation}). */\n readonly target?: URI.URI;\n readonly state?: State;\n /** Space from the process's {@link Environment}; a process with no space matches no space filter. */\n readonly space?: SpaceId;\n readonly parentPid?: ID | null;\n}\n\n/**\n * Whether `info` satisfies `filter`. Exported so every {@link Manager} filters identically rather\n * than each implementation growing its own notion of a match.\n */\nexport const matchesFilter = (info: Process, filter: Filter = {}): boolean => {\n if (filter.key !== undefined && info.key !== filter.key) {\n return false;\n }\n if (filter.state !== undefined && info.state !== filter.state) {\n return false;\n }\n if (filter.space !== undefined && info.environment.space !== filter.space) {\n return false;\n }\n if (filter.parentPid !== undefined && info.parentPid !== filter.parentPid) {\n return false;\n }\n if (\n filter.target !== undefined &&\n Option.getOrUndefined(Annotation.getDictionary(info.params.annotations, TargetAnnotation)) !== filter.target\n ) {\n return false;\n }\n return true;\n};\n\n/** {@link Manager.list} over a tree read, so a manager implements it by supplying only that read. */\nexport const listFromTree =\n (processTree: Effect.Effect<readonly Process[]>) =>\n (filter?: Filter): Effect.Effect<readonly Process[]> =>\n Effect.map(processTree, (tree) => tree.filter((info) => matchesFilter(info, filter)));\n\n/**\n * A process: one running (or finished) instance of a durable operation, wherever its runtime runs.\n */\nexport interface Process {\n readonly pid: ID;\n readonly parentPid: ID | null;\n\n /**\n * Key of the process.\n *\n * NOTE: There might be multiple running processes with the same key.\n */\n readonly key: string;\n\n /**\n * Parameters of the process.\n */\n readonly params: Params;\n\n /**\n * What the process is running on behalf of. See {@link Environment}.\n */\n readonly environment: Environment;\n\n /**\n * State of the process.\n */\n readonly state: State;\n\n /**\n * How the process failed as a serializable {@link SerializedError} (its `context` carries any\n * structured detail, e.g. a notify override), or `null` unless it is in FAILED state.\n */\n readonly error: SerializedError | null;\n\n /**\n * UNIX timestamp in milliseconds.\n */\n readonly startedAt: number;\n\n /**\n * UNIX timestamp in milliseconds.\n */\n readonly completedAt: Option.Option<number>;\n\n readonly metrics: {\n /**\n * Total wall time of all handler invocations of the process in milliseconds.\n */\n readonly wallTime: number;\n\n /**\n * Total number of inputs submitted to the process.\n */\n readonly inputCount: number;\n\n /**\n * Total number of outputs submitted to the process.\n */\n readonly outputCount: number;\n };\n}\n\n//\n// Handles.\n//\n\nexport interface Status {\n readonly state: State;\n readonly exit: Option.Option<Exit.Exit<void>>;\n\n readonly startedAt: Date;\n readonly completedAt: Option.Option<Date>;\n}\n\nexport interface Handle<_Input, _Output, _Rpcs extends Rpc.Any> {\n readonly pid: ID;\n readonly parentId: ID | null;\n\n /**\n * Process definition key ({@link Operation.Durable.key}) for this process.\n */\n readonly key: string;\n\n /**\n * Parameters of the process.\n */\n readonly params: Params;\n\n /**\n * What the process is running on behalf of. See {@link Environment}.\n */\n readonly environment: Environment;\n\n submitInput(input: _Input): Effect.Effect<void>;\n subscribeOutputs(): Stream.Stream<_Output>;\n\n /**\n * Subscribe to ephemeral trace messages for this process.\n * Replays buffered events, then streams new ones as they arrive.\n * The stream completes when the process reaches a terminal state.\n *\n * When consuming this stream from a short-lived parent effect (e.g. React\n * `useEffect` that `runPromise(Effect.forEach(subscribe))` and returns), fork\n * the collector with {@link Effect.forkDetach}, not {@link Effect.forkChild} — the\n * parent scope closes as soon as `forEach` finishes and interrupts scoped forks\n * before live `pushEphemeral` events arrive. Interrupt the daemon fiber explicitly\n * on dispose (see {@link ProcessOperationInvoker.fiberFromProcess}).\n */\n subscribeEphemeral(): Stream.Stream<Trace.Message>;\n\n terminate(): Effect.Effect<void>;\n readonly status: Status;\n\n /**\n * Absolute due-time (epoch ms) of the process's pending alarm, or `null` when none is scheduled.\n * A host that suspends the process between turns (a Durable Object) mirrors this onto its own\n * scheduler, since the runtime's alarm is an in-memory timer.\n */\n readonly alarmDueAt: number | null;\n statusAtom: Atom.Atom<Status>;\n\n /**\n * Resolves when the process reaches {@link State.IDLE} (nothing in-flight; waiting for input),\n * or a terminal state ({@link State.SUCCEEDED}, {@link State.TERMINATED}, {@link State.FAILED}).\n *\n * Does not resolve while the process is {@link State.HYBERNATING} (e.g. alarm pending or non-terminal child).\n * The effect keeps waiting until that external work finishes and the process becomes idle or terminal.\n *\n * If the process fails, this effect throws a defect.\n */\n runToCompletion(): Effect.Effect<void>;\n\n /**\n * Resolves when the process settles its current foreground turn: {@link State.IDLE} or\n * {@link State.SUCCEEDED}, or {@link State.HYBERNATING} with no pending alarm\n * (i.e. only background children remain in flight).\n *\n * Unlike {@link runToCompletion}, this does NOT wait for background children (e.g. delegated\n * sub-agents) to finish — so a supervisor's chat turn returns as soon as its reply is complete,\n * while sub-agents continue running and report back out of band. Still waits through\n * alarm-pending hybernation (more queued turn work). Defects on {@link State.FAILED}.\n */\n runUntilSettled(): Effect.Effect<void>;\n\n /**\n * Submits each input in order, then streams outputs until the process reaches {@link State.IDLE}\n * or {@link State.SUCCEEDED}. While {@link State.HYBERNATING}, keeps waiting for outputs\n * or a terminal state. The stream fails with a defect if the process reaches {@link State.FAILED}\n * or {@link State.TERMINATED}.\n */\n runAndExit(options: { readonly inputs: readonly _Input[] }): Stream.Stream<_Output>;\n\n /**\n * Hydrates a dormant persisted process using the supplied definition.\n * No-op when the handle is already live (returns self).\n */\n hydrate(definition: Operation.Durable<_Input, _Output, any, any>): Effect.Effect<Handle<_Input, _Output, _Rpcs>>;\n\n readonly rpc: RpcClient.RpcClient<_Rpcs>;\n}\n\nexport namespace Handle {\n // Widened to `any` Rpcs so the implemented `rpc: RpcClient<any>` is assignable\n // regardless of a handle's concrete RPC group (variance, see design spec §4.4).\n export type Any = Handle<any, any, any>;\n}\n\n/**\n * Options for spawning a process.\n */\nexport interface SpawnOptions {\n /** Parent process ID — child inherits the parent's trace context. */\n readonly parentProcessId?: ID;\n\n /**\n * Process name for debugging purposes.\n */\n readonly name?: string;\n\n /**\n * Target object that this process is assigned to.\n * Ergonomic shorthand folded into {@link TargetAnnotation} on the process annotations.\n */\n // TODO(dmaretskyi): Consider opaques metadata instead of opinionated `target` field.\n readonly target?: URI.URI;\n\n /**\n * Tracing metadata for this invocation.\n */\n readonly traceMeta?: Trace.Meta;\n\n readonly environment?: Environment;\n\n /**\n * Who the process's database writes are attributed to (see `Database.Origin`); also the origin of every process it\n * invokes. Persisted with the process, so a restored process keeps it.\n */\n readonly origin?: Database.Origin;\n\n /**\n * User-facing notifications requested for this process's lifecycle phases.\n * Ergonomic shorthand folded into {@link NotifyAnnotation} on the process annotations.\n */\n readonly notify?: Operation.NotifyOptions;\n\n /**\n * User-defined annotations to attach to the process.\n * Caller-supplied entries are merged over the {@link target}/{@link notify} shorthands.\n */\n readonly annotations?: Annotation.Dictionary;\n}\n\nexport interface ListOptions {\n /**\n * Filter processes by process definition key.\n */\n readonly key?: string;\n\n /**\n * Filter processes by parent process ID.\n */\n readonly parentProcessId?: ID;\n\n /**\n * Filter processes by state.\n */\n readonly state?: State;\n\n /**\n * Filter processes by target object ID.\n */\n readonly target?: URI.URI;\n}\n\n//\n// Manager.\n//\n\n/** Where a process runs: this runtime, or the remote (EDGE) runtime hosting `space`. */\nexport type Location = { readonly kind: 'local' } | { readonly kind: 'edge'; readonly space: SpaceId };\n\nexport interface LocationOptions {\n /** Defaults to local. */\n readonly location?: Location;\n}\n\n/**\n * Every process this client can see or run, local and remote, behind one surface.\n *\n * The reads span both runtimes, so they answer \"is my process running, wherever it runs\" — which is\n * what a UI renders and what a caller holding no handle can ask. The control verbs take a\n * {@link Location}: a remote process is not a local one, so the caller still names where it runs,\n * but no longer has to hold both managers to do so.\n */\nexport interface Manager {\n /** Current state of the process tree. */\n readonly processTree: Effect.Effect<readonly Process[]>;\n\n /** Atom for the process tree. */\n readonly processTreeAtom: Atom.Atom<readonly Process[]>;\n\n /**\n * The process tree narrowed by {@link Filter}. A filter naming a space re-reads that space from\n * the remote runtime first, so it is the authoritative read where the atom is the cheap one.\n */\n list(filter?: Filter): Effect.Effect<readonly Process[]>;\n\n /**\n * Stream ephemeral trace messages matching `filter` (DX-1125), sourced from local in-process\n * runtimes and remote runtimes broadcasting over the space swarm. Used to drive live progress UI.\n */\n subscribeToTraceMessages(filter: Trace.Filter): Stream.Stream<Trace.Message>;\n\n /**\n * Spawn a process at `options.location`. A remote host is sent only the definition's key; the\n * definition stays local, supplying the codecs and RPC group the handle is typed by.\n *\n * Dies when the location is a remote runtime that offers no process control.\n */\n spawn<I, O, Rpcs extends Rpc.Any = never>(\n definition: Operation.Durable<I, O, any, Rpcs>,\n options?: SpawnOptions & LocationOptions,\n ): Effect.Effect<Handle<I, O, Rpcs>>;\n\n /**\n * Handles on the processes at `options.location` matching `options`. Dormant entries require\n * {@link Handle.hydrate} before inputs can be submitted.\n *\n * Dies when the location is a remote runtime that offers no process control.\n */\n handles(options?: ListOptions & LocationOptions): Effect.Effect<readonly Handle.Any[]>;\n}\n\nexport class ManagerService extends Context.Service<ManagerService, Manager>()(\n '@dxos/compute/Process.ManagerService',\n) {}\n\n/**\n * New process is spawned.\n */\nexport const SpawnedEvent = Trace.EventType('process.spawned', {\n schema: Schema.Void,\n isEphemeral: false,\n});\n\n/**\n * Process has reached a terminal state.\n */\nexport const ExitedEvent = Trace.EventType('process.exited', {\n schema: Schema.Struct({\n outcome: Schema.Literals(['succeeded', 'failed', 'terminated']),\n }),\n isEphemeral: false,\n});\n\n/**\n * Renders spawned processes as a forest: top-level rows use \"- \", nested rows use ├── / └── / │.\n */\nexport const prettyProcessTree = (tree: readonly Process[]): string => {\n if (tree.length === 0) {\n return '';\n }\n\n const pidSet = new Set(tree.map((node) => node.pid));\n const childrenByParent = new Map<string, Process[]>();\n const roots: Process[] = [];\n\n for (const node of tree) {\n const parent = node.parentPid;\n if (parent === null || !pidSet.has(parent)) {\n roots.push(node);\n continue;\n }\n const key = String(parent);\n const siblings = childrenByParent.get(key) ?? [];\n siblings.push(node);\n childrenByParent.set(key, siblings);\n }\n\n const byPid = (a: Process, b: Process) => String(a.pid).localeCompare(String(b.pid));\n roots.sort(byPid);\n for (const siblings of childrenByParent.values()) {\n siblings.sort(byPid);\n }\n\n const formatLabel = (node: Process): string => {\n const idShort = String(node.pid).slice(0, 6);\n const parts = [idShort, node.state];\n if (node.params.name != null && node.params.name !== '') {\n parts.push(node.params.name);\n }\n if (node.error != null) {\n parts.push(`(${node.error.message ?? node.error.name ?? 'error'})`);\n }\n const { inputCount, outputCount, wallTime } = node.metrics;\n parts.push(`[in:${inputCount} out:${outputCount} wall:${Math.round(wallTime)}ms]`);\n return parts.join(' ');\n };\n\n const lines: string[] = [];\n\n const walk = (node: Process, prefix: string, isLast: boolean, isRoot: boolean): void => {\n if (isRoot) {\n lines.push(`- ${formatLabel(node)}`);\n } else {\n const branch = isLast ? '└── ' : '├── ';\n lines.push(`${prefix}${branch}${formatLabel(node)}`);\n }\n\n const children = childrenByParent.get(String(node.pid)) ?? [];\n const nextPrefix = isRoot ? ' ' : `${prefix}${isLast ? ' ' : '│ '}`;\n children.forEach((child, index) => {\n walk(child, nextPrefix, index === children.length - 1, false);\n });\n };\n\n for (const root of roots) {\n walk(root, '', true, true);\n }\n\n return lines.join('\\n');\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,KAAK,OAAO,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;;;;;AAoC9D,IAAa,qBAAb,cAAwC,QAAQ,QAAyC,CAAC,CACxF,0CACF,CAAC,CAAC,CAAC;;AAGH,IAAa,qBAAiD,OAAO,cAAc,kBAAkB,CAAC,CAAC,KACrG,OAAO,IAAI,OAAO,iBAA8B,CAAC,EAAE,CAAC,CACtD;;;;AAKA,IAAa,mBAAmB,WAAW,KAAK;CAC9C,IAAI;CACJ,QAAQ,MAAI;AACd,CAAC;;;;AAKD,IAAa,mBAAmB,WAAW,KAAK;CAC9C,IAAI;CACJ,QAAQ;AACV,CAAC;;;;AAKD,IAAa,wBAAwB,WAAW,KAAK;CACnD,IAAI;CACJ,QAAQ,OAAO;AACjB,CAAC;;AAGD,IAAa,iBAAiB,SAC5B,OAAO,UAAU,WAAW,cAAc,KAAK,OAAO,aAAa,qBAAqB,SAAS,KAAK;;;;AAKxG,IAAY,QAAL,yBAAA,OAAA;CAIL,MAAA,cAAA;CAGA,MAAA,aAAA;CAGA,MAAA,iBAAA;CAGA,MAAA,UAAA;CAIA,MAAA,iBAAA;CAGA,MAAA,gBAAA;CAGA,MAAA,eAAA;CAGA,MAAA,YAAA;;AACF,EAAA,CAAA,CAAA;;;;;AAMA,IAAa,YAAY,UACvB,UAAA,eAA6B,UAAA,YAA0B,UAAA;;;;;;AAOzD,IAAa,cAAc,UAA0B,SAAS,KAAK,KAAK,UAAA;;;;;AAiBxE,IAAa,iBAAiB,MAAe,SAAiB,CAAC,MAAe;CAC5E,IAAI,OAAO,QAAQ,KAAA,KAAa,KAAK,QAAQ,OAAO,KAClD,OAAO;CAET,IAAI,OAAO,UAAU,KAAA,KAAa,KAAK,UAAU,OAAO,OACtD,OAAO;CAET,IAAI,OAAO,UAAU,KAAA,KAAa,KAAK,YAAY,UAAU,OAAO,OAClE,OAAO;CAET,IAAI,OAAO,cAAc,KAAA,KAAa,KAAK,cAAc,OAAO,WAC9D,OAAO;CAET,IACE,OAAO,WAAW,KAAA,KAClB,OAAO,eAAe,WAAW,cAAc,KAAK,OAAO,aAAa,gBAAgB,CAAC,MAAM,OAAO,QAEtG,OAAO;CAET,OAAO;AACT;;AAGA,IAAa,gBACV,iBACA,WACC,OAAO,IAAI,cAAc,SAAS,KAAK,QAAQ,SAAS,cAAc,MAAM,MAAM,CAAC,CAAC;AAwSxF,IAAa,iBAAb,cAAoC,QAAQ,QAAiC,CAAC,CAC5E,sCACF,CAAC,CAAC,CAAC;;;;AAKH,IAAa,eAAe,UAAgB,mBAAmB;CAC7D,QAAQ,OAAO;CACf,aAAa;AACf,CAAC;;;;AAKD,IAAa,cAAc,UAAgB,kBAAkB;CAC3D,QAAQ,OAAO,OAAO,EACpB,SAAS,OAAO,SAAS;EAAC;EAAa;EAAU;CAAY,CAAC,EAChE,CAAC;CACD,aAAa;AACf,CAAC;;;;AAKD,IAAa,qBAAqB,SAAqC;CACrE,IAAI,KAAK,WAAW,GAClB,OAAO;CAGT,MAAM,SAAS,IAAI,IAAI,KAAK,KAAK,SAAS,KAAK,GAAG,CAAC;CACnD,MAAM,mCAAmB,IAAI,IAAuB;CACpD,MAAM,QAAmB,CAAC;CAE1B,KAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,SAAS,KAAK;EACpB,IAAI,WAAW,QAAQ,CAAC,OAAO,IAAI,MAAM,GAAG;GAC1C,MAAM,KAAK,IAAI;GACf;EACF;EACA,MAAM,MAAM,OAAO,MAAM;EACzB,MAAM,WAAW,iBAAiB,IAAI,GAAG,KAAK,CAAC;EAC/C,SAAS,KAAK,IAAI;EAClB,iBAAiB,IAAI,KAAK,QAAQ;CACpC;CAEA,MAAM,SAAS,GAAY,MAAe,OAAO,EAAE,GAAG,CAAC,CAAC,cAAc,OAAO,EAAE,GAAG,CAAC;CACnF,MAAM,KAAK,KAAK;CAChB,KAAK,MAAM,YAAY,iBAAiB,OAAO,GAC7C,SAAS,KAAK,KAAK;CAGrB,MAAM,eAAe,SAA0B;EAE7C,MAAM,QAAQ,CADE,OAAO,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,CAC3B,GAAS,KAAK,KAAK;EAClC,IAAI,KAAK,OAAO,QAAQ,QAAQ,KAAK,OAAO,SAAS,IACnD,MAAM,KAAK,KAAK,OAAO,IAAI;EAE7B,IAAI,KAAK,SAAS,MAChB,MAAM,KAAK,IAAI,KAAK,MAAM,WAAW,KAAK,MAAM,QAAQ,QAAQ,EAAE;EAEpE,MAAM,EAAE,YAAY,aAAa,aAAa,KAAK;EACnD,MAAM,KAAK,OAAO,WAAW,OAAO,YAAY,QAAQ,KAAK,MAAM,QAAQ,EAAE,IAAI;EACjF,OAAO,MAAM,KAAK,GAAG;CACvB;CAEA,MAAM,QAAkB,CAAC;CAEzB,MAAM,QAAQ,MAAe,QAAgB,QAAiB,WAA0B;EACtF,IAAI,QACF,MAAM,KAAK,KAAK,YAAY,IAAI,GAAG;OAC9B;GACL,MAAM,SAAS,SAAS,SAAS;GACjC,MAAM,KAAK,GAAG,SAAS,SAAS,YAAY,IAAI,GAAG;EACrD;EAEA,MAAM,WAAW,iBAAiB,IAAI,OAAO,KAAK,GAAG,CAAC,KAAK,CAAC;EAC5D,MAAM,aAAa,SAAS,OAAO,GAAG,SAAS,SAAS,SAAS;EACjE,SAAS,SAAS,OAAO,UAAU;GACjC,KAAK,OAAO,YAAY,UAAU,SAAS,SAAS,GAAG,KAAK;EAC9D,CAAC;CACH;CAEA,KAAK,MAAM,QAAQ,OACjB,KAAK,MAAM,IAAI,MAAM,IAAI;CAG3B,OAAO,MAAM,KAAK,IAAI;AACxB"}
|
|
1
|
+
{"version":3,"file":"Process.mjs","names":[],"sources":["../../src/Process.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Cause from 'effect/Cause';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport * as Option from 'effect/Option';\nimport type * as Atom from 'effect/reactivity/Atom';\nimport type * as Rpc from 'effect/rpc/Rpc';\nimport type * as RpcClient from 'effect/rpc/RpcClient';\nimport * as Schema from 'effect/Schema';\nimport * as Stream from 'effect/Stream';\n\nimport { Annotation, type Database } from '@dxos/echo';\nimport { type SpaceId, URI } from '@dxos/keys';\nimport type { SerializedError } from '@dxos/protocols';\n\nimport * as Operation from './Operation.ts';\nimport * as Trace from './Trace.ts';\n\nexport { RUN_AGAIN_ERROR_CODE, RUN_AGAIN_MESSAGE, RunAgainError } from './errors.ts';\n\n//\n// Process.\n//\n\n/** Opaque process id (arbitrary string). */\nexport const ID = Schema.String.pipe(Schema.brand('ProcessId'));\nexport type ID = Schema.Schema.Type<typeof ID>;\n\n/**\n * Generic parameters for a all processes.\n */\nexport interface Params {\n /**\n * Process name for debugging purposes.\n */\n readonly name: string | null;\n\n /**\n * User-defined annotations for the process.\n * Can only be set when the process is spawned.\n */\n readonly annotations: Annotation.Dictionary;\n}\n\n/**\n * What the process is running on behalf of, fixed at spawn and inherited by child processes.\n *\n * Determines which services the runtime can provide (a space-scoped database, a conversation's harness).\n */\nexport interface Environment {\n /** Space the process is scoped to; absent for app-level work. */\n readonly space?: SpaceId;\n\n /** URI of the conversation feed (queue) the process is serving; absent outside a conversation. */\n readonly conversation?: URI.URI;\n}\n\n/**\n * The environment the running process was spawned with, for a handler that hands its work to another\n * runtime (e.g. EDGE dispatching an operation to its operation worker), which must carry the conversation along.\n */\nexport class EnvironmentService extends Context.Service<EnvironmentService, Environment>()(\n '@dxos/compute/Process.EnvironmentService',\n) {}\n\n/** The running process's environment; empty outside a process, so a handler can read it wherever it runs. */\nexport const currentEnvironment: Effect.Effect<Environment> = Effect.serviceOption(EnvironmentService).pipe(\n Effect.map(Option.getOrElse((): Environment => ({}))),\n);\n\n/**\n * Attaches the process to a target object.\n */\nexport const TargetAnnotation = Annotation.make({\n id: 'org.dxos.process.target',\n schema: URI.Schema,\n});\n\n/**\n * Notification descriptor for surfacing process lifecycle events to the user.\n */\nexport const NotifyAnnotation = Annotation.make({\n id: 'org.dxos.process.notify',\n schema: Operation.NotifyOptions,\n});\n\n/**\n * Marks a process as the harness host for its conversation (discovery substrate).\n */\nexport const HarnessHostAnnotation = Annotation.make({\n id: 'org.dxos.process.harnessHost',\n schema: Schema.Boolean,\n});\n\n/** Whether `info` is a conversation's agent process (stamped {@link HarnessHostAnnotation} at spawn). */\nexport const isHarnessHost = (info: Pick<Process, 'params'>): boolean =>\n Option.getOrElse(Annotation.getDictionary(info.params.annotations, HarnessHostAnnotation), () => false);\n\n/**\n * Runtime state of a process.\n */\nexport enum State {\n // Command to spawn the process has been accepted locally but the runtime hosting it has not yet\n // acknowledged it. Only ever reported by a client queueing commands for a remote runtime\n // (`RemoteCommandQueue`); a process the local runtime owns is never in this state.\n STARTING = 'STARTING',\n\n // Process is actively running.\n RUNNING = 'RUNNING',\n\n // Process is waiting for a child process to complete or an alarm to trigger.\n HYBERNATING = 'HYBERNATING',\n\n // Process is waiting for input. It will only resume when input is submitted.\n IDLE = 'IDLE',\n\n // Process is terminating and will transition to TERMINATED state.\n // TODO(dmaretskyi): Consider removing.\n TERMINATING = 'TERMINATING',\n\n // Process has been externally terminated.\n TERMINATED = 'TERMINATED',\n\n // Process has completed successfully.\n SUCCEEDED = 'SUCCEEDED',\n\n // Process has failed.\n FAILED = 'FAILED',\n}\n\n/**\n * Whether a process in `state` has exited: SUCCEEDED, FAILED or TERMINATED. A runtime never moves a\n * process out of these states, which is what makes them final for a host.\n */\nexport const isExited = (state: State): boolean =>\n state === State.SUCCEEDED || state === State.FAILED || state === State.TERMINATED;\n\n/**\n * Whether a process in `state` will never run another turn: {@link isExited}, or TERMINATING.\n * TERMINATING counts because the handle is already finished, so a caller adopting it would submit\n * input that is dropped and wait on a process that will never run again.\n */\nexport const isTerminal = (state: State): boolean => isExited(state) || state === State.TERMINATING;\n\n/** Filters for {@link Manager.list}; an absent field matches everything. */\nexport interface Filter {\n readonly key?: string;\n /** Target object the process was spawned against ({@link TargetAnnotation}). */\n readonly target?: URI.URI;\n readonly state?: State;\n /** Space from the process's {@link Environment}; a process with no space matches no space filter. */\n readonly space?: SpaceId;\n readonly parentPid?: ID | null;\n}\n\n/**\n * Whether `info` satisfies `filter`. Exported so every {@link Manager} filters identically rather\n * than each implementation growing its own notion of a match.\n */\nexport const matchesFilter = (info: Data, filter: Filter = {}): boolean => {\n if (filter.key !== undefined && info.key !== filter.key) {\n return false;\n }\n if (filter.state !== undefined && info.state !== filter.state) {\n return false;\n }\n if (filter.space !== undefined && info.environment.space !== filter.space) {\n return false;\n }\n if (filter.parentPid !== undefined && info.parentPid !== filter.parentPid) {\n return false;\n }\n if (\n filter.target !== undefined &&\n Option.getOrUndefined(Annotation.getDictionary(info.params.annotations, TargetAnnotation)) !== filter.target\n ) {\n return false;\n }\n return true;\n};\n\n/** {@link Manager.list} over a tree read, so a manager implements it by supplying only that read. */\nexport const listFromTree =\n (processTree: Effect.Effect<readonly Process[]>) =>\n (filter?: Filter): Effect.Effect<readonly Process[]> =>\n Effect.map(processTree, (tree) => tree.filter((info) => matchesFilter(info, filter)));\n\nexport interface SubscribeEphemeralOptions {\n /**\n * Replay the buffered events before streaming new ones (the default), so a reader attaching mid-turn\n * sees the turn so far. A reader that subscribes BEFORE causing the work it wants to follow passes\n * `false`: the buffer then holds only earlier turns, which would read as the new one.\n */\n readonly replay?: boolean;\n}\n\n/**\n * The plain fields of a {@link Process}: what a process tree holds, what crosses the wire, and what\n * {@link make} turns back into a process.\n */\nexport interface Data {\n readonly pid: ID;\n readonly parentPid: ID | null;\n\n /**\n * Key of the process.\n *\n * NOTE: There might be multiple running processes with the same key.\n */\n readonly key: string;\n\n /**\n * Parameters of the process.\n */\n readonly params: Params;\n\n /**\n * What the process is running on behalf of. See {@link Environment}.\n */\n readonly environment: Environment;\n\n /**\n * State of the process.\n */\n readonly state: State;\n\n /**\n * How the process failed as a serializable {@link SerializedError} (its `context` carries any\n * structured detail, e.g. a notify override), or `null` unless it is in FAILED state.\n */\n readonly error: SerializedError | null;\n\n /**\n * UNIX timestamp in milliseconds.\n */\n readonly startedAt: number;\n\n /**\n * UNIX timestamp in milliseconds.\n */\n readonly completedAt: Option.Option<number>;\n\n readonly metrics: {\n /**\n * Total wall time of all handler invocations of the process in milliseconds.\n */\n readonly wallTime: number;\n\n /**\n * Total number of inputs submitted to the process.\n */\n readonly inputCount: number;\n\n /**\n * Total number of outputs submitted to the process.\n */\n readonly outputCount: number;\n };\n}\n\nexport interface Status {\n readonly state: State;\n readonly exit: Option.Option<Exit.Exit<void>>;\n\n readonly startedAt: Date;\n readonly completedAt: Option.Option<Date>;\n}\n\n/**\n * A process: one running (or finished) instance of a durable operation, wherever its runtime runs.\n *\n * Its {@link Data} fields are plain values; the rest are live members that talk to the runtime.\n * A process read from a tree or list is a cheap snapshot that reaches its runtime only once a\n * live member is used (see {@link make}).\n */\nexport interface Process<_Input = any, _Output = any, _Rpcs extends Rpc.Any = any> extends Data {\n submitInput(input: _Input): Effect.Effect<void>;\n subscribeOutputs(): Stream.Stream<_Output>;\n\n /**\n * Subscribe to ephemeral trace messages for this process.\n * Replays buffered events, then streams new ones as they arrive.\n * The stream completes when the process reaches a terminal state.\n *\n * When consuming this stream from a short-lived parent effect (e.g. React\n * `useEffect` that `runPromise(Effect.forEach(subscribe))` and returns), fork\n * the collector with {@link Effect.forkDetach}, not {@link Effect.forkChild} — the\n * parent scope closes as soon as `forEach` finishes and interrupts scoped forks\n * before live `pushEphemeral` events arrive. Interrupt the daemon fiber explicitly\n * on dispose.\n */\n subscribeEphemeral(options?: SubscribeEphemeralOptions): Stream.Stream<Trace.Message>;\n\n terminate(): Effect.Effect<void>;\n readonly status: Status;\n\n /**\n * Absolute due-time (epoch ms) of the process's pending alarm, or `null` when none is scheduled.\n * A host that suspends the process between turns (a Durable Object) mirrors this onto its own\n * scheduler, since the runtime's alarm is an in-memory timer.\n */\n readonly alarmDueAt: number | null;\n statusAtom: Atom.Atom<Status>;\n\n /**\n * Resolves when the process reaches {@link State.IDLE} (nothing in-flight; waiting for input),\n * or a terminal state ({@link State.SUCCEEDED}, {@link State.TERMINATED}, {@link State.FAILED}).\n *\n * Does not resolve while the process is {@link State.HYBERNATING} (e.g. alarm pending or non-terminal child).\n * The effect keeps waiting until that external work finishes and the process becomes idle or terminal.\n *\n * If the process fails, this effect throws a defect.\n */\n runToCompletion(): Effect.Effect<void>;\n\n /**\n * Resolves when the process settles its current foreground turn: {@link State.IDLE} or\n * {@link State.SUCCEEDED}, or {@link State.HYBERNATING} with no pending alarm\n * (i.e. only background children remain in flight).\n *\n * Unlike {@link runToCompletion}, this does NOT wait for background children (e.g. delegated\n * sub-agents) to finish — so a supervisor's chat turn returns as soon as its reply is complete,\n * while sub-agents continue running and report back out of band. Still waits through\n * alarm-pending hybernation (more queued turn work). Defects on {@link State.FAILED}.\n */\n runUntilSettled(): Effect.Effect<void>;\n\n /**\n * Submits each input in order, then streams outputs until the process reaches {@link State.IDLE}\n * or {@link State.SUCCEEDED}. While {@link State.HYBERNATING}, keeps waiting for outputs\n * or a terminal state. The stream fails with a defect if the process reaches {@link State.FAILED}\n * or {@link State.TERMINATED}.\n */\n runAndExit(options: { readonly inputs: readonly _Input[] }): Stream.Stream<_Output>;\n\n /**\n * Hydrates a dormant persisted process using the supplied definition.\n * No-op when the handle is already live (returns self).\n */\n hydrate(definition: Operation.Durable<_Input, _Output, any, any>): Effect.Effect<Process<_Input, _Output, _Rpcs>>;\n\n readonly rpc: RpcClient.RpcClient<_Rpcs>;\n}\n\n// `any` Rpcs keeps every process assignable regardless of its concrete RPC group (variance, see design spec §4.4).\nexport type Any = Process<any, any, any>;\n\n/**\n * Options for spawning a process.\n */\nexport interface SpawnOptions {\n /**\n * Parent process ID — child inherits the parent's trace context.\n * Inside a process, its {@link ManagerService} defaults this to that process; pass `undefined`\n * explicitly to spawn a detached process.\n */\n readonly parentProcessId?: ID;\n\n /**\n * Process name for debugging purposes.\n */\n readonly name?: string;\n\n /**\n * Target object that this process is assigned to.\n * Ergonomic shorthand folded into {@link TargetAnnotation} on the process annotations.\n */\n // TODO(dmaretskyi): Consider opaques metadata instead of opinionated `target` field.\n readonly target?: URI.URI;\n\n /**\n * Tracing metadata for this invocation.\n */\n readonly traceMeta?: Trace.Meta;\n\n readonly environment?: Environment;\n\n /**\n * Who the process's database writes are attributed to (see `Database.Origin`); also the origin of every process it\n * invokes. Persisted with the process, so a restored process keeps it.\n */\n readonly origin?: Database.Origin;\n\n /**\n * User-facing notifications requested for this process's lifecycle phases.\n * Ergonomic shorthand folded into {@link NotifyAnnotation} on the process annotations.\n */\n readonly notify?: Operation.NotifyOptions;\n\n /**\n * User-defined annotations to attach to the process.\n * Caller-supplied entries are merged over the {@link target}/{@link notify} shorthands.\n */\n readonly annotations?: Annotation.Dictionary;\n}\n\nexport interface ListOptions {\n /**\n * Filter processes by process definition key.\n */\n readonly key?: string;\n\n /**\n * Filter processes by parent process ID.\n */\n readonly parentProcessId?: ID;\n\n /**\n * Filter processes by state.\n */\n readonly state?: State;\n\n /**\n * Filter processes by target object ID.\n */\n readonly target?: URI.URI;\n}\n\n//\n// Manager.\n//\n\n/** Where a process runs: this runtime, or the remote (EDGE) runtime hosting `space`. */\nexport type Location = { readonly kind: 'local' } | { readonly kind: 'edge'; readonly space: SpaceId };\n\nexport interface LocationOptions {\n /** Defaults to local. */\n readonly location?: Location;\n}\n\n/**\n * Every process this client can see or run, local and remote, behind one surface.\n *\n * The reads span both runtimes, so they answer \"is my process running, wherever it runs\" — which is\n * what a UI renders and what a caller holding no handle can ask. The control verbs take a\n * {@link Location}: a remote process is not a local one, so the caller still names where it runs,\n * but no longer has to hold both managers to do so.\n */\nexport interface Manager {\n /** Current state of the process tree. */\n readonly processTree: Effect.Effect<readonly Process[]>;\n\n /** Atom for the process tree. */\n readonly processTreeAtom: Atom.Atom<readonly Process[]>;\n\n /**\n * The process tree narrowed by {@link Filter}. A filter naming a space re-reads that space from\n * the remote runtime first, so it is the authoritative read where the atom is the cheap one.\n */\n list(filter?: Filter): Effect.Effect<readonly Process[]>;\n\n /**\n * Stream ephemeral trace messages matching `filter` (DX-1125), sourced from local in-process\n * runtimes and remote runtimes broadcasting over the space swarm. Used to drive live progress UI.\n */\n subscribeToTraceMessages(filter: Trace.Filter): Stream.Stream<Trace.Message>;\n\n /**\n * Spawn a process at `options.location`. A remote host is sent only the definition's key; the\n * definition stays local, supplying the codecs and RPC group the handle is typed by.\n *\n * Dies when the location is a remote runtime that offers no process control.\n */\n spawn<I, O, Rpcs extends Rpc.Any = never>(\n definition: Operation.Durable<I, O, any, Rpcs>,\n options?: SpawnOptions & LocationOptions,\n ): Effect.Effect<Process<I, O, Rpcs>>;\n\n /**\n * Live processes at `options.location` matching `options`. Dormant entries require\n * {@link Process.hydrate} before inputs can be submitted.\n *\n * Dies when the location is a remote runtime that offers no process control.\n */\n handles(options?: ListOptions & LocationOptions): Effect.Effect<readonly Any[]>;\n\n /**\n * The live process `pid` at `options.location`. A process that has already exited\n * replays its outputs, so its result stays readable after the exit.\n *\n * Dies when no such process is known, or when the location is a remote runtime that offers no\n * process control.\n */\n attach<I, O, Rpcs extends Rpc.Any = never>(pid: ID, options?: LocationOptions): Effect.Effect<Process<I, O, Rpcs>>;\n}\n\n/**\n * The first output of a single-output process (e.g. one running an operation), failing with the\n * process's own cause when it fails without one.\n */\nexport const awaitOutput = <O>(handle: Process<any, O, any>): Effect.Effect<O> =>\n handle.subscribeOutputs().pipe(\n Stream.runHead,\n Effect.flatMap(\n Option.match({\n onSome: Effect.succeed,\n onNone: () => {\n switch (handle.status.state) {\n case State.FAILED:\n return Effect.failCause(\n handle.status.exit.pipe(\n Option.flatMap(Exit.getCause),\n Option.getOrElse(() => Cause.die('Operation failed with unknown error')),\n ),\n );\n case State.TERMINATED:\n return Effect.die('Operation was terminated');\n case State.SUCCEEDED:\n return Effect.die('Process produced no output');\n default:\n // Outputs close on a live process only when its manager suspends it (app shutdown): the\n // wait was cut short rather than answered.\n return Effect.interrupt;\n }\n },\n }),\n ),\n );\n\nexport class ManagerService extends Context.Service<ManagerService, Manager>()(\n '@dxos/compute/Process.ManagerService',\n) {}\n\n/**\n * Spawns `durable` through the ambient {@link ManagerService} and submits `input`; read its result with\n * {@link awaitOutput}.\n */\nexport const spawn = <I, O, Rpcs extends Rpc.Any = never>(\n durable: Operation.Durable<I, O, any, Rpcs>,\n input: I,\n options?: SpawnOptions & LocationOptions,\n): Effect.Effect<Process<I, O, Rpcs>, never, ManagerService> =>\n Effect.gen(function* () {\n const manager = yield* ManagerService;\n const handle = yield* manager.spawn(durable, {\n ...(durable.name !== undefined ? { name: durable.name } : {}),\n ...options,\n });\n yield* handle.submitInput(input);\n return handle;\n });\n\n/**\n * New process is spawned.\n */\nexport const SpawnedEvent = Trace.EventType('process.spawned', {\n schema: Schema.Void,\n isEphemeral: false,\n});\n\n/**\n * Process has reached a terminal state.\n */\nexport const ExitedEvent = Trace.EventType('process.exited', {\n schema: Schema.Struct({\n outcome: Schema.Literals(['succeeded', 'failed', 'terminated']),\n }),\n isEphemeral: false,\n});\n\n/**\n * Renders spawned processes as a forest: top-level rows use \"- \", nested rows use ├── / └── / │.\n */\nexport const prettyProcessTree = (tree: readonly Process[]): string => {\n if (tree.length === 0) {\n return '';\n }\n\n const pidSet = new Set(tree.map((node) => node.pid));\n const childrenByParent = new Map<string, Process[]>();\n const roots: Process[] = [];\n\n for (const node of tree) {\n const parent = node.parentPid;\n if (parent === null || !pidSet.has(parent)) {\n roots.push(node);\n continue;\n }\n const key = String(parent);\n const siblings = childrenByParent.get(key) ?? [];\n siblings.push(node);\n childrenByParent.set(key, siblings);\n }\n\n const byPid = (a: Process, b: Process) => String(a.pid).localeCompare(String(b.pid));\n roots.sort(byPid);\n for (const siblings of childrenByParent.values()) {\n siblings.sort(byPid);\n }\n\n const formatLabel = (node: Process): string => {\n const idShort = String(node.pid).slice(0, 6);\n const parts = [idShort, node.state];\n if (node.params.name != null && node.params.name !== '') {\n parts.push(node.params.name);\n }\n if (node.error != null) {\n parts.push(`(${node.error.message ?? node.error.name ?? 'error'})`);\n }\n const { inputCount, outputCount, wallTime } = node.metrics;\n parts.push(`[in:${inputCount} out:${outputCount} wall:${Math.round(wallTime)}ms]`);\n return parts.join(' ');\n };\n\n const lines: string[] = [];\n\n const walk = (node: Process, prefix: string, isLast: boolean, isRoot: boolean): void => {\n if (isRoot) {\n lines.push(`- ${formatLabel(node)}`);\n } else {\n const branch = isLast ? '└── ' : '├── ';\n lines.push(`${prefix}${branch}${formatLabel(node)}`);\n }\n\n const children = childrenByParent.get(String(node.pid)) ?? [];\n const nextPrefix = isRoot ? ' ' : `${prefix}${isLast ? ' ' : '│ '}`;\n children.forEach((child, index) => {\n walk(child, nextPrefix, index === children.length - 1, false);\n });\n };\n\n for (const root of roots) {\n walk(root, '', true, true);\n }\n\n return lines.join('\\n');\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
|
+
import { BaseError } from "@dxos/errors";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
//#region src/ShellService.ts
|
|
5
|
+
var ShellService_exports = /* @__PURE__ */ __exportAll({
|
|
6
|
+
ShellError: () => ShellError,
|
|
7
|
+
ShellService: () => ShellService,
|
|
8
|
+
exec: () => exec,
|
|
9
|
+
spawn: () => spawn
|
|
10
|
+
});
|
|
11
|
+
/** Raised when an operating-system process cannot be started. */
|
|
12
|
+
var ShellError = class extends BaseError.extend("ShellError", "Shell command failed to start") {};
|
|
13
|
+
/**
|
|
14
|
+
* Runs operating-system processes and bash commands for a compute process. Each compute process gets
|
|
15
|
+
* its own instance (process affinity), so nothing it started outlives it. Only a host that can start
|
|
16
|
+
* one provides it (the desktop app, the vite dev server, a CLI); a process that requires it runs nowhere else.
|
|
17
|
+
*/
|
|
18
|
+
var ShellService = class extends Context.Service()("@dxos/compute/ShellService") {};
|
|
19
|
+
var spawn = (options) => ShellService.use((service) => service.spawn(options));
|
|
20
|
+
var exec = (options) => ShellService.use((service) => service.exec(options));
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ShellError, ShellService, exec, spawn, ShellService_exports as t };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=ShellService.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellService.mjs","names":[],"sources":["../../src/ShellService.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport type * as Scope from 'effect/Scope';\n\nimport { BaseError } from '@dxos/errors';\n\n/** Raised when an operating-system process cannot be started. */\nexport class ShellError extends BaseError.extend('ShellError', 'Shell command failed to start') {}\n\nexport type SpawnOptions = {\n /** Executable to run, resolved against `PATH` when not a path. */\n readonly command: string;\n readonly args?: readonly string[];\n /** Working directory; the host's own when unset. */\n readonly cwd?: string;\n /** Added to the host's environment, overriding what it already has. */\n readonly env?: Readonly<Record<string, string>>;\n};\n\nexport type ExecOptions = Omit<SpawnOptions, 'command' | 'args'> & {\n /** A bash script, run as `bash -c <script>`. */\n readonly script: string;\n};\n\nexport type ExecResult = {\n /** `null` when a signal ended the command. */\n readonly exitCode: number | null;\n readonly stdout: string;\n readonly stderr: string;\n};\n\n/** A running operating-system process and its standard streams. */\nexport interface Child {\n readonly pid: number | undefined;\n readonly stdin: WritableStream<Uint8Array>;\n readonly stdout: ReadableStream<Uint8Array>;\n readonly stderr: ReadableStream<Uint8Array>;\n /** Completes with the exit code once the process exits; `null` when a signal ended it. */\n readonly exited: Effect.Effect<number | null>;\n /** Ends the process; a process that already exited is left as it is. */\n readonly kill: Effect.Effect<void>;\n}\n\nexport interface Service {\n /**\n * Starts `options.command` as an operating-system process. It is killed when the scope closes or\n * when the compute process that owns this service ends, whichever comes first.\n */\n spawn(options: SpawnOptions): Effect.Effect<Child, ShellError, Scope.Scope>;\n /** Runs a bash script to completion and collects its output. */\n exec(options: ExecOptions): Effect.Effect<ExecResult, ShellError>;\n}\n\n/**\n * Runs operating-system processes and bash commands for a compute process. Each compute process gets\n * its own instance (process affinity), so nothing it started outlives it. Only a host that can start\n * one provides it (the desktop app, the vite dev server, a CLI); a process that requires it runs nowhere else.\n */\nexport class ShellService extends Context.Service<ShellService, Service>()('@dxos/compute/ShellService') {}\n\nexport const spawn = (options: SpawnOptions): Effect.Effect<Child, ShellError, ShellService | Scope.Scope> =>\n ShellService.use((service) => service.spawn(options));\n\nexport const exec = (options: ExecOptions): Effect.Effect<ExecResult, ShellError, ShellService> =>\n ShellService.use((service) => service.exec(options));\n"],"mappings":";;;;;;;;;;;AAaA,IAAa,aAAb,cAAgC,UAAU,OAAO,cAAc,+BAA+B,CAAC,CAAC,CAAC;;;;;;AAmDjG,IAAa,eAAb,cAAkC,QAAQ,QAA+B,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC;AAE1G,IAAa,SAAS,YACpB,aAAa,KAAK,YAAY,QAAQ,MAAM,OAAO,CAAC;AAEtD,IAAa,QAAQ,YACnB,aAAa,KAAK,YAAY,QAAQ,KAAK,OAAO,CAAC"}
|
|
@@ -4,13 +4,13 @@ import * as Effect from "effect/Effect";
|
|
|
4
4
|
import * as Cause from "effect/Cause";
|
|
5
5
|
import * as Context from "effect/Context";
|
|
6
6
|
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Exit from "effect/Exit";
|
|
8
|
+
import * as Stream from "effect/Stream";
|
|
7
9
|
import * as Reactivity from "effect/reactivity/Reactivity";
|
|
8
10
|
import * as SqlClient from "effect/sql/SqlClient";
|
|
9
11
|
import * as SqlError from "effect/sql/SqlError";
|
|
10
12
|
import * as Statement from "effect/sql/Statement";
|
|
11
|
-
import * as Stream from "effect/Stream";
|
|
12
13
|
import * as Deferred from "effect/Deferred";
|
|
13
|
-
import * as Exit from "effect/Exit";
|
|
14
14
|
import * as Queue from "effect/Queue";
|
|
15
15
|
import * as Migrator from "effect/sql/Migrator";
|
|
16
16
|
//#region src/migrations/sql-service/0001_init.sql?raw
|
package/dist/lib/errors.mjs
CHANGED
|
@@ -46,6 +46,12 @@ var FunctionError = class extends BaseError.extend("FunctionError", "Function in
|
|
|
46
46
|
var InvalidOperationInputError = class extends BaseError.extend("InvalidOperationInput", "Operation input did not match schema") {};
|
|
47
47
|
var InvalidOperationOutputError = class extends BaseError.extend("InvalidOperationOutput", "Operation output did not match schema") {};
|
|
48
48
|
var TriggerStateNotFoundError = class extends BaseError.extend("TriggerStateNotFound", "Trigger state not found") {};
|
|
49
|
+
/** A manual run was asked of a trigger that is switched off; nothing was dispatched. */
|
|
50
|
+
var TriggerDisabledError = class extends BaseError.extend("TriggerDisabledError", "Trigger is disabled") {
|
|
51
|
+
constructor(triggerId) {
|
|
52
|
+
super({ context: { triggerId } });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
49
55
|
/**
|
|
50
56
|
* Raised when the upstream AI gateway responds with a structured JSON error envelope
|
|
51
57
|
* (`{ "type": "error", "error": { "type": ..., "message": ... } }`). Surfaces as a typed
|
|
@@ -60,6 +66,6 @@ var FunctionsAiUpstreamError = class extends BaseError.extend("FunctionsAiUpstre
|
|
|
60
66
|
*/
|
|
61
67
|
var FunctionsAiMemoizationMissError = class extends BaseError.extend("FunctionsAiMemoizationMissError", "No memoized AI conversation found for the given cache key") {};
|
|
62
68
|
//#endregion
|
|
63
|
-
export { FunctionError, FunctionNotFoundError, FunctionsAiMemoizationMissError, FunctionsAiUpstreamError, InvalidOperationInputError, InvalidOperationOutputError, InvokerNotInitializedError, NoHandlerError, RUN_AGAIN_ERROR_CODE, RUN_AGAIN_MESSAGE, RunAgainError, ServiceNotAvailableError, TriggerStateNotFoundError };
|
|
69
|
+
export { FunctionError, FunctionNotFoundError, FunctionsAiMemoizationMissError, FunctionsAiUpstreamError, InvalidOperationInputError, InvalidOperationOutputError, InvokerNotInitializedError, NoHandlerError, RUN_AGAIN_ERROR_CODE, RUN_AGAIN_MESSAGE, RunAgainError, ServiceNotAvailableError, TriggerDisabledError, TriggerStateNotFoundError };
|
|
64
70
|
|
|
65
71
|
//# sourceMappingURL=errors.mjs.map
|
package/dist/lib/errors.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/errors.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { BaseError, type BaseErrorOptions } from '@dxos/errors';\n\n/**\n * Stable code (error name) for {@link RunAgainError}. Exported so trace consumers can recognize a\n * run-again yield from a persisted `operation.end` event via its `errorCode` field, without matching\n * on the human-readable message.\n */\nexport const RUN_AGAIN_ERROR_CODE = 'RunAgainError';\n\n/**\n * Message carried by {@link RunAgainError}. Exported for backward compatibility: `operation.end`\n * events persisted before {@link RUN_AGAIN_ERROR_CODE} existed carry only this message, so trace\n * consumers fall back to it when `errorCode` is absent.\n */\nexport const RUN_AGAIN_MESSAGE = 'Run again';\n\n/**\n * Raised when a function wants to yield to the scheduler to not exceed platform limits.\n * The scheduler will re-run the function after the yield.\n * It is assumed that the function can be retried with the same input.\n * The function might have produced side-effects on the first run.\n */\nexport class RunAgainError extends BaseError.extend(RUN_AGAIN_ERROR_CODE, RUN_AGAIN_MESSAGE) {}\n\n// Errors from @dxos/operation.\n\nexport class InvokerNotInitializedError extends BaseError.extend(\n 'InvokerNotInitializedError',\n 'Invoker not initialized',\n) {}\n\nexport class NoHandlerError extends BaseError.extend('NoHandlerError', 'No handler found for operation. ') {\n constructor(operationKey: string) {\n super({ context: { operationKey } });\n }\n}\n\n// Errors from @dxos/functions.\n\nexport class ServiceNotAvailableError extends BaseError.extend('ServiceNotAvailable', 'Service not available') {\n constructor(service: string, options?: Omit<BaseErrorOptions, 'context'>) {\n super({ context: { service }, message: `Service not available: ${service}`, ...options });\n }\n}\n\nexport class FunctionNotFoundError extends BaseError.extend('FunctionNotFound', 'Function not found') {\n constructor(functionKey: string, options?: Omit<BaseErrorOptions, 'context'>) {\n super({ context: { function: functionKey }, ...options });\n }\n}\n\nexport class FunctionError extends BaseError.extend('FunctionError', 'Function invocation error') {}\n\nexport class InvalidOperationInputError extends BaseError.extend(\n 'InvalidOperationInput',\n 'Operation input did not match schema',\n) {}\n\nexport class InvalidOperationOutputError extends BaseError.extend(\n 'InvalidOperationOutput',\n 'Operation output did not match schema',\n) {}\n\nexport class TriggerStateNotFoundError extends BaseError.extend('TriggerStateNotFound', 'Trigger state not found') {}\n\n/**\n * Raised when the upstream AI gateway responds with a structured JSON error envelope\n * (`{ \"type\": \"error\", \"error\": { \"type\": ..., \"message\": ... } }`). Surfaces as a typed\n * defect from `FunctionsAiHttpClient`, bypassing `@effect/ai`'s generic `HttpResponseError`\n * wrapping so callers can match on the error class directly.\n */\nexport class FunctionsAiUpstreamError extends BaseError.extend(\n 'FunctionsAiUpstreamError',\n 'Upstream AI service returned an error',\n) {}\n\n/**\n * Specialized `FunctionsAiUpstreamError` for the memoization layer: the recorded fixture for a\n * given cache key was not found and `DX_UPDATE_MODEL_FIXTURES` is unset. The `cacheKey` is exposed\n * via `context.cacheKey` to make regeneration straightforward.\n */\nexport class FunctionsAiMemoizationMissError extends BaseError.extend(\n 'FunctionsAiMemoizationMissError',\n 'No memoized AI conversation found for the given cache key',\n) {}\n"],"mappings":";;;;;;;AAWA,IAAa,uBAAuB;;;;;;AAOpC,IAAa,oBAAoB;;;;;;;AAQjC,IAAa,gBAAb,cAAmC,UAAU,OAAO,sBAAsB,iBAAiB,CAAC,CAAC,CAAC;AAI9F,IAAa,6BAAb,cAAgD,UAAU,OACxD,8BACA,yBACF,CAAC,CAAC,CAAC;AAEH,IAAa,iBAAb,cAAoC,UAAU,OAAO,kBAAkB,kCAAkC,CAAC,CAAC;CACzG,YAAY,cAAsB;EAChC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;CACrC;AACF;AAIA,IAAa,2BAAb,cAA8C,UAAU,OAAO,uBAAuB,uBAAuB,CAAC,CAAC;CAC7G,YAAY,SAAiB,SAA6C;EACxE,MAAM;GAAE,SAAS,EAAE,QAAQ;GAAG,SAAS,0BAA0B;GAAW,GAAG;EAAQ,CAAC;CAC1F;AACF;AAEA,IAAa,wBAAb,cAA2C,UAAU,OAAO,oBAAoB,oBAAoB,CAAC,CAAC;CACpG,YAAY,aAAqB,SAA6C;EAC5E,MAAM;GAAE,SAAS,EAAE,UAAU,YAAY;GAAG,GAAG;EAAQ,CAAC;CAC1D;AACF;AAEA,IAAa,gBAAb,cAAmC,UAAU,OAAO,iBAAiB,2BAA2B,CAAC,CAAC,CAAC;AAEnG,IAAa,6BAAb,cAAgD,UAAU,OACxD,yBACA,sCACF,CAAC,CAAC,CAAC;AAEH,IAAa,8BAAb,cAAiD,UAAU,OACzD,0BACA,uCACF,CAAC,CAAC,CAAC;AAEH,IAAa,4BAAb,cAA+C,UAAU,OAAO,wBAAwB,yBAAyB,CAAC,CAAC,CAAC;;;;;;;
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/errors.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { BaseError, type BaseErrorOptions } from '@dxos/errors';\n\n/**\n * Stable code (error name) for {@link RunAgainError}. Exported so trace consumers can recognize a\n * run-again yield from a persisted `operation.end` event via its `errorCode` field, without matching\n * on the human-readable message.\n */\nexport const RUN_AGAIN_ERROR_CODE = 'RunAgainError';\n\n/**\n * Message carried by {@link RunAgainError}. Exported for backward compatibility: `operation.end`\n * events persisted before {@link RUN_AGAIN_ERROR_CODE} existed carry only this message, so trace\n * consumers fall back to it when `errorCode` is absent.\n */\nexport const RUN_AGAIN_MESSAGE = 'Run again';\n\n/**\n * Raised when a function wants to yield to the scheduler to not exceed platform limits.\n * The scheduler will re-run the function after the yield.\n * It is assumed that the function can be retried with the same input.\n * The function might have produced side-effects on the first run.\n */\nexport class RunAgainError extends BaseError.extend(RUN_AGAIN_ERROR_CODE, RUN_AGAIN_MESSAGE) {}\n\n// Errors from @dxos/operation.\n\nexport class InvokerNotInitializedError extends BaseError.extend(\n 'InvokerNotInitializedError',\n 'Invoker not initialized',\n) {}\n\nexport class NoHandlerError extends BaseError.extend('NoHandlerError', 'No handler found for operation. ') {\n constructor(operationKey: string) {\n super({ context: { operationKey } });\n }\n}\n\n// Errors from @dxos/functions.\n\nexport class ServiceNotAvailableError extends BaseError.extend('ServiceNotAvailable', 'Service not available') {\n constructor(service: string, options?: Omit<BaseErrorOptions, 'context'>) {\n super({ context: { service }, message: `Service not available: ${service}`, ...options });\n }\n}\n\nexport class FunctionNotFoundError extends BaseError.extend('FunctionNotFound', 'Function not found') {\n constructor(functionKey: string, options?: Omit<BaseErrorOptions, 'context'>) {\n super({ context: { function: functionKey }, ...options });\n }\n}\n\nexport class FunctionError extends BaseError.extend('FunctionError', 'Function invocation error') {}\n\nexport class InvalidOperationInputError extends BaseError.extend(\n 'InvalidOperationInput',\n 'Operation input did not match schema',\n) {}\n\nexport class InvalidOperationOutputError extends BaseError.extend(\n 'InvalidOperationOutput',\n 'Operation output did not match schema',\n) {}\n\nexport class TriggerStateNotFoundError extends BaseError.extend('TriggerStateNotFound', 'Trigger state not found') {}\n\n/** A manual run was asked of a trigger that is switched off; nothing was dispatched. */\nexport class TriggerDisabledError extends BaseError.extend('TriggerDisabledError', 'Trigger is disabled') {\n constructor(triggerId: string) {\n super({ context: { triggerId } });\n }\n}\n\n/**\n * Raised when the upstream AI gateway responds with a structured JSON error envelope\n * (`{ \"type\": \"error\", \"error\": { \"type\": ..., \"message\": ... } }`). Surfaces as a typed\n * defect from `FunctionsAiHttpClient`, bypassing `@effect/ai`'s generic `HttpResponseError`\n * wrapping so callers can match on the error class directly.\n */\nexport class FunctionsAiUpstreamError extends BaseError.extend(\n 'FunctionsAiUpstreamError',\n 'Upstream AI service returned an error',\n) {}\n\n/**\n * Specialized `FunctionsAiUpstreamError` for the memoization layer: the recorded fixture for a\n * given cache key was not found and `DX_UPDATE_MODEL_FIXTURES` is unset. The `cacheKey` is exposed\n * via `context.cacheKey` to make regeneration straightforward.\n */\nexport class FunctionsAiMemoizationMissError extends BaseError.extend(\n 'FunctionsAiMemoizationMissError',\n 'No memoized AI conversation found for the given cache key',\n) {}\n"],"mappings":";;;;;;;AAWA,IAAa,uBAAuB;;;;;;AAOpC,IAAa,oBAAoB;;;;;;;AAQjC,IAAa,gBAAb,cAAmC,UAAU,OAAO,sBAAsB,iBAAiB,CAAC,CAAC,CAAC;AAI9F,IAAa,6BAAb,cAAgD,UAAU,OACxD,8BACA,yBACF,CAAC,CAAC,CAAC;AAEH,IAAa,iBAAb,cAAoC,UAAU,OAAO,kBAAkB,kCAAkC,CAAC,CAAC;CACzG,YAAY,cAAsB;EAChC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;CACrC;AACF;AAIA,IAAa,2BAAb,cAA8C,UAAU,OAAO,uBAAuB,uBAAuB,CAAC,CAAC;CAC7G,YAAY,SAAiB,SAA6C;EACxE,MAAM;GAAE,SAAS,EAAE,QAAQ;GAAG,SAAS,0BAA0B;GAAW,GAAG;EAAQ,CAAC;CAC1F;AACF;AAEA,IAAa,wBAAb,cAA2C,UAAU,OAAO,oBAAoB,oBAAoB,CAAC,CAAC;CACpG,YAAY,aAAqB,SAA6C;EAC5E,MAAM;GAAE,SAAS,EAAE,UAAU,YAAY;GAAG,GAAG;EAAQ,CAAC;CAC1D;AACF;AAEA,IAAa,gBAAb,cAAmC,UAAU,OAAO,iBAAiB,2BAA2B,CAAC,CAAC,CAAC;AAEnG,IAAa,6BAAb,cAAgD,UAAU,OACxD,yBACA,sCACF,CAAC,CAAC,CAAC;AAEH,IAAa,8BAAb,cAAiD,UAAU,OACzD,0BACA,uCACF,CAAC,CAAC,CAAC;AAEH,IAAa,4BAAb,cAA+C,UAAU,OAAO,wBAAwB,yBAAyB,CAAC,CAAC,CAAC;;AAGpH,IAAa,uBAAb,cAA0C,UAAU,OAAO,wBAAwB,qBAAqB,CAAC,CAAC;CACxG,YAAY,WAAmB;EAC7B,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;CAClC;AACF;;;;;;;AAQA,IAAa,2BAAb,cAA8C,UAAU,OACtD,4BACA,uCACF,CAAC,CAAC,CAAC;;;;;;AAOH,IAAa,kCAAb,cAAqD,UAAU,OAC7D,mCACA,2DACF,CAAC,CAAC,CAAC"}
|
package/dist/lib/index.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import { t as OperationRegistry_exports } from "./OperationRegistry.mjs";
|
|
|
21
21
|
import { t as Trace_exports } from "./Trace.mjs";
|
|
22
22
|
import { t as Process_exports } from "./Process.mjs";
|
|
23
23
|
import { t as ServiceResolver_exports } from "./ServiceResolver.mjs";
|
|
24
|
+
import { t as ShellService_exports } from "./ShellService.mjs";
|
|
24
25
|
import { r as SqlService_exports } from "./chunk-SqlService.mjs";
|
|
25
26
|
import { t as StorageService_exports } from "./StorageService.mjs";
|
|
26
|
-
export { AgentIdentity_exports as AgentIdentity, AgentService_exports as AgentService, Cancellation_exports as Cancellation, Credential_exports as Credential, FunctionsAiError_exports as FunctionsAiError, Header_exports as Header, Instructions_exports as Instructions, LayerSpec_exports as LayerSpec, McpServer_exports as McpServer, Operation_exports as Operation, OperationHandlerSet_exports as OperationHandlerSet, OperationRegistry_exports as OperationRegistry, Process_exports as Process, Project_exports as Project, Routine_exports as Routine, Runnable_exports as Runnable, Script_exports as Script, ServiceResolver_exports as ServiceResolver, Skill_exports as Skill, SqlService_exports as SqlService, StorageService_exports as StorageService, Template_exports as Template, Trace_exports as Trace, Trigger_exports as Trigger, TriggerEvent_exports as TriggerEvent };
|
|
27
|
+
export { AgentIdentity_exports as AgentIdentity, AgentService_exports as AgentService, Cancellation_exports as Cancellation, Credential_exports as Credential, FunctionsAiError_exports as FunctionsAiError, Header_exports as Header, Instructions_exports as Instructions, LayerSpec_exports as LayerSpec, McpServer_exports as McpServer, Operation_exports as Operation, OperationHandlerSet_exports as OperationHandlerSet, OperationRegistry_exports as OperationRegistry, Process_exports as Process, Project_exports as Project, Routine_exports as Routine, Runnable_exports as Runnable, Script_exports as Script, ServiceResolver_exports as ServiceResolver, ShellService_exports as ShellService, Skill_exports as Skill, SqlService_exports as SqlService, StorageService_exports as StorageService, Template_exports as Template, Trace_exports as Trace, Trigger_exports as Trigger, TriggerEvent_exports as TriggerEvent };
|
|
@@ -50,6 +50,14 @@ var Project = class extends Type.makeObject(DXN.make("org.dxos.type.project", "0
|
|
|
50
50
|
* tasks are local can still reference the repository its issues are filed against.
|
|
51
51
|
*/
|
|
52
52
|
repo: Schema.optional(Ref.Ref(Repo.Repo).annotate({ title: "Repository" })),
|
|
53
|
+
/**
|
|
54
|
+
* Repositories the project's coding agents work on. A cloud session checks each one out in its
|
|
55
|
+
* sandbox; a session on this computer works in the folder chosen for the project there.
|
|
56
|
+
*/
|
|
57
|
+
repositories: Schema.optional(Schema.Array(Ref.Ref(Repo.Repo)).annotate({
|
|
58
|
+
title: "Repositories",
|
|
59
|
+
description: "Checked out for coding agents that run in the cloud."
|
|
60
|
+
})),
|
|
53
61
|
/** The default configuration (model, ...) for the sessions this project starts; a chat's own overrides it. */
|
|
54
62
|
session: Schema.optional(SessionConfig.SessionConfig.pipe(Annotation.FormInputAnnotation.set(false)))
|
|
55
63
|
}).pipe(Schema.annotate({ title: "Project" }), Annotation.LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Project.mjs","names":[],"sources":["../../../src/types/Project.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Schema from 'effect/Schema';\n\nimport { SessionConfig } from '@dxos/ai';\nimport { Annotation, DXN, Format, Obj, Ref, Type } from '@dxos/echo';\nimport { ArchivableAnnotation } from '@dxos/schema';\nimport { Outline, Repo, TaskSet } from '@dxos/types';\n\nimport * as Instructions from './Instructions.ts';\nimport * as Routine from './Routine.ts';\nimport * as Skill from './Skill.ts';\n\n/** Work-stream lifecycle state; what done means lives on the task set's milestones. */\nexport const ProjectStatus = Schema.Literals(['active', 'paused', 'blocked', 'ended']);\nexport type ProjectStatus = Schema.Schema.Type<typeof ProjectStatus>;\n\n/**\n * A user-facing container for interactive, long-running work: instructions (skills + commands),\n * artifacts, tasks, and AI chat sessions in project context. Successor to `Topic`.\n *\n * Fields are the refs the project owns and orders; everything that merely accumulates around it is\n * a query — chats by the ECHO parent edge, agents via chats.\n */\nexport class Project extends Type.makeObject<Project>(DXN.make('org.dxos.type.project', '0.6.0'))(\n Schema.Struct({\n name: Schema.optional(Schema.String),\n description: Schema.optional(Format.Text),\n\n /** Work-stream lifecycle state. */\n status: Schema.optional(ProjectStatus),\n\n /** Owned agent instructions (created at the plugin layer; parented by `SetParent`). */\n instructions: Schema.optional(\n Ref.Ref(Instructions.Instructions).pipe(Annotation.SetParent.set(), Annotation.FormInlineAnnotation.set(true)),\n ),\n\n /** Artifacts (documents, outliners, tables, ...), in order. */\n artifacts: Schema.Array(Ref.Ref(Obj.Unknown)).pipe(\n Annotation.SetParent.set({ override: false }),\n Annotation.FormInputAnnotation.set(false),\n ),\n\n /** Routines the project owns, in order, parented so they cascade-delete with it. */\n routines: Schema.Array(Ref.Ref(Routine.Routine)).pipe(\n Annotation.SetParent.set(),\n Annotation.FormInputAnnotation.set(false),\n ),\n\n /** Ad hoc markdown checklist — the scratch surface; project chats write into it. */\n outline: Schema.optional(Ref.Ref(Outline.Outline).pipe(Annotation.SetParent.set())),\n\n /** Owned (or adopted synced) task container, holding the project's tasks and milestones. */\n taskSet: Schema.optional(Ref.Ref(TaskSet.TaskSet).pipe(Annotation.SetParent.set())),\n\n /**\n * Source repository this project's work lands in. Independent of `taskSet`: a project that\n * mirrors a repository adopts its synced task set AND names it here, while a project whose\n * tasks are local can still reference the repository its issues are filed against.\n */\n // TODO(burdon): Change to array? Move into taskSet?\n repo: Schema.optional(Ref.Ref(Repo.Repo).annotate({ title: 'Repository' })),\n\n /** The default configuration (model, ...) for the sessions this project starts; a chat's own overrides it. */\n session: Schema.optional(SessionConfig.SessionConfig.pipe(Annotation.FormInputAnnotation.set(false))),\n }).pipe(\n Schema.annotate({ title: 'Project' }),\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--stack--regular', hue: 'amber' }),\n ArchivableAnnotation.set(true),\n // Only the project skill: filing created objects into `artifacts` is what a project-scoped\n // session structurally needs; artifact-type skills are enabled on demand. Plain dotted key, so\n // the type does not depend on the plugin that owns the skill.\n Skill.SkillsAnnotation.set(['org.dxos.skill.project']),\n Annotation.UserType.set(),\n ),\n) {}\n\n/**\n * Factory wrapper around `Obj.make` for {@link Project}.\n *\n * Materializes the owned task set and scratch outline unless the caller supplies them: there is no UI\n * to add either to a project that lacks one, so a project without them has nowhere to put its tasks\n * and nothing to draft in. The parent edges follow from the fields' `SetParent` annotation.\n */\nexport const make = (\n props: Omit<Partial<Obj.MakeProps<typeof Project>>, 'artifacts' | 'routines'> & {\n artifacts?: ReadonlyArray<Ref.Ref<Obj.Unknown>>;\n } = {},\n): Project => {\n const project = Obj.make(Project, { ...props, artifacts: props.artifacts ?? [], routines: [] });\n if (!props.taskSet) {\n const taskSet = TaskSet.make();\n Obj.update(project, (project) => {\n project.taskSet = Ref.make(taskSet);\n });\n }\n if (!props.outline) {\n const outline = Outline.make({ name: props.name });\n Obj.update(project, (project) => {\n project.outline = Ref.make(outline);\n });\n }\n return project;\n};\n\n/** Adds a routine to the project as an owned child; `SetParent` on the field cascades it. */\nexport const addRoutine = (project: Project, routine: Routine.Routine): void => {\n Obj.update(project, (project) => {\n project.routines.push(Ref.make(routine));\n });\n};\n\n/** Bindings a chat session should receive when running in a project's context. */\nexport type ContextBindings = {\n skills: Ref.Ref<Skill.Skill>[];\n objects: Ref.Ref<Obj.Unknown>[];\n};\n\n/**\n * Bindings a chat session should receive when running in this project's context: the instructions'\n * skills and context objects. The instructions object itself is NOT bound — its text/commands reach\n * the system prompt through the chat's `instructions` ref, and a bound Instructions object would\n * render as an inert context stub. Requires the instructions ref to be resolved (`.target`);\n * unresolved refs contribute nothing.\n */\nexport const contextBindings = (project: Project): ContextBindings => {\n const instructionsRef = project.instructions;\n // `.target` throws when the ref has neither an inlined target nor a resolver attached\n // (e.g. an unresolved ref outside a database context); guard with `isAvailable` first.\n if (!instructionsRef?.isAvailable) {\n return { skills: [], objects: [] };\n }\n\n const instructions = instructionsRef.target;\n if (!instructions) {\n return { skills: [], objects: [] };\n }\n\n // Refs are kept as-is (never re-wrapped via `Ref.make`): bindings persist to the conversation feed,\n // and re-wrapping would drop the registry DXN of skills that have no space-DB identity.\n return {\n skills: [...instructions.skills],\n objects: [...(instructions.objects ?? [])],\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,IAAa,gBAAgB,OAAO,SAAS;CAAC;CAAU;CAAU;CAAW;AAAO,CAAC;;;;;;;;AAUrF,IAAa,UAAb,cAA6B,KAAK,WAAoB,IAAI,KAAK,yBAAyB,OAAO,CAAC,CAAC,CAC/F,OAAO,OAAO;CACZ,MAAM,OAAO,SAAS,OAAO,MAAM;CACnC,aAAa,OAAO,SAAS,OAAO,IAAI;;CAGxC,QAAQ,OAAO,SAAS,aAAa;;CAGrC,cAAc,OAAO,SACnB,IAAI,IAAI,YAAyB,CAAC,CAAC,KAAK,WAAW,UAAU,IAAI,GAAG,WAAW,qBAAqB,IAAI,IAAI,CAAC,CAC/G;;CAGA,WAAW,OAAO,MAAM,IAAI,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAC5C,WAAW,UAAU,IAAI,EAAE,UAAU,MAAM,CAAC,GAC5C,WAAW,oBAAoB,IAAI,KAAK,CAC1C;;CAGA,UAAU,OAAO,MAAM,IAAI,IAAI,OAAe,CAAC,CAAC,CAAC,KAC/C,WAAW,UAAU,IAAI,GACzB,WAAW,oBAAoB,IAAI,KAAK,CAC1C;;CAGA,SAAS,OAAO,SAAS,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC,KAAK,WAAW,UAAU,IAAI,CAAC,CAAC;;CAGlF,SAAS,OAAO,SAAS,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC,KAAK,WAAW,UAAU,IAAI,CAAC,CAAC;;;;;;CAQlF,MAAM,OAAO,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,OAAO,aAAa,CAAC,CAAC;;
|
|
1
|
+
{"version":3,"file":"Project.mjs","names":[],"sources":["../../../src/types/Project.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Schema from 'effect/Schema';\n\nimport { SessionConfig } from '@dxos/ai';\nimport { Annotation, DXN, Format, Obj, Ref, Type } from '@dxos/echo';\nimport { ArchivableAnnotation } from '@dxos/schema';\nimport { Outline, Repo, TaskSet } from '@dxos/types';\n\nimport * as Instructions from './Instructions.ts';\nimport * as Routine from './Routine.ts';\nimport * as Skill from './Skill.ts';\n\n/** Work-stream lifecycle state; what done means lives on the task set's milestones. */\nexport const ProjectStatus = Schema.Literals(['active', 'paused', 'blocked', 'ended']);\nexport type ProjectStatus = Schema.Schema.Type<typeof ProjectStatus>;\n\n/**\n * A user-facing container for interactive, long-running work: instructions (skills + commands),\n * artifacts, tasks, and AI chat sessions in project context. Successor to `Topic`.\n *\n * Fields are the refs the project owns and orders; everything that merely accumulates around it is\n * a query — chats by the ECHO parent edge, agents via chats.\n */\nexport class Project extends Type.makeObject<Project>(DXN.make('org.dxos.type.project', '0.6.0'))(\n Schema.Struct({\n name: Schema.optional(Schema.String),\n description: Schema.optional(Format.Text),\n\n /** Work-stream lifecycle state. */\n status: Schema.optional(ProjectStatus),\n\n /** Owned agent instructions (created at the plugin layer; parented by `SetParent`). */\n instructions: Schema.optional(\n Ref.Ref(Instructions.Instructions).pipe(Annotation.SetParent.set(), Annotation.FormInlineAnnotation.set(true)),\n ),\n\n /** Artifacts (documents, outliners, tables, ...), in order. */\n artifacts: Schema.Array(Ref.Ref(Obj.Unknown)).pipe(\n Annotation.SetParent.set({ override: false }),\n Annotation.FormInputAnnotation.set(false),\n ),\n\n /** Routines the project owns, in order, parented so they cascade-delete with it. */\n routines: Schema.Array(Ref.Ref(Routine.Routine)).pipe(\n Annotation.SetParent.set(),\n Annotation.FormInputAnnotation.set(false),\n ),\n\n /** Ad hoc markdown checklist — the scratch surface; project chats write into it. */\n outline: Schema.optional(Ref.Ref(Outline.Outline).pipe(Annotation.SetParent.set())),\n\n /** Owned (or adopted synced) task container, holding the project's tasks and milestones. */\n taskSet: Schema.optional(Ref.Ref(TaskSet.TaskSet).pipe(Annotation.SetParent.set())),\n\n /**\n * Source repository this project's work lands in. Independent of `taskSet`: a project that\n * mirrors a repository adopts its synced task set AND names it here, while a project whose\n * tasks are local can still reference the repository its issues are filed against.\n */\n // TODO(burdon): Change to array? Move into taskSet?\n repo: Schema.optional(Ref.Ref(Repo.Repo).annotate({ title: 'Repository' })),\n\n /**\n * Repositories the project's coding agents work on. A cloud session checks each one out in its\n * sandbox; a session on this computer works in the folder chosen for the project there.\n */\n repositories: Schema.optional(\n Schema.Array(Ref.Ref(Repo.Repo)).annotate({\n title: 'Repositories',\n description: 'Checked out for coding agents that run in the cloud.',\n }),\n ),\n\n /** The default configuration (model, ...) for the sessions this project starts; a chat's own overrides it. */\n session: Schema.optional(SessionConfig.SessionConfig.pipe(Annotation.FormInputAnnotation.set(false))),\n }).pipe(\n Schema.annotate({ title: 'Project' }),\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--stack--regular', hue: 'amber' }),\n ArchivableAnnotation.set(true),\n // Only the project skill: filing created objects into `artifacts` is what a project-scoped\n // session structurally needs; artifact-type skills are enabled on demand. Plain dotted key, so\n // the type does not depend on the plugin that owns the skill.\n Skill.SkillsAnnotation.set(['org.dxos.skill.project']),\n Annotation.UserType.set(),\n ),\n) {}\n\n/**\n * Factory wrapper around `Obj.make` for {@link Project}.\n *\n * Materializes the owned task set and scratch outline unless the caller supplies them: there is no UI\n * to add either to a project that lacks one, so a project without them has nowhere to put its tasks\n * and nothing to draft in. The parent edges follow from the fields' `SetParent` annotation.\n */\nexport const make = (\n props: Omit<Partial<Obj.MakeProps<typeof Project>>, 'artifacts' | 'routines'> & {\n artifacts?: ReadonlyArray<Ref.Ref<Obj.Unknown>>;\n } = {},\n): Project => {\n const project = Obj.make(Project, { ...props, artifacts: props.artifacts ?? [], routines: [] });\n if (!props.taskSet) {\n const taskSet = TaskSet.make();\n Obj.update(project, (project) => {\n project.taskSet = Ref.make(taskSet);\n });\n }\n if (!props.outline) {\n const outline = Outline.make({ name: props.name });\n Obj.update(project, (project) => {\n project.outline = Ref.make(outline);\n });\n }\n return project;\n};\n\n/** Adds a routine to the project as an owned child; `SetParent` on the field cascades it. */\nexport const addRoutine = (project: Project, routine: Routine.Routine): void => {\n Obj.update(project, (project) => {\n project.routines.push(Ref.make(routine));\n });\n};\n\n/** Bindings a chat session should receive when running in a project's context. */\nexport type ContextBindings = {\n skills: Ref.Ref<Skill.Skill>[];\n objects: Ref.Ref<Obj.Unknown>[];\n};\n\n/**\n * Bindings a chat session should receive when running in this project's context: the instructions'\n * skills and context objects. The instructions object itself is NOT bound — its text/commands reach\n * the system prompt through the chat's `instructions` ref, and a bound Instructions object would\n * render as an inert context stub. Requires the instructions ref to be resolved (`.target`);\n * unresolved refs contribute nothing.\n */\nexport const contextBindings = (project: Project): ContextBindings => {\n const instructionsRef = project.instructions;\n // `.target` throws when the ref has neither an inlined target nor a resolver attached\n // (e.g. an unresolved ref outside a database context); guard with `isAvailable` first.\n if (!instructionsRef?.isAvailable) {\n return { skills: [], objects: [] };\n }\n\n const instructions = instructionsRef.target;\n if (!instructions) {\n return { skills: [], objects: [] };\n }\n\n // Refs are kept as-is (never re-wrapped via `Ref.make`): bindings persist to the conversation feed,\n // and re-wrapping would drop the registry DXN of skills that have no space-DB identity.\n return {\n skills: [...instructions.skills],\n objects: [...(instructions.objects ?? [])],\n };\n};\n"],"mappings":"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"}
|
package/dist/lib/types/Skill.mjs
CHANGED
|
@@ -95,7 +95,13 @@ var Hook = Schema.Struct({
|
|
|
95
95
|
* Passed as the input data to the function.
|
|
96
96
|
* Must match the function's input schema.
|
|
97
97
|
*/
|
|
98
|
-
input: InputTemplate.pipe(Annotation.FormInputAnnotation.set(false), Schema.optional)
|
|
98
|
+
input: InputTemplate.pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),
|
|
99
|
+
/**
|
|
100
|
+
* Run in the background (end-request only): the request settles without waiting for the hook, and
|
|
101
|
+
* the agent process awaits it before it finishes. For work the user should not wait on, e.g.
|
|
102
|
+
* reviewing the turn that just ended.
|
|
103
|
+
*/
|
|
104
|
+
async: Schema.optional(Schema.Boolean.annotate({ title: "Run in background" }))
|
|
99
105
|
});
|
|
100
106
|
/**
|
|
101
107
|
* Create a new Skill.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Skill.mjs","names":[],"sources":["../../../src/types/Skill.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport * as Schema from 'effect/Schema';\n\nimport { ToolId } from '@dxos/ai';\nimport { Annotation, Database, DXN, Filter, Format, Obj, Ref, Registry, Type, URI } from '@dxos/echo';\nimport { BaseError } from '@dxos/errors';\n// Text is referenced in the inferred type of Skill (via Template.Template → Ref.Ref(Text.Text));\n// the import lets TypeScript name it in the emitted .d.ts.\n// eslint-disable-next-line unused-imports/no-unused-imports\nimport { type Text } from '@dxos/schema';\n\nimport * as McpServer from '../McpServer.ts';\nimport * as Operation from '../Operation.ts';\nimport * as Template from './Template.ts';\nimport * as Trigger from './Trigger.ts';\n\n/**\n * Skill schema defines the structure for AI assistant skills.\n * Skills contain instructions, tools, and artifacts that guide the AI's behavior.\n * Skills may use tools to create and read artifacts, which are managed by the assistant.\n *\n * The registry `key` and `version` are stored in the object meta — access them via\n * `Obj.getMeta(skill).key` and `Obj.getMeta(skill).version`.\n */\nexport class Skill extends Type.makeObject<Skill>(DXN.make('org.dxos.type.skill', '0.2.0'))(\n Schema.Struct({\n /**\n * Human-readable name of the skill.\n */\n name: Schema.String.annotate({\n description: 'Human-readable name of the skill',\n }),\n\n /**\n * Description of the skill's purpose and functionality.\n */\n description: Schema.optional(Format.Text).annotate({\n description: \"Description of the skill's purpose and functionality\",\n }),\n\n /**\n * Instructions that guide the AI assistant's behavior and responses.\n * These are system prompts or guidelines that the AI should follow.\n */\n instructions: Template.Template.annotate({\n description: \"Instructions that guide the AI assistant's behavior and responses\",\n }),\n\n /**\n * Array of tools that the AI assistant can use when this skill is active.\n */\n tools: Schema.Array(ToolId).annotate({\n description: 'Array of tools that the AI assistant can use when this skill is active',\n }),\n\n /**\n * Whether an agent is allowed to auto-enable this skill in a conversation.\n */\n agentCanEnable: Schema.optional(Schema.Boolean).annotate({\n description: 'Whether an agent is allowed to auto-enable this skill in a conversation.',\n }),\n\n /**\n * Array of MCP servers that the AI assistant can use when this skill is active.\n */\n mcpServers: Schema.optional(Schema.Array(McpServer.Spec)),\n\n /**\n * Hooks triggered automatically at certain points in the agent's lifecycle.\n */\n hooks: Schema.optional(Schema.Array(Schema.suspend(() => Hook))),\n }).pipe(\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--student--regular', hue: 'amber' }),\n Annotation.UserType.set(),\n ),\n) {}\n\n/**\n * Controls when the hook is triggered.\n */\nexport const HookSpec = Schema.Union([\n /**\n * Triggered when the agent is about to start a request.\n * A request is a series of agent/tool turns that the model drives.\n */\n Schema.TaggedStruct('begin-request', {}),\n /**\n * Triggered when the agent has completed a request.\n * A request is a series of agent/tool turns that the model drives.\n */\n Schema.TaggedStruct('end-request', {}),\n]);\n\n/**\n * Allows hooking into the agent's lifecycle.\n * NOTE: Intentionally similar to Trigger, perhaps we should merge them.\n */\nexport const Hook = Schema.Struct({\n /**\n * What to do when the hook is triggered.\n *\n * Can be a Ref to a PersistentOperation.\n */\n function: Schema.optional(Ref.Ref(Obj.Unknown).annotate({ title: 'Function' })),\n\n /**\n * Controls when the hook is triggered.\n */\n spec: HookSpec,\n\n /**\n * Passed as the input data to the function.\n * Must match the function's input schema.\n */\n input: Trigger.InputTemplate.pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),\n});\n\n/**\n * Create a new Skill.\n * The `key` (and optional `version`) are stored in the object meta.\n * `key` must be a valid DXN name (e.g. `org.dxos.skill.mySkill`).\n */\nexport const make: {\n <T extends string>(\n props: {\n key: [DXN.Name<T>] extends [never] ? `Invalid DXN name \"${T}\": final segment must be camelCase (no hyphens)` : T;\n version?: string;\n name: string;\n /** Opt into MCP projection; see {@link McpPromptAnnotation}. Stored in meta, not as a field. */\n mcpPrompt?: boolean;\n } & Partial<Skill>,\n ): Skill;\n} = ({ key, version, mcpPrompt, tools = [], instructions = Template.make(), ...props }) => {\n const annotations: Annotation.Dictionary = {};\n if (mcpPrompt !== undefined) {\n Annotation.setDictionary(annotations, McpPromptAnnotation, mcpPrompt);\n }\n return Obj.make(Skill, {\n [Obj.Meta]: { key, version, annotations },\n tools,\n instructions,\n ...props,\n });\n};\n\n/**\n * Annotation opting a skill into MCP projection: it becomes a prompt and is loadable by name\n * through the server's `loadSkill` tool.\n *\n * Opt-in: a skill written for an in-app chat runtime may assume tools an MCP client does not have,\n * and only the author can judge whether the workflow still holds on the MCP surface. Absent ⇒ the\n * skill stays internal to hosts that resolve skills directly.\n *\n * Rides in the object's meta rather than on `Definition`, so it survives into a persisted skill —\n * `Definition` is a build-time factory type and cannot describe a skill stored in a space.\n */\n/**\n * Skill keys a session scoped to an instance of the annotated type should carry — read by the AI\n * companion, the blank routine template, and project chat creation. Held as plain dotted keys so the\n * annotated type does not depend on the plugin that owns each skill.\n */\nexport const SkillsAnnotation = Annotation.make<string[]>({\n id: 'org.dxos.annotation.skills',\n schema: Schema.mutable(Schema.Array(Schema.String)),\n});\n\nexport const McpPromptAnnotation = Annotation.make({\n id: 'org.dxos.skill.mcp-prompt',\n schema: Schema.Boolean,\n});\n\n/** Whether the skill opted into MCP projection; see {@link McpPromptAnnotation}. */\nexport const isMcpPrompt = (skill: Skill): boolean =>\n Annotation.get(skill, McpPromptAnnotation).pipe(Option.getOrElse(() => false));\n\n/**\n * Get the registry key for a skill.\n */\nexport const getKey = (skill: Skill): string => {\n const key = Obj.getMeta(skill).key;\n if (key === undefined) {\n throw new Error('Skill is missing the meta key.');\n }\n return key;\n};\n\n/**\n * Get the registry version for a skill, if any.\n */\nexport const getVersion = (skill: Skill): string | undefined => Obj.getMeta(skill).version;\n\n/**\n * Util to create tool definitions for a skill.\n * Operation tool ids are the model-facing names (see {@link Operation.toolName}), so the skill's\n * `tools` array and the session toolkit speak one identifier space.\n */\nexport const toolDefinitions = ({\n tools = [],\n operations = [],\n}: {\n tools?: readonly string[];\n operations?: readonly Operation.Definition.Any[];\n}) => [...operations.map((op) => ToolId.make(Operation.toolName(op))), ...tools.map((tool) => ToolId.make(tool))];\n\n/**\n * Factory for the skills.\n */\nexport type Definition = {\n key: DXN.Name<string>;\n make: () => Skill;\n /**\n * Operation definitions behind the skill's `tools` list, for hosts that serve the skill without\n * a registry to resolve ToolIds against (see mcp-server `McpServer.fromSkills`). Absent ⇒ the skill is\n * only served through a registry-backed host.\n */\n operations?: readonly Operation.Definition.Any[];\n};\n\n/**\n * Returns the canonical URI used to reference this skill in the registry.\n * Use this URI with `Ref.fromURI` to bind a skill without cloning it to the DB.\n *\n * TODO(wittjosiah): Should use Obj.getURI instead once it supports options to prefer meta key over EID.\n */\nexport const registryURI = (key: DXN.Name<string>): URI.URI => (DXN.tryMake(`dxn:${key}`) ?? URI.make(key)) as URI.URI;\n\n/**\n * The ref a session binds for a resolved skill: a skill in a database is bound as-is (it is\n * space-authored or a fork carrying the user's edits), anything else by its registry URI, so a\n * rebind never substitutes the pristine copy for the fork or vice versa.\n */\nexport const makeRef = (skill: Skill): Ref.Ref<Skill> =>\n Obj.getDatabase(skill) !== undefined ? Ref.make(skill) : Ref.fromURI(registryURI(getKey(skill)));\n\n/**\n * Registry skill refs declared by an object's type via {@link SkillsAnnotation}.\n * Bound by URI rather than a DB clone, so the ref resolves through the hypergraph registry.\n */\nexport const annotatedSkillRefs = (object: Obj.Unknown): Ref.Ref<Skill>[] => {\n const type = Obj.getType(object);\n if (!type) {\n return [];\n }\n\n return annotatedSkillKeys(type).map((key) => Ref.fromURI(registryURI(key)));\n};\n\n/** Skill keys declared by a type via {@link SkillsAnnotation}. */\nexport const annotatedSkillKeys = (type: Type.AnyEntity): string[] =>\n Option.getOrElse(() => [] as string[])(SkillsAnnotation.get(Type.getSchema(type)));\n\n/**\n * Refs for an object's {@link SkillsAnnotation} keys; a space copy wins, since it is a fork of the\n * registry skill and carries the user's edits.\n *\n * TODO(wittjosiah): Lift this two-source merge into the query layer; it is how forking should work\n * for any type, not just skills.\n */\nexport const resolveAnnotatedSkills = Effect.fnUntraced(function* (object: Obj.Unknown) {\n const type = Obj.getType(object);\n const keys = type ? annotatedSkillKeys(type) : [];\n if (keys.length === 0) {\n return [] as Ref.Ref<Skill>[];\n }\n\n const byKey = new Map<string, Ref.Ref<Skill>>();\n for (const skill of yield* Registry.runQuery(Filter.type(Skill))) {\n const key = Obj.getMeta(skill).key;\n if (key && keys.includes(key)) {\n // By URI rather than a clone: the ECHO ref resolver already spans the registry.\n byKey.set(key, Ref.fromURI(registryURI(key)));\n }\n }\n for (const skill of yield* Database.query(Filter.type(Skill)).run) {\n const key = Obj.getMeta(skill).key;\n if (key && keys.includes(key)) {\n byKey.set(key, Ref.make(skill));\n }\n }\n\n return [...byKey.values()];\n});\n\n/**\n * Resolves a skill from the registry by its meta key.\n * Does not check the local database for the skill.\n */\nexport const resolve = (key: string): Effect.Effect<Skill, NotFoundError, Registry.Service> =>\n Effect.gen(function* () {\n const results = yield* Registry.runQuery(Filter.and(Filter.type(Skill), Filter.key(key)));\n const skill = results[0];\n if (!skill) {\n return yield* Effect.fail(new NotFoundError({ context: { key } }));\n }\n return skill;\n });\n\n/**\n * Upserts a skill into the database.\n * If the skill already exists in the database, the local (possibly forked) copy is returned as-is.\n * Otherwise, a fresh copy is cloned from the registry and added.\n * @deprecated Since we're using a registry we no longer need to store skills in the database.\n */\n// TODO(dmaretskyi): Remove.\nexport const upsert = (key: string): Effect.Effect<Skill, NotFoundError, Registry.Service | Database.Service> =>\n Effect.gen(function* () {\n const local = yield* Database.query(Filter.and(Filter.type(Skill), Filter.key(key))).run;\n if (local.length > 0) {\n return local[0];\n }\n return yield* Database.add(Obj.clone(yield* resolve(key), { deep: 'all' }));\n });\n\nexport class NotFoundError extends BaseError.extend('SkillNotFound', 'Skill not found') {}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Skill.mjs","names":[],"sources":["../../../src/types/Skill.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport * as Schema from 'effect/Schema';\n\nimport { ToolId } from '@dxos/ai';\nimport { Annotation, Database, DXN, Filter, Format, Obj, Ref, Registry, Type, URI } from '@dxos/echo';\nimport { BaseError } from '@dxos/errors';\n// Text is referenced in the inferred type of Skill (via Template.Template → Ref.Ref(Text.Text));\n// the import lets TypeScript name it in the emitted .d.ts.\n// eslint-disable-next-line unused-imports/no-unused-imports\nimport { type Text } from '@dxos/schema';\n\nimport * as McpServer from '../McpServer.ts';\nimport * as Operation from '../Operation.ts';\nimport * as Template from './Template.ts';\nimport * as Trigger from './Trigger.ts';\n\n/**\n * Skill schema defines the structure for AI assistant skills.\n * Skills contain instructions, tools, and artifacts that guide the AI's behavior.\n * Skills may use tools to create and read artifacts, which are managed by the assistant.\n *\n * The registry `key` and `version` are stored in the object meta — access them via\n * `Obj.getMeta(skill).key` and `Obj.getMeta(skill).version`.\n */\nexport class Skill extends Type.makeObject<Skill>(DXN.make('org.dxos.type.skill', '0.2.0'))(\n Schema.Struct({\n /**\n * Human-readable name of the skill.\n */\n name: Schema.String.annotate({\n description: 'Human-readable name of the skill',\n }),\n\n /**\n * Description of the skill's purpose and functionality.\n */\n description: Schema.optional(Format.Text).annotate({\n description: \"Description of the skill's purpose and functionality\",\n }),\n\n /**\n * Instructions that guide the AI assistant's behavior and responses.\n * These are system prompts or guidelines that the AI should follow.\n */\n instructions: Template.Template.annotate({\n description: \"Instructions that guide the AI assistant's behavior and responses\",\n }),\n\n /**\n * Array of tools that the AI assistant can use when this skill is active.\n */\n tools: Schema.Array(ToolId).annotate({\n description: 'Array of tools that the AI assistant can use when this skill is active',\n }),\n\n /**\n * Whether an agent is allowed to auto-enable this skill in a conversation.\n */\n agentCanEnable: Schema.optional(Schema.Boolean).annotate({\n description: 'Whether an agent is allowed to auto-enable this skill in a conversation.',\n }),\n\n /**\n * Array of MCP servers that the AI assistant can use when this skill is active.\n */\n mcpServers: Schema.optional(Schema.Array(McpServer.Spec)),\n\n /**\n * Hooks triggered automatically at certain points in the agent's lifecycle.\n */\n hooks: Schema.optional(Schema.Array(Schema.suspend(() => Hook))),\n }).pipe(\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--student--regular', hue: 'amber' }),\n Annotation.UserType.set(),\n ),\n) {}\n\n/**\n * Controls when the hook is triggered.\n */\nexport const HookSpec = Schema.Union([\n /**\n * Triggered when the agent is about to start a request.\n * A request is a series of agent/tool turns that the model drives.\n */\n Schema.TaggedStruct('begin-request', {}),\n /**\n * Triggered when the agent has completed a request.\n * A request is a series of agent/tool turns that the model drives.\n */\n Schema.TaggedStruct('end-request', {}),\n]);\n\n/**\n * Allows hooking into the agent's lifecycle.\n * NOTE: Intentionally similar to Trigger, perhaps we should merge them.\n */\nexport const Hook = Schema.Struct({\n /**\n * What to do when the hook is triggered.\n *\n * Can be a Ref to a PersistentOperation.\n */\n function: Schema.optional(Ref.Ref(Obj.Unknown).annotate({ title: 'Function' })),\n\n /**\n * Controls when the hook is triggered.\n */\n spec: HookSpec,\n\n /**\n * Passed as the input data to the function.\n * Must match the function's input schema.\n */\n input: Trigger.InputTemplate.pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),\n\n /**\n * Run in the background (end-request only): the request settles without waiting for the hook, and\n * the agent process awaits it before it finishes. For work the user should not wait on, e.g.\n * reviewing the turn that just ended.\n */\n async: Schema.optional(Schema.Boolean.annotate({ title: 'Run in background' })),\n});\n\nexport type Hook = Schema.Schema.Type<typeof Hook>;\n\n/**\n * Create a new Skill.\n * The `key` (and optional `version`) are stored in the object meta.\n * `key` must be a valid DXN name (e.g. `org.dxos.skill.mySkill`).\n */\nexport const make: {\n <T extends string>(\n props: {\n key: [DXN.Name<T>] extends [never] ? `Invalid DXN name \"${T}\": final segment must be camelCase (no hyphens)` : T;\n version?: string;\n name: string;\n /** Opt into MCP projection; see {@link McpPromptAnnotation}. Stored in meta, not as a field. */\n mcpPrompt?: boolean;\n } & Partial<Skill>,\n ): Skill;\n} = ({ key, version, mcpPrompt, tools = [], instructions = Template.make(), ...props }) => {\n const annotations: Annotation.Dictionary = {};\n if (mcpPrompt !== undefined) {\n Annotation.setDictionary(annotations, McpPromptAnnotation, mcpPrompt);\n }\n return Obj.make(Skill, {\n [Obj.Meta]: { key, version, annotations },\n tools,\n instructions,\n ...props,\n });\n};\n\n/**\n * Annotation opting a skill into MCP projection: it becomes a prompt and is loadable by name\n * through the server's `loadSkill` tool.\n *\n * Opt-in: a skill written for an in-app chat runtime may assume tools an MCP client does not have,\n * and only the author can judge whether the workflow still holds on the MCP surface. Absent ⇒ the\n * skill stays internal to hosts that resolve skills directly.\n *\n * Rides in the object's meta rather than on `Definition`, so it survives into a persisted skill —\n * `Definition` is a build-time factory type and cannot describe a skill stored in a space.\n */\n/**\n * Skill keys a session scoped to an instance of the annotated type should carry — read by the AI\n * companion, the blank routine template, and project chat creation. Held as plain dotted keys so the\n * annotated type does not depend on the plugin that owns each skill.\n */\nexport const SkillsAnnotation = Annotation.make<string[]>({\n id: 'org.dxos.annotation.skills',\n schema: Schema.mutable(Schema.Array(Schema.String)),\n});\n\nexport const McpPromptAnnotation = Annotation.make({\n id: 'org.dxos.skill.mcp-prompt',\n schema: Schema.Boolean,\n});\n\n/** Whether the skill opted into MCP projection; see {@link McpPromptAnnotation}. */\nexport const isMcpPrompt = (skill: Skill): boolean =>\n Annotation.get(skill, McpPromptAnnotation).pipe(Option.getOrElse(() => false));\n\n/**\n * Get the registry key for a skill.\n */\nexport const getKey = (skill: Skill): string => {\n const key = Obj.getMeta(skill).key;\n if (key === undefined) {\n throw new Error('Skill is missing the meta key.');\n }\n return key;\n};\n\n/**\n * Get the registry version for a skill, if any.\n */\nexport const getVersion = (skill: Skill): string | undefined => Obj.getMeta(skill).version;\n\n/**\n * Util to create tool definitions for a skill.\n * Operation tool ids are the model-facing names (see {@link Operation.toolName}), so the skill's\n * `tools` array and the session toolkit speak one identifier space.\n */\nexport const toolDefinitions = ({\n tools = [],\n operations = [],\n}: {\n tools?: readonly string[];\n operations?: readonly Operation.Definition.Any[];\n}) => [...operations.map((op) => ToolId.make(Operation.toolName(op))), ...tools.map((tool) => ToolId.make(tool))];\n\n/**\n * Factory for the skills.\n */\nexport type Definition = {\n key: DXN.Name<string>;\n make: () => Skill;\n /**\n * Operation definitions behind the skill's `tools` list, for hosts that serve the skill without\n * a registry to resolve ToolIds against (see mcp-server `McpServer.fromSkills`). Absent ⇒ the skill is\n * only served through a registry-backed host.\n */\n operations?: readonly Operation.Definition.Any[];\n};\n\n/**\n * Returns the canonical URI used to reference this skill in the registry.\n * Use this URI with `Ref.fromURI` to bind a skill without cloning it to the DB.\n *\n * TODO(wittjosiah): Should use Obj.getURI instead once it supports options to prefer meta key over EID.\n */\nexport const registryURI = (key: DXN.Name<string>): URI.URI => (DXN.tryMake(`dxn:${key}`) ?? URI.make(key)) as URI.URI;\n\n/**\n * The ref a session binds for a resolved skill: a skill in a database is bound as-is (it is\n * space-authored or a fork carrying the user's edits), anything else by its registry URI, so a\n * rebind never substitutes the pristine copy for the fork or vice versa.\n */\nexport const makeRef = (skill: Skill): Ref.Ref<Skill> =>\n Obj.getDatabase(skill) !== undefined ? Ref.make(skill) : Ref.fromURI(registryURI(getKey(skill)));\n\n/**\n * Registry skill refs declared by an object's type via {@link SkillsAnnotation}.\n * Bound by URI rather than a DB clone, so the ref resolves through the hypergraph registry.\n */\nexport const annotatedSkillRefs = (object: Obj.Unknown): Ref.Ref<Skill>[] => {\n const type = Obj.getType(object);\n if (!type) {\n return [];\n }\n\n return annotatedSkillKeys(type).map((key) => Ref.fromURI(registryURI(key)));\n};\n\n/** Skill keys declared by a type via {@link SkillsAnnotation}. */\nexport const annotatedSkillKeys = (type: Type.AnyEntity): string[] =>\n Option.getOrElse(() => [] as string[])(SkillsAnnotation.get(Type.getSchema(type)));\n\n/**\n * Refs for an object's {@link SkillsAnnotation} keys; a space copy wins, since it is a fork of the\n * registry skill and carries the user's edits.\n *\n * TODO(wittjosiah): Lift this two-source merge into the query layer; it is how forking should work\n * for any type, not just skills.\n */\nexport const resolveAnnotatedSkills = Effect.fnUntraced(function* (object: Obj.Unknown) {\n const type = Obj.getType(object);\n const keys = type ? annotatedSkillKeys(type) : [];\n if (keys.length === 0) {\n return [] as Ref.Ref<Skill>[];\n }\n\n const byKey = new Map<string, Ref.Ref<Skill>>();\n for (const skill of yield* Registry.runQuery(Filter.type(Skill))) {\n const key = Obj.getMeta(skill).key;\n if (key && keys.includes(key)) {\n // By URI rather than a clone: the ECHO ref resolver already spans the registry.\n byKey.set(key, Ref.fromURI(registryURI(key)));\n }\n }\n for (const skill of yield* Database.query(Filter.type(Skill)).run) {\n const key = Obj.getMeta(skill).key;\n if (key && keys.includes(key)) {\n byKey.set(key, Ref.make(skill));\n }\n }\n\n return [...byKey.values()];\n});\n\n/**\n * Resolves a skill from the registry by its meta key.\n * Does not check the local database for the skill.\n */\nexport const resolve = (key: string): Effect.Effect<Skill, NotFoundError, Registry.Service> =>\n Effect.gen(function* () {\n const results = yield* Registry.runQuery(Filter.and(Filter.type(Skill), Filter.key(key)));\n const skill = results[0];\n if (!skill) {\n return yield* Effect.fail(new NotFoundError({ context: { key } }));\n }\n return skill;\n });\n\n/**\n * Upserts a skill into the database.\n * If the skill already exists in the database, the local (possibly forked) copy is returned as-is.\n * Otherwise, a fresh copy is cloned from the registry and added.\n * @deprecated Since we're using a registry we no longer need to store skills in the database.\n */\n// TODO(dmaretskyi): Remove.\nexport const upsert = (key: string): Effect.Effect<Skill, NotFoundError, Registry.Service | Database.Service> =>\n Effect.gen(function* () {\n const local = yield* Database.query(Filter.and(Filter.type(Skill), Filter.key(key))).run;\n if (local.length > 0) {\n return local[0];\n }\n return yield* Database.add(Obj.clone(yield* resolve(key), { deep: 'all' }));\n });\n\nexport class NotFoundError extends BaseError.extend('SkillNotFound', 'Skill not found') {}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "../chunk-rolldown-runtime.mjs";
|
|
2
|
-
import { TriggerStateNotFoundError } from "../errors.mjs";
|
|
2
|
+
import { TriggerDisabledError, TriggerStateNotFoundError } from "../errors.mjs";
|
|
3
3
|
import { Runnable } from "../Runnable.mjs";
|
|
4
4
|
import * as Schema from "effect/Schema";
|
|
5
5
|
import { Annotation, DXN, Feed, Obj, QueryAST, Ref, Type } from "@dxos/echo";
|
|
@@ -13,11 +13,12 @@ var Trigger_exports = /* @__PURE__ */ __exportAll({
|
|
|
13
13
|
FeedSpec: () => FeedSpec,
|
|
14
14
|
InputTemplate: () => InputTemplate,
|
|
15
15
|
Kinds: () => Kinds,
|
|
16
|
+
ManagerService: () => ManagerService,
|
|
16
17
|
Spec: () => Spec,
|
|
17
18
|
SubscriptionSpec: () => SubscriptionSpec,
|
|
18
19
|
TimerSpec: () => TimerSpec,
|
|
19
20
|
Trigger: () => Trigger,
|
|
20
|
-
|
|
21
|
+
TriggerDisabledError: () => TriggerDisabledError,
|
|
21
22
|
TriggerStateNotFoundError: () => TriggerStateNotFoundError,
|
|
22
23
|
WebhookSpec: () => WebhookSpec,
|
|
23
24
|
isManuallyInvokable: () => isManuallyInvokable,
|
|
@@ -194,10 +195,10 @@ var make = (props) => Obj.make(Trigger, props);
|
|
|
194
195
|
*/
|
|
195
196
|
var isManuallyInvokable = (spec) => spec?.kind === "direct" || spec?.kind === "timer";
|
|
196
197
|
/**
|
|
197
|
-
* Service
|
|
198
|
+
* Service providing the {@link Manager}.
|
|
198
199
|
*/
|
|
199
|
-
var
|
|
200
|
+
var ManagerService = class extends Context.Service()("@dxos/compute/Trigger.ManagerService") {};
|
|
200
201
|
//#endregion
|
|
201
|
-
export { DirectSpec, EmailSpec, FeedSpec, InputTemplate, Kinds, Spec, SubscriptionSpec, TimerSpec, Trigger,
|
|
202
|
+
export { DirectSpec, EmailSpec, FeedSpec, InputTemplate, Kinds, ManagerService, Spec, SubscriptionSpec, TimerSpec, Trigger, TriggerDisabledError, TriggerStateNotFoundError, WebhookSpec, isManuallyInvokable, make, specDirect, specEmail, specFeed, specSubscription, specTimer, specWebhook, Trigger_exports as t };
|
|
202
203
|
|
|
203
204
|
//# sourceMappingURL=Trigger.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Trigger.mjs","names":[],"sources":["../../../src/types/Trigger.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Context from 'effect/Context';\nimport type * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport type * as Atom from 'effect/reactivity/Atom';\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Feed, Obj, type Query, QueryAST, Ref, Type } from '@dxos/echo';\nimport { OptionsAnnotationId } from '@dxos/echo/Format';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\n\nimport * as Runnable from '../Runnable.ts';\nimport type * as TriggerEvent from './TriggerEvent.ts';\n\n/**\n * Type discriminator for TriggerType.\n * Every spec has a type field of type TriggerKind that we can use to understand which type we're working with.\n * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions\n */\nexport const Kinds = ['email', 'feed', 'direct', 'subscription', 'timer', 'webhook'] as const;\nexport type Kind = (typeof Kinds)[number];\n\nconst kindLiteralAnnotations = { title: 'Kind' };\n\nexport const EmailSpec = Schema.Struct({\n kind: Schema.Literal('email').annotate(kindLiteralAnnotations),\n});\nexport type EmailSpec = Schema.Schema.Type<typeof EmailSpec>;\n\n/**\n * Construct an Email trigger spec.\n */\nexport const specEmail = (): EmailSpec => ({ kind: 'email' });\n\n// TODO(wittjosiah): Remove. Migrate to Subscription triggers once EDGE supports them for feed queries.\nexport const FeedSpec = Schema.Struct({\n kind: Schema.Literal('feed').annotate(kindLiteralAnnotations),\n feed: Schema.optional(Ref.Ref(Feed.Feed).annotate({ title: 'Feed' })),\n});\nexport type FeedSpec = Schema.Schema.Type<typeof FeedSpec>;\n\n/**\n * Construct a Feed trigger spec from a Feed object.\n */\nexport const specFeed = (feed: Feed.Feed): FeedSpec => ({\n kind: 'feed',\n feed: Ref.make(feed),\n});\n\n/**\n * Subscription.\n */\nexport const SubscriptionSpec = Schema.Struct({\n kind: Schema.Literal('subscription').annotate(kindLiteralAnnotations),\n\n // TODO(burdon): Issue.\n query: Schema.Struct({\n raw: Schema.optional(Schema.String.annotate({ title: 'Query' })),\n ast: QueryAST.Query,\n }),\n\n options: Schema.optional(\n Schema.Struct({\n // Watch changes to object (not just creation).\n deep: Schema.optional(Schema.Boolean.annotate({ title: 'Nested' })),\n // Debounce changes (delay in ms).\n delay: Schema.optional(Schema.Number.annotate({ title: 'Delay' })),\n }).annotate({ title: 'Options' }),\n ),\n});\nexport type SubscriptionSpec = Schema.Schema.Type<typeof SubscriptionSpec>;\n\n/**\n * Construct a Subscription trigger spec from a Query object.\n */\nexport const specSubscription = (\n query: Query.Query<any>,\n options?: { deep?: boolean; delay?: number },\n): SubscriptionSpec => ({\n kind: 'subscription',\n query: {\n ast: query.ast,\n },\n options: options\n ? {\n deep: options.deep,\n delay: options.delay,\n }\n : undefined,\n});\n\n/**\n * Direct invocation only; never scheduled by the dispatcher (invoked on demand by a caller).\n */\nexport const DirectSpec = Schema.Struct({\n kind: Schema.Literal('direct').annotate(kindLiteralAnnotations),\n});\nexport type DirectSpec = Schema.Schema.Type<typeof DirectSpec>;\n\n/**\n * Construct a Direct trigger spec.\n */\nexport const specDirect = (): DirectSpec => ({ kind: 'direct' });\n\n/**\n * Cron timer.\n */\nexport const TimerSpec = Schema.Struct({\n kind: Schema.Literal('timer').annotate(kindLiteralAnnotations),\n cron: Schema.String.annotate({\n title: 'Cron',\n [SchemaAST.ExamplesAnnotationId]: ['0 0 * * *'],\n }),\n});\nexport type TimerSpec = Schema.Schema.Type<typeof TimerSpec>;\n\n/**\n * Construct a Timer trigger spec from a cron string.\n */\nexport const specTimer = (cron: string): TimerSpec => ({ kind: 'timer', cron });\n\n/**\n * Webhook.\n */\nexport const WebhookSpec = Schema.Struct({\n kind: Schema.Literal('webhook').annotate(kindLiteralAnnotations),\n method: Schema.optional(\n Schema.String.annotate({\n title: 'Method',\n [OptionsAnnotationId]: ['GET', 'POST'],\n }),\n ),\n port: Schema.optional(\n Schema.Number.annotate({\n title: 'Port',\n }),\n ),\n});\nexport type WebhookSpec = Schema.Schema.Type<typeof WebhookSpec>;\n\n/**\n * Construct a Webhook trigger spec from a method and port.\n */\nexport const specWebhook = (opts?: { method?: string; port?: number }): WebhookSpec => ({\n kind: 'webhook',\n method: opts?.method,\n port: opts?.port,\n});\n\n/**\n * Trigger schema.\n */\nexport const Spec = Schema.Union([EmailSpec, FeedSpec, DirectSpec, SubscriptionSpec, TimerSpec, WebhookSpec]).annotate({\n title: 'Trigger',\n});\nexport type Spec = Schema.Schema.Type<typeof Spec>;\n\n/**\n * Forms the input data passed to the function.\n * Must match the function's input schema.\n *\n * @example\n * {\n * item: '{{event.item}}',\n * instructions: 'Summarize and perform entity-extraction'\n * mailbox: { '/': 'echo://AAA/ZZZ' }\n * }\n */\nexport const InputTemplate = Schema.Record(Schema.String, Schema.Any);\n\n/**\n * Function trigger.\n * Runnable is invoked with the `payload` passed as input data.\n * The event that fires the trigger is available in the runnable context.\n */\nexport class Trigger extends Type.makeObject<Trigger>(DXN.make('org.dxos.type.trigger', '0.1.0'))(\n Schema.Struct({\n /**\n * Runnable (operation or workflow) to invoke.\n * Wired programmatically (see plugin-routine's `wireTriggers`); not user-editable, so hidden from forms.\n */\n runnable: Ref.Ref(Runnable.Runnable).pipe(\n Schema.annotate({ title: 'Runnable' }),\n Annotation.FormInputAnnotation.set(false),\n Schema.optional,\n ),\n\n spec: Spec.pipe(Schema.optional),\n\n enabled: Schema.Boolean.pipe(Schema.optional),\n\n /**\n * Runs this trigger on the edge rather than locally.\n * When unset, the trigger runs locally on the client.\n */\n remote: Schema.Boolean.pipe(Schema.annotate({ title: 'Remote' }), Schema.optional),\n\n concurrency: Schema.Number.pipe(\n Schema.annotate({\n title: 'Concurrency',\n default: 1,\n description: 'Maximum number of concurrent invocations of the trigger.',\n }),\n Annotation.FormInputAnnotation.set(false),\n Schema.optional,\n ),\n\n /**\n * Only used for workflowSchema.\n * Specifies the input node in the circuit.\n * @deprecated Remove and enforce a single input node in all compute graphSchema.\n */\n inputNodeId: Schema.String.pipe(\n Schema.annotate({ title: 'Input Node ID' }),\n Annotation.FormInputAnnotation.set(false),\n Schema.optional,\n ),\n\n /**\n * Passed as the input data to the runnable.\n */\n input: InputTemplate.pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),\n }).pipe(Annotation.IconAnnotation.set({ icon: 'ph--lightning--regular', hue: 'yellow' })),\n) {}\n\nexport const make = (props: Obj.MakeProps<typeof Trigger>) => Obj.make(Trigger, props);\n\n/**\n * Checks if a trigger having this spec can be manually invoked (a `direct` or `timer` trigger).\n */\nexport const isManuallyInvokable = (spec?: Spec): boolean => spec?.kind === 'direct' || spec?.kind === 'timer';\n\n/**\n * Runtime state of a trigger.\n */\nexport interface State {\n /**\n * Reference to the trigger object.\n */\n readonly trigger: Ref.Ref<Trigger>;\n\n /**\n * Where is this trigger running.\n */\n readonly environment: 'edge' | 'local';\n\n /**\n * Next scheduled cron execution. Set only for `timer` triggers.\n */\n readonly nextExecution?: Date;\n\n /**\n * Time until which scheduled invocations of this trigger are skipped after a genuine failure.\n * Applies to all trigger kinds. Manual {@link TriggerDispatcher.invokeTrigger} calls bypass it.\n */\n readonly cooldownUntil?: Date;\n\n /**\n * Pending re-invocation requested via {@link Operation.runAgain} ({@link RunAgainError}).\n * Retries are drained at the tail of the invocation queue, ordered by `enqueuedAt`.\n */\n readonly retry?: {\n /**\n * Event to replay on the retry. Re-running is assumed safe with the same input.\n */\n readonly event: TriggerEvent.TriggerEvent;\n\n /**\n * Monotonic sequence number used to order pending retries FIFO at the tail of the queue.\n */\n readonly enqueuedAt: number;\n };\n\n /**\n * Result of the most recent invocation of this trigger.\n */\n readonly lastResult: Exit.Exit<unknown> | null;\n}\n\nexport interface InvokeOptions {\n trigger: Trigger;\n event: TriggerEvent.TriggerEvent;\n}\n\n/**\n * Service for monitoring trigger dispatcher state.\n */\nexport interface Monitor {\n /**\n * Triggers actively registered in the dispatcher.\n * Could contain entries for both local and edge triggers, but only the edge ones are actually running.\n */\n readonly triggers: Atom.Atom<readonly State[]>;\n\n readonly localDispatcherEnabled: boolean;\n\n /**\n * Invoke a trigger.\n * Available only for direct and timer triggers.\n * Invocation respects the trigger's concurrency limit.\n */\n readonly invokeTrigger: (options: InvokeOptions) => Effect.Effect<void>;\n}\n\n/**\n * Service for monitoring trigger executions.\n */\nexport class TriggerMonitorService extends Context.Service<TriggerMonitorService, Monitor>()(\n '@dxos/functions/TriggerMonitorService',\n) {}\n\nexport { TriggerStateNotFoundError } from '../errors.ts';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Trigger.mjs","names":[],"sources":["../../../src/types/Trigger.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Context from 'effect/Context';\nimport type * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport type * as Atom from 'effect/reactivity/Atom';\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Feed, Obj, type Query, QueryAST, Ref, Type } from '@dxos/echo';\nimport { OptionsAnnotationId } from '@dxos/echo/Format';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\n\nimport type { TriggerDisabledError } from '../errors.ts';\nimport * as Runnable from '../Runnable.ts';\nimport type * as TriggerEvent from './TriggerEvent.ts';\n\n/**\n * Type discriminator for TriggerType.\n * Every spec has a type field of type TriggerKind that we can use to understand which type we're working with.\n * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions\n */\nexport const Kinds = ['email', 'feed', 'direct', 'subscription', 'timer', 'webhook'] as const;\nexport type Kind = (typeof Kinds)[number];\n\nconst kindLiteralAnnotations = { title: 'Kind' };\n\nexport const EmailSpec = Schema.Struct({\n kind: Schema.Literal('email').annotate(kindLiteralAnnotations),\n});\nexport type EmailSpec = Schema.Schema.Type<typeof EmailSpec>;\n\n/**\n * Construct an Email trigger spec.\n */\nexport const specEmail = (): EmailSpec => ({ kind: 'email' });\n\n// TODO(wittjosiah): Remove. Migrate to Subscription triggers once EDGE supports them for feed queries.\nexport const FeedSpec = Schema.Struct({\n kind: Schema.Literal('feed').annotate(kindLiteralAnnotations),\n feed: Schema.optional(Ref.Ref(Feed.Feed).annotate({ title: 'Feed' })),\n});\nexport type FeedSpec = Schema.Schema.Type<typeof FeedSpec>;\n\n/**\n * Construct a Feed trigger spec from a Feed object.\n */\nexport const specFeed = (feed: Feed.Feed): FeedSpec => ({\n kind: 'feed',\n feed: Ref.make(feed),\n});\n\n/**\n * Subscription.\n */\nexport const SubscriptionSpec = Schema.Struct({\n kind: Schema.Literal('subscription').annotate(kindLiteralAnnotations),\n\n // TODO(burdon): Issue.\n query: Schema.Struct({\n raw: Schema.optional(Schema.String.annotate({ title: 'Query' })),\n ast: QueryAST.Query,\n }),\n\n options: Schema.optional(\n Schema.Struct({\n // Watch changes to object (not just creation).\n deep: Schema.optional(Schema.Boolean.annotate({ title: 'Nested' })),\n // Debounce changes (delay in ms).\n delay: Schema.optional(Schema.Number.annotate({ title: 'Delay' })),\n }).annotate({ title: 'Options' }),\n ),\n});\nexport type SubscriptionSpec = Schema.Schema.Type<typeof SubscriptionSpec>;\n\n/**\n * Construct a Subscription trigger spec from a Query object.\n */\nexport const specSubscription = (\n query: Query.Query<any>,\n options?: { deep?: boolean; delay?: number },\n): SubscriptionSpec => ({\n kind: 'subscription',\n query: {\n ast: query.ast,\n },\n options: options\n ? {\n deep: options.deep,\n delay: options.delay,\n }\n : undefined,\n});\n\n/**\n * Direct invocation only; never scheduled by the dispatcher (invoked on demand by a caller).\n */\nexport const DirectSpec = Schema.Struct({\n kind: Schema.Literal('direct').annotate(kindLiteralAnnotations),\n});\nexport type DirectSpec = Schema.Schema.Type<typeof DirectSpec>;\n\n/**\n * Construct a Direct trigger spec.\n */\nexport const specDirect = (): DirectSpec => ({ kind: 'direct' });\n\n/**\n * Cron timer.\n */\nexport const TimerSpec = Schema.Struct({\n kind: Schema.Literal('timer').annotate(kindLiteralAnnotations),\n cron: Schema.String.annotate({\n title: 'Cron',\n [SchemaAST.ExamplesAnnotationId]: ['0 0 * * *'],\n }),\n});\nexport type TimerSpec = Schema.Schema.Type<typeof TimerSpec>;\n\n/**\n * Construct a Timer trigger spec from a cron string.\n */\nexport const specTimer = (cron: string): TimerSpec => ({ kind: 'timer', cron });\n\n/**\n * Webhook.\n */\nexport const WebhookSpec = Schema.Struct({\n kind: Schema.Literal('webhook').annotate(kindLiteralAnnotations),\n method: Schema.optional(\n Schema.String.annotate({\n title: 'Method',\n [OptionsAnnotationId]: ['GET', 'POST'],\n }),\n ),\n port: Schema.optional(\n Schema.Number.annotate({\n title: 'Port',\n }),\n ),\n});\nexport type WebhookSpec = Schema.Schema.Type<typeof WebhookSpec>;\n\n/**\n * Construct a Webhook trigger spec from a method and port.\n */\nexport const specWebhook = (opts?: { method?: string; port?: number }): WebhookSpec => ({\n kind: 'webhook',\n method: opts?.method,\n port: opts?.port,\n});\n\n/**\n * Trigger schema.\n */\nexport const Spec = Schema.Union([EmailSpec, FeedSpec, DirectSpec, SubscriptionSpec, TimerSpec, WebhookSpec]).annotate({\n title: 'Trigger',\n});\nexport type Spec = Schema.Schema.Type<typeof Spec>;\n\n/**\n * Forms the input data passed to the function.\n * Must match the function's input schema.\n *\n * @example\n * {\n * item: '{{event.item}}',\n * instructions: 'Summarize and perform entity-extraction'\n * mailbox: { '/': 'echo://AAA/ZZZ' }\n * }\n */\nexport const InputTemplate = Schema.Record(Schema.String, Schema.Any);\n\n/**\n * Function trigger.\n * Runnable is invoked with the `payload` passed as input data.\n * The event that fires the trigger is available in the runnable context.\n */\nexport class Trigger extends Type.makeObject<Trigger>(DXN.make('org.dxos.type.trigger', '0.1.0'))(\n Schema.Struct({\n /**\n * Runnable (operation or workflow) to invoke.\n * Wired programmatically (see plugin-routine's `wireTriggers`); not user-editable, so hidden from forms.\n */\n runnable: Ref.Ref(Runnable.Runnable).pipe(\n Schema.annotate({ title: 'Runnable' }),\n Annotation.FormInputAnnotation.set(false),\n Schema.optional,\n ),\n\n spec: Spec.pipe(Schema.optional),\n\n enabled: Schema.Boolean.pipe(Schema.optional),\n\n /**\n * Runs this trigger on the edge rather than locally.\n * When unset, the trigger runs locally on the client.\n */\n remote: Schema.Boolean.pipe(Schema.annotate({ title: 'Remote' }), Schema.optional),\n\n concurrency: Schema.Number.pipe(\n Schema.annotate({\n title: 'Concurrency',\n default: 1,\n description: 'Maximum number of concurrent invocations of the trigger.',\n }),\n Annotation.FormInputAnnotation.set(false),\n Schema.optional,\n ),\n\n /**\n * Only used for workflowSchema.\n * Specifies the input node in the circuit.\n * @deprecated Remove and enforce a single input node in all compute graphSchema.\n */\n inputNodeId: Schema.String.pipe(\n Schema.annotate({ title: 'Input Node ID' }),\n Annotation.FormInputAnnotation.set(false),\n Schema.optional,\n ),\n\n /**\n * Passed as the input data to the runnable.\n */\n input: InputTemplate.pipe(Annotation.FormInputAnnotation.set(false), Schema.optional),\n }).pipe(Annotation.IconAnnotation.set({ icon: 'ph--lightning--regular', hue: 'yellow' })),\n) {}\n\nexport const make = (props: Obj.MakeProps<typeof Trigger>) => Obj.make(Trigger, props);\n\n/**\n * Checks if a trigger having this spec can be manually invoked (a `direct` or `timer` trigger).\n */\nexport const isManuallyInvokable = (spec?: Spec): boolean => spec?.kind === 'direct' || spec?.kind === 'timer';\n\n/**\n * Runtime state of a trigger.\n */\nexport interface State {\n /**\n * Reference to the trigger object.\n */\n readonly trigger: Ref.Ref<Trigger>;\n\n /**\n * Where is this trigger running.\n */\n readonly environment: 'edge' | 'local';\n\n /**\n * Next scheduled cron execution. Set only for `timer` triggers.\n */\n readonly nextExecution?: Date;\n\n /**\n * Time until which scheduled invocations of this trigger are skipped after a genuine failure.\n * Applies to all trigger kinds. Manual {@link TriggerDispatcher.invokeTrigger} calls bypass it.\n */\n readonly cooldownUntil?: Date;\n\n /**\n * Pending re-invocation requested via {@link Operation.runAgain} ({@link RunAgainError}).\n * Retries are drained at the tail of the invocation queue, ordered by `enqueuedAt`.\n */\n readonly retry?: {\n /**\n * Event to replay on the retry. Re-running is assumed safe with the same input.\n */\n readonly event: TriggerEvent.TriggerEvent;\n\n /**\n * Monotonic sequence number used to order pending retries FIFO at the tail of the queue.\n */\n readonly enqueuedAt: number;\n };\n\n /**\n * Result of the most recent invocation of this trigger.\n */\n readonly lastResult: Exit.Exit<unknown> | null;\n}\n\nexport interface InvokeOptions {\n trigger: Trigger;\n event: TriggerEvent.TriggerEvent;\n}\n\n/**\n * Observes and invokes the triggers registered with the dispatchers (local and edge).\n */\nexport interface Manager {\n /**\n * Triggers actively registered in the dispatcher.\n * Could contain entries for both local and edge triggers, but only the edge ones are actually running.\n */\n readonly triggers: Atom.Atom<readonly State[]>;\n\n readonly localDispatcherEnabled: boolean;\n\n /**\n * Invoke a trigger.\n * Available only for direct and timer triggers.\n * Invocation respects the trigger's concurrency limit.\n * Fails with {@link TriggerDisabledError} for a trigger that is switched off.\n */\n readonly invokeTrigger: (options: InvokeOptions) => Effect.Effect<void, TriggerDisabledError>;\n}\n\n/**\n * Service providing the {@link Manager}.\n */\nexport class ManagerService extends Context.Service<ManagerService, Manager>()(\n '@dxos/compute/Trigger.ManagerService',\n) {}\n\nexport { TriggerDisabledError, TriggerStateNotFoundError } from '../errors.ts';\n"],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import type { SessionConfig } from '@dxos/ai';
|
|
|
6
6
|
import type { Database, Feed, Obj, Ref } from '@dxos/echo';
|
|
7
7
|
import { DXN } from '@dxos/keys';
|
|
8
8
|
import type { ContentBlock } from '@dxos/types';
|
|
9
|
+
import type * as Process from './Process.ts';
|
|
9
10
|
import type * as Trace from './Trace.ts';
|
|
10
11
|
import { Instructions } from './types/index.ts';
|
|
11
12
|
/**
|
|
@@ -98,7 +99,7 @@ export interface Session {
|
|
|
98
99
|
* `runPromise(Effect.forEach(subscribe))`), use {@link Effect.forkDetach} so the
|
|
99
100
|
* stream survives after the parent scope closes; interrupt it on dispose.
|
|
100
101
|
*/
|
|
101
|
-
subscribeEphemeral: () => Stream.Stream<Trace.Message>;
|
|
102
|
+
subscribeEphemeral: (options?: Process.SubscribeEphemeralOptions) => Stream.Stream<Trace.Message>;
|
|
102
103
|
}
|
|
103
104
|
export declare const getSession: (...args: Parameters<Context.Service.Shape<typeof AgentService>['getSession']>) => Effect.Effect<Session, never, AgentService | Database.Service>;
|
|
104
105
|
export declare const hydrate: (...args: Parameters<Context.Service.Shape<typeof AgentService>['hydrate']>) => Effect.Effect<void, never, AgentService>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentService.d.ts","sourceRoot":"","sources":["../../../src/AgentService.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AgentService.d.ts","sourceRoot":"","sources":["../../../src/AgentService.ts"],"names":[],"mappings":"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"}
|