@plexspaces/sdk 0.1.4 → 0.1.7

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.
@@ -0,0 +1,239 @@
1
+ /** Step kind in an OODA agent trajectory. */
2
+ export type AgentStepKind = 'observe' | 'orient' | 'decide' | 'act' | 'tool_call' | 'suspend';
3
+ /**
4
+ * A single recorded step in an agent trajectory.
5
+ *
6
+ * Mirrors Python `AgentStep` dataclass. All timing fields are milliseconds
7
+ * since epoch (Date.now()).
8
+ */
9
+ export interface AgentStep {
10
+ /** Unique ULID-style step identifier. */
11
+ stepId: string;
12
+ /** OODA phase or special step kind. */
13
+ kind: AgentStepKind;
14
+ /** Method or tool name that produced this step. */
15
+ method: string;
16
+ /** Input data passed into the step. */
17
+ input: unknown;
18
+ /** Output produced by the step (undefined until completed). */
19
+ output: unknown;
20
+ /** Epoch-millisecond timestamp when the step started. */
21
+ startedAtMs: number;
22
+ /** Epoch-millisecond timestamp when the step completed (0 if pending). */
23
+ completedAtMs: number;
24
+ /** Wall-clock duration in milliseconds. */
25
+ durationMs: number;
26
+ /** Whether the step succeeded. */
27
+ success: boolean;
28
+ /** Error message if the step failed; undefined on success. */
29
+ error?: string;
30
+ /** Tool name for tool_call steps; undefined otherwise. */
31
+ toolName?: string;
32
+ /** LLM input tokens consumed in this step. */
33
+ inputTokens: number;
34
+ /** LLM output tokens produced in this step. */
35
+ outputTokens: number;
36
+ /** Model identifier used in this step (empty string if N/A). */
37
+ model: string;
38
+ /** Arbitrary key/value metadata attached to this step. */
39
+ metadata: Record<string, unknown>;
40
+ }
41
+ /**
42
+ * Complete agent execution trajectory.
43
+ *
44
+ * Mirrors Python `AgentTrajectory` dataclass. Captured from the first
45
+ * observe/orient/decide/act call through finalizeTrajectory().
46
+ */
47
+ export interface AgentTrajectory {
48
+ /** Unique ULID-style trajectory identifier. */
49
+ trajectoryId: string;
50
+ /** Actor ID of the agent that produced this trajectory. */
51
+ agentActorId: string;
52
+ /** Eval harness run ID (for offline evaluation). */
53
+ evalRunId: string;
54
+ /** Scenario ID (for offline evaluation). */
55
+ scenarioId: string;
56
+ /** Ordered list of recorded steps. */
57
+ steps: AgentStep[];
58
+ /** Lifecycle outcome: 'running' | 'success' | 'failed' | 'suspended' | ... */
59
+ outcome: string;
60
+ /** Human-readable outcome detail. */
61
+ outcomeDetail: string;
62
+ /** Sum of inputTokens across all steps. */
63
+ totalInputTokens: number;
64
+ /** Sum of outputTokens across all steps. */
65
+ totalOutputTokens: number;
66
+ /** Epoch-millisecond timestamp when the trajectory started. */
67
+ startedAtMs: number;
68
+ /** Epoch-millisecond timestamp when the trajectory completed (0 while running). */
69
+ completedAtMs: number;
70
+ /** Total wall-clock duration in milliseconds (0 while running). */
71
+ durationMs: number;
72
+ /** Optional evaluation score attached post-run. */
73
+ score: number;
74
+ /** Arbitrary key/value metadata attached to this trajectory. */
75
+ metadata: Record<string, unknown>;
76
+ }
77
+ /**
78
+ * Configuration for an agent actor's loop.
79
+ *
80
+ * Mirrors the keyword arguments of Python `@agent_actor(...)`.
81
+ */
82
+ export interface AgentConfig {
83
+ /** Maximum OODA iterations before forced stop. 0 = unlimited. */
84
+ maxIterations: number;
85
+ /** Maximum cumulative tokens before forced stop. 0 = unlimited. */
86
+ tokenBudget: number;
87
+ /** Eval run ID for trajectory tagging (empty = not tracked). */
88
+ evalRunId: string;
89
+ /** Scenario ID for trajectory tagging (empty = not tagged). */
90
+ scenarioId: string;
91
+ }
92
+ /**
93
+ * Returns a sensible default AgentConfig.
94
+ *
95
+ * @returns `{ maxIterations: 10, tokenBudget: 0, evalRunId: '', scenarioId: '' }`
96
+ */
97
+ export declare function defaultAgentConfig(): AgentConfig;
98
+ /**
99
+ * Stateful OODA-loop harness for agent actors.
100
+ *
101
+ * Tracks steps, tokens, suspension state, and iteration count. The class is
102
+ * injected into actor instances by `agentActor()`. Call `getTrajectory()` for
103
+ * a live snapshot and `finalizeTrajectory(outcome)` to close the trajectory.
104
+ *
105
+ * Example:
106
+ * ```ts
107
+ * const loop = new AgentLoop('my-actor-id', { maxIterations: 5, tokenBudget: 1000, evalRunId: '', scenarioId: '' });
108
+ * const obs = loop.observe({ query: 'hello' });
109
+ * const plan = loop.orient(obs);
110
+ * loop.act(plan, { inputTokens: 50, outputTokens: 20, model: 'claude-3' });
111
+ * const traj = loop.finalizeTrajectory('success');
112
+ * ```
113
+ */
114
+ export declare class AgentLoop {
115
+ private readonly config;
116
+ private readonly agentActorId;
117
+ private readonly trajectory;
118
+ private _isSuspended;
119
+ private _iterationCount;
120
+ /**
121
+ * @param agentActorId - Actor ID embedded in trajectory metadata.
122
+ * @param config - Agent loop configuration (maxIterations, tokenBudget, etc.)
123
+ */
124
+ constructor(agentActorId: string, config: AgentConfig);
125
+ /**
126
+ * Record an OBSERVE step: gather information from environment, memory, or context.
127
+ *
128
+ * @param input - Raw observation data.
129
+ * @returns The same input (pass-through).
130
+ */
131
+ observe(input: unknown): unknown;
132
+ /**
133
+ * Record an ORIENT step: process observations into a plan or understanding.
134
+ *
135
+ * @param obs - Observation data from the observe step.
136
+ * @returns The same obs (pass-through).
137
+ */
138
+ orient(obs: unknown): unknown;
139
+ /**
140
+ * Record a DECIDE step: select the next action from available options.
141
+ *
142
+ * @param plan - Planning data from the orient step.
143
+ * @returns The same plan (pass-through).
144
+ */
145
+ decide(plan: unknown): unknown;
146
+ /**
147
+ * Record an ACT step: execute the chosen action.
148
+ *
149
+ * @param action - Action data to execute.
150
+ * @param opts - Optional token usage and model metadata.
151
+ * @returns The same action (pass-through).
152
+ */
153
+ act(action: unknown, opts?: {
154
+ inputTokens?: number;
155
+ outputTokens?: number;
156
+ model?: string;
157
+ }): unknown;
158
+ /**
159
+ * Record a TOOL_CALL step: validated tool invocation with arguments and result.
160
+ *
161
+ * @param toolName - Name of the tool invoked.
162
+ * @param args - Arguments passed to the tool.
163
+ * @param result - Result returned by the tool.
164
+ * @param opts - Optional token usage and model metadata.
165
+ * @returns The result value (pass-through).
166
+ */
167
+ toolCall(toolName: string, args: unknown, result: unknown, opts?: {
168
+ inputTokens?: number;
169
+ outputTokens?: number;
170
+ model?: string;
171
+ }): unknown;
172
+ /**
173
+ * Suspend the agent loop, waiting for an external signal (human approval, etc.).
174
+ *
175
+ * After calling this, check `isSuspended` in your run loop and return early.
176
+ *
177
+ * @param reason - Human-readable reason for suspension.
178
+ */
179
+ suspend(reason: string): void;
180
+ /** Whether the agent loop has been suspended via `suspend()`. */
181
+ get isSuspended(): boolean;
182
+ /**
183
+ * Returns true if cumulative token usage meets or exceeds the configured budget.
184
+ * Always returns false when `tokenBudget` is 0 (unlimited).
185
+ */
186
+ budgetExceeded(): boolean;
187
+ /**
188
+ * Returns true if the iteration count meets or exceeds `maxIterations`.
189
+ * Always returns false when `maxIterations` is 0 (unlimited).
190
+ */
191
+ iterationLimitReached(): boolean;
192
+ /** Increment the iteration counter (call once per OODA loop pass). */
193
+ incrementIteration(): void;
194
+ /**
195
+ * Close the trajectory and return the final snapshot.
196
+ *
197
+ * @param outcome - Outcome label (e.g. `'success'`, `'failed'`, `'suspended'`).
198
+ * @param detail - Optional human-readable outcome detail.
199
+ * @returns Completed AgentTrajectory snapshot.
200
+ */
201
+ finalizeTrajectory(outcome: string, detail?: string): AgentTrajectory;
202
+ /**
203
+ * Return a live snapshot of the current trajectory (trajectory is still open).
204
+ *
205
+ * @returns Current AgentTrajectory (shallow copy of steps array).
206
+ */
207
+ getTrajectory(): AgentTrajectory;
208
+ private recordStep;
209
+ private addStep;
210
+ }
211
+ /**
212
+ * Class decorator that injects an `AgentLoop` into actor instances.
213
+ *
214
+ * Mirrors Python `@agent_actor(max_iterations=10, token_budget=4096, ...)`.
215
+ * Purely additive — does not alter existing methods or WIT entry points.
216
+ *
217
+ * The decorator adds:
218
+ * - `_agentLoop: AgentLoop` — private backing field (initialized on first access)
219
+ * - `get agentLoop(): AgentLoop` — public accessor
220
+ *
221
+ * Example:
222
+ * ```ts
223
+ * import { agentActor, PlexSpacesActor } from '@plexspaces/sdk';
224
+ *
225
+ * @agentActor({ maxIterations: 5, tokenBudget: 2048 })
226
+ * class SummaryAgent extends PlexSpacesActor<{ result: string }> {
227
+ * getDefaultState() { return { result: '' }; }
228
+ * run(payload: Record<string, unknown>) {
229
+ * const obs = this.agentLoop.observe(payload);
230
+ * // ...
231
+ * return this.agentLoop.finalizeTrajectory('success');
232
+ * }
233
+ * }
234
+ * ```
235
+ *
236
+ * @param config - Partial AgentConfig; unset fields use defaultAgentConfig() values.
237
+ * @returns ClassDecorator that injects AgentLoop.
238
+ */
239
+ export declare function agentActor(config?: Partial<AgentConfig>): ClassDecorator;
package/dist/agent.js ADDED
@@ -0,0 +1,324 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (C) 2025 PlexSpaces Contributors
3
+ //
4
+ // AgentLoop standalone utility — OODA-loop agent harness for PlexSpaces TypeScript SDK.
5
+ // TypeScript SDK parity with Python sdks/python/plexspaces/agent.py.
6
+ //
7
+ // Provides:
8
+ // - Structured step recording (each observe/orient/decide/act journaled in AgentTrajectory)
9
+ // - Token budget enforcement (budgetExceeded() halts loop when cumulative tokens >= budget)
10
+ // - Iteration limit enforcement (iterationLimitReached() halts loop after N iterations)
11
+ // - Human-in-the-loop suspend (suspend() sets isSuspended to true)
12
+ // - Trajectory capture (finalizeTrajectory / getTrajectory)
13
+ //
14
+ // AgentLoop is a standalone plain class — it is NOT a decorator and does NOT wrap
15
+ // other types. Use agentActor() as a convenience decorator to inject an AgentLoop
16
+ // into actor instances.
17
+ //
18
+ // Usage:
19
+ // import { agentActor, AgentLoop } from '@plexspaces/sdk';
20
+ //
21
+ // @agentActor({ maxIterations: 10, tokenBudget: 4096 })
22
+ // class ResearchAgent extends PlexSpacesActor<ResearchState> {
23
+ // getDefaultState() { return { result: '' }; }
24
+ // run(payload: Record<string, unknown>) {
25
+ // const obs = this.agentLoop.observe(payload);
26
+ // const plan = this.agentLoop.orient(obs);
27
+ // const action = this.agentLoop.decide(plan);
28
+ // const result = this.agentLoop.act(action);
29
+ // return this.agentLoop.finalizeTrajectory('success');
30
+ // }
31
+ // }
32
+ /**
33
+ * Returns a sensible default AgentConfig.
34
+ *
35
+ * @returns `{ maxIterations: 10, tokenBudget: 0, evalRunId: '', scenarioId: '' }`
36
+ */
37
+ export function defaultAgentConfig() {
38
+ return {
39
+ maxIterations: 10,
40
+ tokenBudget: 0,
41
+ evalRunId: '',
42
+ scenarioId: '',
43
+ };
44
+ }
45
+ // ─── Internal helpers ─────────────────────────────────────────────────────────
46
+ /** Returns epoch milliseconds. Uses Date.now() which is safe in all environments. */
47
+ function nowMs() {
48
+ return Date.now();
49
+ }
50
+ /** Generate a lightweight unique ID. Uses Math.random for WASM portability. */
51
+ function newStepId() {
52
+ const ts = nowMs().toString(36);
53
+ const rnd = Math.random().toString(36).slice(2, 9);
54
+ return `step-${ts}-${rnd}`;
55
+ }
56
+ /** Generate a trajectory ID with different prefix. */
57
+ function newTrajectoryId() {
58
+ const ts = nowMs().toString(36);
59
+ const rnd = Math.random().toString(36).slice(2, 9);
60
+ return `traj-${ts}-${rnd}`;
61
+ }
62
+ // ─── AgentLoop ────────────────────────────────────────────────────────────────
63
+ /**
64
+ * Stateful OODA-loop harness for agent actors.
65
+ *
66
+ * Tracks steps, tokens, suspension state, and iteration count. The class is
67
+ * injected into actor instances by `agentActor()`. Call `getTrajectory()` for
68
+ * a live snapshot and `finalizeTrajectory(outcome)` to close the trajectory.
69
+ *
70
+ * Example:
71
+ * ```ts
72
+ * const loop = new AgentLoop('my-actor-id', { maxIterations: 5, tokenBudget: 1000, evalRunId: '', scenarioId: '' });
73
+ * const obs = loop.observe({ query: 'hello' });
74
+ * const plan = loop.orient(obs);
75
+ * loop.act(plan, { inputTokens: 50, outputTokens: 20, model: 'claude-3' });
76
+ * const traj = loop.finalizeTrajectory('success');
77
+ * ```
78
+ */
79
+ export class AgentLoop {
80
+ /**
81
+ * @param agentActorId - Actor ID embedded in trajectory metadata.
82
+ * @param config - Agent loop configuration (maxIterations, tokenBudget, etc.)
83
+ */
84
+ constructor(agentActorId, config) {
85
+ this._isSuspended = false;
86
+ this._iterationCount = 0;
87
+ this.config = { ...config };
88
+ this.agentActorId = agentActorId;
89
+ this.trajectory = {
90
+ trajectoryId: newTrajectoryId(),
91
+ agentActorId,
92
+ evalRunId: config.evalRunId,
93
+ scenarioId: config.scenarioId,
94
+ steps: [],
95
+ outcome: 'running',
96
+ outcomeDetail: '',
97
+ totalInputTokens: 0,
98
+ totalOutputTokens: 0,
99
+ startedAtMs: nowMs(),
100
+ completedAtMs: 0,
101
+ durationMs: 0,
102
+ score: 0,
103
+ metadata: {},
104
+ };
105
+ }
106
+ // ─── Public step methods ───────────────────────────────────────────────────
107
+ /**
108
+ * Record an OBSERVE step: gather information from environment, memory, or context.
109
+ *
110
+ * @param input - Raw observation data.
111
+ * @returns The same input (pass-through).
112
+ */
113
+ observe(input) {
114
+ return this.recordStep('observe', 'observe', input, input);
115
+ }
116
+ /**
117
+ * Record an ORIENT step: process observations into a plan or understanding.
118
+ *
119
+ * @param obs - Observation data from the observe step.
120
+ * @returns The same obs (pass-through).
121
+ */
122
+ orient(obs) {
123
+ return this.recordStep('orient', 'orient', obs, obs);
124
+ }
125
+ /**
126
+ * Record a DECIDE step: select the next action from available options.
127
+ *
128
+ * @param plan - Planning data from the orient step.
129
+ * @returns The same plan (pass-through).
130
+ */
131
+ decide(plan) {
132
+ return this.recordStep('decide', 'decide', plan, plan);
133
+ }
134
+ /**
135
+ * Record an ACT step: execute the chosen action.
136
+ *
137
+ * @param action - Action data to execute.
138
+ * @param opts - Optional token usage and model metadata.
139
+ * @returns The same action (pass-through).
140
+ */
141
+ act(action, opts) {
142
+ return this.recordStep('act', 'act', action, action, opts);
143
+ }
144
+ /**
145
+ * Record a TOOL_CALL step: validated tool invocation with arguments and result.
146
+ *
147
+ * @param toolName - Name of the tool invoked.
148
+ * @param args - Arguments passed to the tool.
149
+ * @param result - Result returned by the tool.
150
+ * @param opts - Optional token usage and model metadata.
151
+ * @returns The result value (pass-through).
152
+ */
153
+ toolCall(toolName, args, result, opts) {
154
+ const started = nowMs();
155
+ const step = {
156
+ stepId: newStepId(),
157
+ kind: 'tool_call',
158
+ method: `tool:${toolName}`,
159
+ input: { name: toolName, arguments: args },
160
+ output: result,
161
+ startedAtMs: started,
162
+ completedAtMs: nowMs(),
163
+ durationMs: 0,
164
+ success: true,
165
+ toolName,
166
+ inputTokens: opts?.inputTokens ?? 0,
167
+ outputTokens: opts?.outputTokens ?? 0,
168
+ model: opts?.model ?? '',
169
+ metadata: {},
170
+ };
171
+ step.durationMs = step.completedAtMs - step.startedAtMs;
172
+ this.addStep(step);
173
+ return result;
174
+ }
175
+ /**
176
+ * Suspend the agent loop, waiting for an external signal (human approval, etc.).
177
+ *
178
+ * After calling this, check `isSuspended` in your run loop and return early.
179
+ *
180
+ * @param reason - Human-readable reason for suspension.
181
+ */
182
+ suspend(reason) {
183
+ this._isSuspended = true;
184
+ this.recordStep('suspend', 'suspend', reason, undefined);
185
+ }
186
+ /** Whether the agent loop has been suspended via `suspend()`. */
187
+ get isSuspended() {
188
+ return this._isSuspended;
189
+ }
190
+ /**
191
+ * Returns true if cumulative token usage meets or exceeds the configured budget.
192
+ * Always returns false when `tokenBudget` is 0 (unlimited).
193
+ */
194
+ budgetExceeded() {
195
+ if (this.config.tokenBudget <= 0)
196
+ return false;
197
+ const used = this.trajectory.totalInputTokens + this.trajectory.totalOutputTokens;
198
+ return used >= this.config.tokenBudget;
199
+ }
200
+ /**
201
+ * Returns true if the iteration count meets or exceeds `maxIterations`.
202
+ * Always returns false when `maxIterations` is 0 (unlimited).
203
+ */
204
+ iterationLimitReached() {
205
+ if (this.config.maxIterations <= 0)
206
+ return false;
207
+ return this._iterationCount >= this.config.maxIterations;
208
+ }
209
+ /** Increment the iteration counter (call once per OODA loop pass). */
210
+ incrementIteration() {
211
+ this._iterationCount++;
212
+ }
213
+ /**
214
+ * Close the trajectory and return the final snapshot.
215
+ *
216
+ * @param outcome - Outcome label (e.g. `'success'`, `'failed'`, `'suspended'`).
217
+ * @param detail - Optional human-readable outcome detail.
218
+ * @returns Completed AgentTrajectory snapshot.
219
+ */
220
+ finalizeTrajectory(outcome, detail = '') {
221
+ const now = nowMs();
222
+ this.trajectory.outcome = outcome;
223
+ this.trajectory.outcomeDetail = detail;
224
+ this.trajectory.completedAtMs = now;
225
+ this.trajectory.durationMs = now - this.trajectory.startedAtMs;
226
+ return { ...this.trajectory, steps: [...this.trajectory.steps] };
227
+ }
228
+ /**
229
+ * Return a live snapshot of the current trajectory (trajectory is still open).
230
+ *
231
+ * @returns Current AgentTrajectory (shallow copy of steps array).
232
+ */
233
+ getTrajectory() {
234
+ return { ...this.trajectory, steps: [...this.trajectory.steps] };
235
+ }
236
+ // ─── Private helpers ───────────────────────────────────────────────────────
237
+ recordStep(kind, method, input, output, opts) {
238
+ const started = nowMs();
239
+ const step = {
240
+ stepId: newStepId(),
241
+ kind,
242
+ method,
243
+ input,
244
+ output,
245
+ startedAtMs: started,
246
+ completedAtMs: nowMs(),
247
+ durationMs: 0,
248
+ success: true,
249
+ inputTokens: opts?.inputTokens ?? 0,
250
+ outputTokens: opts?.outputTokens ?? 0,
251
+ model: opts?.model ?? '',
252
+ metadata: {},
253
+ };
254
+ step.durationMs = step.completedAtMs - step.startedAtMs;
255
+ this.addStep(step);
256
+ return output;
257
+ }
258
+ addStep(step) {
259
+ this.trajectory.steps.push(step);
260
+ this.trajectory.totalInputTokens += step.inputTokens;
261
+ this.trajectory.totalOutputTokens += step.outputTokens;
262
+ }
263
+ }
264
+ // ─── @agentActor decorator ────────────────────────────────────────────────────
265
+ /**
266
+ * Class decorator that injects an `AgentLoop` into actor instances.
267
+ *
268
+ * Mirrors Python `@agent_actor(max_iterations=10, token_budget=4096, ...)`.
269
+ * Purely additive — does not alter existing methods or WIT entry points.
270
+ *
271
+ * The decorator adds:
272
+ * - `_agentLoop: AgentLoop` — private backing field (initialized on first access)
273
+ * - `get agentLoop(): AgentLoop` — public accessor
274
+ *
275
+ * Example:
276
+ * ```ts
277
+ * import { agentActor, PlexSpacesActor } from '@plexspaces/sdk';
278
+ *
279
+ * @agentActor({ maxIterations: 5, tokenBudget: 2048 })
280
+ * class SummaryAgent extends PlexSpacesActor<{ result: string }> {
281
+ * getDefaultState() { return { result: '' }; }
282
+ * run(payload: Record<string, unknown>) {
283
+ * const obs = this.agentLoop.observe(payload);
284
+ * // ...
285
+ * return this.agentLoop.finalizeTrajectory('success');
286
+ * }
287
+ * }
288
+ * ```
289
+ *
290
+ * @param config - Partial AgentConfig; unset fields use defaultAgentConfig() values.
291
+ * @returns ClassDecorator that injects AgentLoop.
292
+ */
293
+ export function agentActor(config) {
294
+ const resolved = { ...defaultAgentConfig(), ...config };
295
+ return function (target) {
296
+ const original = target.prototype;
297
+ // Define _agentLoop lazily on each instance so the constructor is not touched.
298
+ // enumerable: false prevents the loop from appearing in JSON.stringify or for..in.
299
+ Object.defineProperty(original, '_agentLoop', {
300
+ get() {
301
+ const key = '__agentLoopInstance__';
302
+ if (!this[key]) {
303
+ // Use actor id if available (set by framework after construction)
304
+ const actorId = typeof this['actorId'] === 'string'
305
+ ? this['actorId']
306
+ : 'unknown';
307
+ this[key] = new AgentLoop(actorId, resolved);
308
+ }
309
+ return this[key];
310
+ },
311
+ enumerable: false,
312
+ configurable: true,
313
+ });
314
+ // Public accessor — forwards to _agentLoop; not enumerable.
315
+ Object.defineProperty(original, 'agentLoop', {
316
+ get() {
317
+ return this['_agentLoop'];
318
+ },
319
+ enumerable: false,
320
+ configurable: true,
321
+ });
322
+ return target;
323
+ };
324
+ }
package/dist/host.d.ts CHANGED
@@ -133,12 +133,19 @@ export declare class Host {
133
133
  selfId(): string;
134
134
  /**
135
135
  * Spawn a new actor through the framework-owned actor spawn path exposed by the host.
136
+ * Returns the canonical actor ID assigned by the framework — use this ID (not actorName)
137
+ * for all subsequent ask/send/stop calls.
138
+ *
136
139
  * @param moduleRef - Actor type/module reference (must be deployed)
137
- * @param actorId - Unique ID for the new actor (empty = auto-generated ULID)
138
- * @param initConfig - Optional config passed to the new actor's init()
139
- * @returns Spawned actor ID string (may be auto-generated if actorId was empty)
140
+ * @param actorName - Requested name for the new actor. The framework forms the full canonical
141
+ * ID from this name, moduleRef, namespace and node. Pass empty string to
142
+ * let the framework auto-generate a ULID name.
143
+ * @param role - Disambiguation key used ONLY when multiple actors in the same supervisor share
144
+ * the same actor_type (moduleRef). Pass empty string when moduleRef is unique.
145
+ * @param args - Key-value init arguments forwarded to the new actor's init()
146
+ * @returns Canonical actor ID string assigned by the framework
140
147
  */
141
- spawn(moduleRef: string, actorId?: string, initConfig?: unknown): string;
148
+ spawn(moduleRef: string, actorName?: string, role?: string, args?: Record<string, string>): string;
142
149
  /** Stop an actor gracefully */
143
150
  stop(actorId: string): void;
144
151
  /** Bidirectional link */
package/dist/host.js CHANGED
@@ -322,14 +322,21 @@ export class Host {
322
322
  // ========================================================================
323
323
  /**
324
324
  * Spawn a new actor through the framework-owned actor spawn path exposed by the host.
325
+ * Returns the canonical actor ID assigned by the framework — use this ID (not actorName)
326
+ * for all subsequent ask/send/stop calls.
327
+ *
325
328
  * @param moduleRef - Actor type/module reference (must be deployed)
326
- * @param actorId - Unique ID for the new actor (empty = auto-generated ULID)
327
- * @param initConfig - Optional config passed to the new actor's init()
328
- * @returns Spawned actor ID string (may be auto-generated if actorId was empty)
329
+ * @param actorName - Requested name for the new actor. The framework forms the full canonical
330
+ * ID from this name, moduleRef, namespace and node. Pass empty string to
331
+ * let the framework auto-generate a ULID name.
332
+ * @param role - Disambiguation key used ONLY when multiple actors in the same supervisor share
333
+ * the same actor_type (moduleRef). Pass empty string when moduleRef is unique.
334
+ * @param args - Key-value init arguments forwarded to the new actor's init()
335
+ * @returns Canonical actor ID string assigned by the framework
329
336
  */
330
- spawn(moduleRef, actorId = '', initConfig) {
331
- const configBytes = encodeWitPayloadUtf8(initConfig !== undefined ? JSON.stringify(initConfig) : '{}');
332
- const result = safeCall(hostSpawn, moduleRef, actorId, configBytes);
337
+ spawn(moduleRef, actorName = '', role = '', args = {}) {
338
+ const argsJson = Object.keys(args).length > 0 ? JSON.stringify(args) : '{}';
339
+ const result = safeCall(hostSpawn, moduleRef, actorName, role, argsJson);
333
340
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
334
341
  throw new Error(result);
335
342
  }
package/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ export { ActorRouter } from "./router.js";
6
6
  export { defaultRetryConfig, withRetry, type RetryConfig, } from "./workflow.js";
7
7
  export { LeaderWorkerClient, listWorkerNodeIds, } from "./leader_worker.js";
8
8
  export { ActorID } from "./actor_id.js";
9
+ export { AgentLoop, agentActor, defaultAgentConfig, type AgentConfig, type AgentStep, type AgentTrajectory, type AgentStepKind, } from "./agent.js";
package/dist/index.js CHANGED
@@ -12,3 +12,4 @@ export { ActorRouter } from "./router.js";
12
12
  export { defaultRetryConfig, withRetry, } from "./workflow.js";
13
13
  export { LeaderWorkerClient, listWorkerNodeIds, } from "./leader_worker.js";
14
14
  export { ActorID } from "./actor_id.js";
15
+ export { AgentLoop, agentActor, defaultAgentConfig, } from "./agent.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexspaces/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "PlexSpaces TypeScript SDK - build actors with minimal boilerplate (inheritance-based)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",