elasticdash-test 0.1.11-alpha-1 → 0.1.11-alpha-2
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/README.md +256 -11
- package/dist/browser-ui.js +0 -4
- package/dist/browser-ui.js.map +1 -1
- package/dist/capture/event.d.ts +19 -0
- package/dist/capture/event.d.ts.map +1 -0
- package/dist/capture/event.js +2 -0
- package/dist/capture/event.js.map +1 -0
- package/dist/capture/index.d.ts +4 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +4 -0
- package/dist/capture/index.js.map +1 -0
- package/dist/capture/recorder.d.ts +19 -0
- package/dist/capture/recorder.d.ts.map +1 -0
- package/dist/capture/recorder.js +36 -0
- package/dist/capture/recorder.js.map +1 -0
- package/dist/capture/replay.d.ts +20 -0
- package/dist/capture/replay.d.ts.map +1 -0
- package/dist/capture/replay.js +47 -0
- package/dist/capture/replay.js.map +1 -0
- package/dist/cli.js +0 -0
- package/dist/core/agent-state.d.ts +47 -0
- package/dist/core/agent-state.d.ts.map +1 -0
- package/dist/core/agent-state.js +137 -0
- package/dist/core/agent-state.js.map +1 -0
- package/dist/dashboard-server.d.ts +3 -0
- package/dist/dashboard-server.d.ts.map +1 -1
- package/dist/dashboard-server.js +2531 -687
- package/dist/dashboard-server.js.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/interceptors/ai-interceptor.d.ts.map +1 -1
- package/dist/interceptors/ai-interceptor.js +221 -10
- package/dist/interceptors/ai-interceptor.js.map +1 -1
- package/dist/interceptors/db-auto.d.ts +8 -0
- package/dist/interceptors/db-auto.d.ts.map +1 -0
- package/dist/interceptors/db-auto.js +125 -0
- package/dist/interceptors/db-auto.js.map +1 -0
- package/dist/interceptors/db.d.ts +18 -0
- package/dist/interceptors/db.d.ts.map +1 -0
- package/dist/interceptors/db.js +59 -0
- package/dist/interceptors/db.js.map +1 -0
- package/dist/interceptors/http.d.ts +3 -0
- package/dist/interceptors/http.d.ts.map +1 -0
- package/dist/interceptors/http.js +172 -0
- package/dist/interceptors/http.js.map +1 -0
- package/dist/interceptors/side-effects.d.ts +7 -0
- package/dist/interceptors/side-effects.d.ts.map +1 -0
- package/dist/interceptors/side-effects.js +72 -0
- package/dist/interceptors/side-effects.js.map +1 -0
- package/dist/interceptors/tool.d.ts +2 -0
- package/dist/interceptors/tool.d.ts.map +1 -0
- package/dist/interceptors/tool.js +75 -0
- package/dist/interceptors/tool.js.map +1 -0
- package/dist/interceptors/workflow-ai.d.ts +2 -0
- package/dist/interceptors/workflow-ai.d.ts.map +1 -0
- package/dist/interceptors/workflow-ai.js +26 -0
- package/dist/interceptors/workflow-ai.js.map +1 -0
- package/dist/internals/async-storage.d.ts +21 -0
- package/dist/internals/async-storage.d.ts.map +1 -0
- package/dist/internals/async-storage.js +46 -0
- package/dist/internals/async-storage.js.map +1 -0
- package/dist/internals/conditional-recorder.d.ts +21 -0
- package/dist/internals/conditional-recorder.d.ts.map +1 -0
- package/dist/internals/conditional-recorder.js +54 -0
- package/dist/internals/conditional-recorder.js.map +1 -0
- package/dist/internals/node-crypto.d.ts +12 -0
- package/dist/internals/node-crypto.d.ts.map +1 -0
- package/dist/internals/node-crypto.js +38 -0
- package/dist/internals/node-crypto.js.map +1 -0
- package/dist/matchers/index.d.ts +2 -1
- package/dist/matchers/index.d.ts.map +1 -1
- package/dist/matchers/index.js +28 -1
- package/dist/matchers/index.js.map +1 -1
- package/dist/runWorkflowSubprocess.d.ts +14 -0
- package/dist/runWorkflowSubprocess.d.ts.map +1 -0
- package/dist/runWorkflowSubprocess.js +66 -0
- package/dist/runWorkflowSubprocess.js.map +1 -0
- package/dist/tool-runner-worker.d.ts +12 -0
- package/dist/tool-runner-worker.d.ts.map +1 -0
- package/dist/tool-runner-worker.js +52 -0
- package/dist/tool-runner-worker.js.map +1 -0
- package/dist/trace-adapter/context.d.ts +2 -0
- package/dist/trace-adapter/context.d.ts.map +1 -1
- package/dist/trace-adapter/context.js +10 -4
- package/dist/trace-adapter/context.js.map +1 -1
- package/dist/tracing-universal.d.ts +13 -0
- package/dist/tracing-universal.d.ts.map +1 -0
- package/dist/tracing-universal.js +33 -0
- package/dist/tracing-universal.js.map +1 -0
- package/dist/tracing.d.ts +2 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +57 -0
- package/dist/tracing.js.map +1 -0
- package/dist/workflow-runner-worker.d.ts +2 -0
- package/dist/workflow-runner-worker.d.ts.map +1 -0
- package/dist/workflow-runner-worker.js +253 -0
- package/dist/workflow-runner-worker.js.map +1 -0
- package/dist/workflow-runner.d.ts +14 -0
- package/dist/workflow-runner.d.ts.map +1 -0
- package/dist/workflow-runner.js +30 -0
- package/dist/workflow-runner.js.map +1 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -285,6 +285,99 @@ aiTest('user lookup flow', async (ctx) => {
|
|
|
285
285
|
|
|
286
286
|
**Libraries using `https.request` directly** (older versions of some SDKs) are not covered by fetch interception. Use manual `ctx.trace.recordLLMStep()` for those.
|
|
287
287
|
|
|
288
|
+
## Automatic Tool Recording
|
|
289
|
+
|
|
290
|
+
Tool functions are automatically wrapped and recorded when imported and called during workflow execution. This gives you tracing and replay support without any manual instrumentation.
|
|
291
|
+
|
|
292
|
+
### How to use tools
|
|
293
|
+
|
|
294
|
+
Define your tools in `ed_tools.ts` and export them:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
// ed_tools.ts
|
|
298
|
+
import { wrapTool } from 'elasticdash-test'
|
|
299
|
+
|
|
300
|
+
export const chargeCard = wrapTool('chargeCard', async (input: { amount: number; cardToken: string }) => {
|
|
301
|
+
// your actual implementation
|
|
302
|
+
return { success: true, transactionId: 'txn-123' }
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
export const fetchOrderStatus = wrapTool('fetchOrderStatus', async (input: { orderId: string }) => {
|
|
306
|
+
// query your database or API
|
|
307
|
+
return { status: 'shipped', tracking: 'track-456' }
|
|
308
|
+
})
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
In your workflows or agents, simply import and call them — the runner automatically wraps them and records each call:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { chargeCard, fetchOrderStatus } from './ed_tools'
|
|
315
|
+
|
|
316
|
+
export async function checkoutFlow(orderId: string, cardToken: string) {
|
|
317
|
+
const order = await fetchOrderStatus({ orderId })
|
|
318
|
+
const charge = await chargeCard({ amount: order.total, cardToken })
|
|
319
|
+
return { orderId, chargeId: charge.transactionId }
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Each call to a tool is automatically recorded to `ctx.trace` with:
|
|
324
|
+
- Tool name
|
|
325
|
+
- Input arguments
|
|
326
|
+
- Output result
|
|
327
|
+
- Timestamp and duration
|
|
328
|
+
|
|
329
|
+
No `ctx.trace.recordToolCall()` needed — it happens behind the scenes.
|
|
330
|
+
|
|
331
|
+
Runtime behavior by mode:
|
|
332
|
+
- Local/normal execution: wrapped tools run normally (no replay context).
|
|
333
|
+
- Trace capture mode: wrapped tools run normally and are recorded.
|
|
334
|
+
- Replay mode (`Run from here`): pre-checkpoint wrapped calls return historical results and skip tool body execution.
|
|
335
|
+
|
|
336
|
+
### Tool replay and freezing in "Run from here"
|
|
337
|
+
|
|
338
|
+
When you use the dashboard's "Run from here" feature to replay a workflow from a checkpoint:
|
|
339
|
+
|
|
340
|
+
1. **Tools called before the checkpoint** are **frozen** — their recorded results are returned instantly without re-executing the actual function.
|
|
341
|
+
2. **Tools called after the checkpoint** execute normally with live dependencies.
|
|
342
|
+
|
|
343
|
+
This means:
|
|
344
|
+
- If a tool made 3 API calls before the checkpoint, those calls won't happen again on replay.
|
|
345
|
+
- If the tool is slow (waiting for external services), frozen calls are instant.
|
|
346
|
+
- The tool's frozen status is visible in the dashboard with a frozen tag/styling, matching AI step behavior.
|
|
347
|
+
- Side-effect replay is type-checked (`Date.now` vs `Math.random`) and malformed observation timestamps are auto-sanitized.
|
|
348
|
+
|
|
349
|
+
Example:
|
|
350
|
+
```
|
|
351
|
+
Task 1: fetchOrderStatus() ↓ frozen (checkpoint before this)
|
|
352
|
+
│─ API call to orders service ↓ frozen
|
|
353
|
+
└─ Result: { status: shipped } ↓ frozen
|
|
354
|
+
|
|
355
|
+
→ You modify input for Task 2 and re-run
|
|
356
|
+
The fetchOrderStatus API call is skipped; the original result is reused instantly.
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Recording tool calls manually
|
|
360
|
+
|
|
361
|
+
If you need to record tools outside the normal import flow or for providers not in `ed_tools.ts`, use:
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
ctx.trace.recordToolCall({
|
|
365
|
+
name: 'customTool',
|
|
366
|
+
args: { param: 'value' },
|
|
367
|
+
result: { success: true }
|
|
368
|
+
})
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Manual recording is best-effort trace logging. For full replay/freeze semantics in "Run from here" (including frozen tool tags and deterministic replay), run tools through the replay-aware wrapper path (auto-wrapped worker tools or `wrapTool(...)`).
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import { wrapTool } from 'elasticdash-test'
|
|
375
|
+
|
|
376
|
+
export const customTool = wrapTool('customTool', async (input: { param: string }) => {
|
|
377
|
+
return await doWork(input)
|
|
378
|
+
})
|
|
379
|
+
```
|
|
380
|
+
|
|
288
381
|
### Recording flow steps without passing `ctx.trace` (AsyncLocalStorage)
|
|
289
382
|
|
|
290
383
|
The runner now sets a per-test `currentTrace` using Node’s `AsyncLocalStorage`, so your app code can record steps without threading `ctx.trace` through every function. This remains safe under parallel execution.
|
|
@@ -377,12 +470,26 @@ elasticdash dashboard --port 4572 # use custom port
|
|
|
377
470
|
elasticdash dashboard --no-open # skip auto-opening browser
|
|
378
471
|
```
|
|
379
472
|
|
|
380
|
-
The dashboard scans
|
|
381
|
-
-
|
|
382
|
-
-
|
|
383
|
-
-
|
|
384
|
-
|
|
385
|
-
|
|
473
|
+
The dashboard scans your workflow/tool files and displays:
|
|
474
|
+
- If both `.ts` and `.js` versions of a file exist (e.g., `ed_workflows.ts` and `ed_workflows.js`), the dashboard will always use the `.ts` file.
|
|
475
|
+
- If only `.ts` exists, it will be automatically transpiled to `.js` before scanning/importing—no manual build step required.
|
|
476
|
+
- If only `.js` exists, it will be used directly.
|
|
477
|
+
|
|
478
|
+
This means you can write your workflows and tools in TypeScript, and the dashboard will handle transpilation automatically. You do not need to run `tsc` or build manually for dashboard usage.
|
|
479
|
+
|
|
480
|
+
**Example file selection logic:**
|
|
481
|
+
| Scenario | File Used |
|
|
482
|
+
|-------------------------|------------------|
|
|
483
|
+
| Only `ed_workflows.ts` | Transpiled `.ts` |
|
|
484
|
+
| Only `ed_workflows.js` | `.js` |
|
|
485
|
+
| Both exist | `.ts` (preferred)|
|
|
486
|
+
|
|
487
|
+
The dashboard displays:
|
|
488
|
+
- **Function names** — all exported functions in the module
|
|
489
|
+
- **Signatures** — function parameters and return types
|
|
490
|
+
- **Async indicator** — marks async vs sync functions
|
|
491
|
+
- **Source module** — where the function is imported from (if re-exported)
|
|
492
|
+
- **File path** — location of the workflow file
|
|
386
493
|
|
|
387
494
|
Use the search field to filter workflows by:
|
|
388
495
|
- **Name** — find workflow by function name (e.g., `checkoutFlow`)
|
|
@@ -391,16 +498,16 @@ Use the search field to filter workflows by:
|
|
|
391
498
|
|
|
392
499
|
This is useful for discovering available workflows, understanding their signatures, and identifying where functions are defined before calling them in tests.
|
|
393
500
|
|
|
394
|
-
### `
|
|
501
|
+
### `ed_workflows.ts`, `ed_tools.ts`, `ed_agents.ts`
|
|
395
502
|
|
|
396
503
|
These optional files bundle and re-export existing functions from your codebase for use in tests.
|
|
397
504
|
|
|
398
|
-
#### `
|
|
505
|
+
#### `ed_workflows.ts`
|
|
399
506
|
|
|
400
507
|
Re-export workflow functions from your application:
|
|
401
508
|
|
|
402
509
|
```ts
|
|
403
|
-
//
|
|
510
|
+
// ed_workflows.ts
|
|
404
511
|
export { orderWorkflow, refundWorkflow } from './src/workflows'
|
|
405
512
|
export { userLookupFlow } from './src/user-flows'
|
|
406
513
|
```
|
|
@@ -408,7 +515,7 @@ export { userLookupFlow } from './src/user-flows'
|
|
|
408
515
|
Access in tests:
|
|
409
516
|
|
|
410
517
|
```ts
|
|
411
|
-
import { orderWorkflow } from './
|
|
518
|
+
import { orderWorkflow } from './ed_workflows'
|
|
412
519
|
|
|
413
520
|
aiTest('full order workflow', async (ctx) => {
|
|
414
521
|
const result = await orderWorkflow('order-123', 'cust-456')
|
|
@@ -422,7 +529,12 @@ Re-export tool functions that agents or workflows can invoke:
|
|
|
422
529
|
|
|
423
530
|
```ts
|
|
424
531
|
// ed_tools.ts
|
|
425
|
-
|
|
532
|
+
import { wrapTool } from 'elasticdash-test'
|
|
533
|
+
import { chargeCard as chargeCardRaw, fetchOrderStatus as fetchOrderStatusRaw, sendNotification as sendNotificationRaw } from './src/tools'
|
|
534
|
+
|
|
535
|
+
export const chargeCard = wrapTool('chargeCard', chargeCardRaw)
|
|
536
|
+
export const fetchOrderStatus = wrapTool('fetchOrderStatus', fetchOrderStatusRaw)
|
|
537
|
+
export const sendNotification = wrapTool('sendNotification', sendNotificationRaw)
|
|
426
538
|
```
|
|
427
539
|
|
|
428
540
|
#### `ed_agents.ts`
|
|
@@ -444,6 +556,111 @@ The dashboard command will scan these files and display all exported functions w
|
|
|
444
556
|
|
|
445
557
|
---
|
|
446
558
|
|
|
559
|
+
## Agent Mid-Trace Replay
|
|
560
|
+
|
|
561
|
+
ElasticDash supports resuming a long-running agent from any task in its plan — without re-executing already-completed steps. This is useful for:
|
|
562
|
+
|
|
563
|
+
- **Resuming after failures**: If task 3 of 5 fails, fix the issue and re-run from task 3 only.
|
|
564
|
+
- **Pausing for approval**: Capture state after task 2, get human sign-off, then continue.
|
|
565
|
+
- **Debugging in isolation**: Re-run a single task with modified input to diagnose a problem.
|
|
566
|
+
|
|
567
|
+
### How it works
|
|
568
|
+
|
|
569
|
+
Agents are structured as an **AgentPlan** — an ordered list of **AgentTask** objects, each with a tool name, input, and output. When serialized, the plan plus all captured trace events form an **AgentState** that can be saved to disk/database and replayed later.
|
|
570
|
+
|
|
571
|
+
### Quick start
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
import { plannerAgent, executorAgent, resumeAgentFromTrace } from './ed_agents'
|
|
575
|
+
import { serializeAgentState, deserializeAgentState } from 'elasticdash-test'
|
|
576
|
+
import fs from 'node:fs'
|
|
577
|
+
|
|
578
|
+
// 1. Generate a plan
|
|
579
|
+
const plan = await plannerAgent('Show me sales for Q1', { userToken: 'tok-abc' })
|
|
580
|
+
|
|
581
|
+
// 2. Execute the plan (runs all tasks sequentially)
|
|
582
|
+
const completedPlan = await executorAgent(plan)
|
|
583
|
+
|
|
584
|
+
// 3. Serialize and save state (e.g., after partial execution)
|
|
585
|
+
const state = serializeAgentState(completedPlan, [] /* pass recorder.events in worker context */)
|
|
586
|
+
fs.writeFileSync('agent-state.json', JSON.stringify(state, null, 2))
|
|
587
|
+
|
|
588
|
+
// 4. Later: load saved state and resume from task 2 (0-based index 1)
|
|
589
|
+
const savedState = JSON.parse(fs.readFileSync('agent-state.json', 'utf8'))
|
|
590
|
+
const stateToResume = deserializeAgentState({ ...savedState, resumeFromTaskIndex: 1 })
|
|
591
|
+
const resumedPlan = await resumeAgentFromTrace(stateToResume)
|
|
592
|
+
|
|
593
|
+
console.log('Resumed plan status:', resumedPlan.status)
|
|
594
|
+
console.log('Task outputs:', resumedPlan.tasks.map((t) => ({ id: t.id, status: t.status })))
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### AgentState structure
|
|
598
|
+
|
|
599
|
+
```ts
|
|
600
|
+
interface AgentState {
|
|
601
|
+
plan: AgentPlan // Full plan with all tasks (completed and pending)
|
|
602
|
+
trace: WorkflowEvent[] // Captured trace events from previous execution
|
|
603
|
+
resumeFromTaskIndex: number // Zero-based index — tasks before this are loaded from cache
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
interface AgentPlan {
|
|
607
|
+
id: string
|
|
608
|
+
tasks: AgentTask[]
|
|
609
|
+
status: 'planning' | 'executing' | 'completed' | 'failed' | 'paused'
|
|
610
|
+
currentTaskIndex: number
|
|
611
|
+
context: Record<string, unknown>
|
|
612
|
+
metadata: Record<string, unknown>
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
interface AgentTask {
|
|
616
|
+
id: string
|
|
617
|
+
status: 'pending' | 'in-progress' | 'completed' | 'failed'
|
|
618
|
+
description: string
|
|
619
|
+
tool: string // Name of the tool function to invoke
|
|
620
|
+
input: unknown // May contain { $ref: "task-N.output.fieldName" } placeholders
|
|
621
|
+
output?: unknown // Populated after execution
|
|
622
|
+
error?: string
|
|
623
|
+
startedAt?: number
|
|
624
|
+
completedAt?: number
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Task input placeholders
|
|
629
|
+
|
|
630
|
+
Task inputs can reference previous task outputs using `{ $ref: "taskId.output.fieldPath" }`:
|
|
631
|
+
|
|
632
|
+
```ts
|
|
633
|
+
// task-2 uses the embedding produced by task-1
|
|
634
|
+
{
|
|
635
|
+
id: 'task-2',
|
|
636
|
+
tool: 'taskSelectorService',
|
|
637
|
+
input: {
|
|
638
|
+
queryEmbedding: { $ref: 'task-1.output.embedding' },
|
|
639
|
+
topK: 3,
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Placeholders are resolved at execution time by `resolveTaskInput()`.
|
|
645
|
+
|
|
646
|
+
### Dashboard integration
|
|
647
|
+
|
|
648
|
+
When running an agent workflow through the dashboard:
|
|
649
|
+
|
|
650
|
+
1. Agent task observations are **visually highlighted** with a purple background and left border, making them easy to identify.
|
|
651
|
+
2. Each agent observation shows a **T1 / T2 / T3** badge indicating which task it belongs to.
|
|
652
|
+
3. In the observation detail panel, a **"Resume from Task N"** button appears (agent steps only — non-agent steps have no button).
|
|
653
|
+
4. Clicking it calls `/api/resume-agent-from-task` with the serialized `AgentState` and the chosen `taskIndex`, then adds the resumed run as a new trace in the comparison table.
|
|
654
|
+
|
|
655
|
+
### Best practices for resumable agents
|
|
656
|
+
|
|
657
|
+
- **Keep tasks idempotent** where possible — if a task must be re-run, ensure it produces the same result.
|
|
658
|
+
- **Store minimal outputs** — only record what downstream tasks need, not full API responses.
|
|
659
|
+
- **Version your state schema** — if tool interfaces change, old states may need migration.
|
|
660
|
+
- **Use sequential tasks** — the current implementation runs tasks one-by-one; parallel task support is a planned future enhancement.
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
447
664
|
## Project Structure
|
|
448
665
|
|
|
449
666
|
```
|
|
@@ -481,6 +698,34 @@ uninstallAIInterceptor() // restore original fetch when done
|
|
|
481
698
|
|
|
482
699
|
---
|
|
483
700
|
|
|
701
|
+
## Recording Tool Calls Explicitly
|
|
702
|
+
|
|
703
|
+
If you want to ensure tool calls are always recorded in the workflow trace—regardless of how your tools are imported or used—you can use the `recordToolCall` utility provided by this SDK.
|
|
704
|
+
|
|
705
|
+
### How to Use
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
1. Import the function in your tool implementation:
|
|
709
|
+
|
|
710
|
+
```ts
|
|
711
|
+
import { recordToolCall } from 'elasticdash-test'
|
|
712
|
+
|
|
713
|
+
export async function myTool(input: string) {
|
|
714
|
+
// ...tool logic...
|
|
715
|
+
const result = `Hello, ${input}!`
|
|
716
|
+
recordToolCall('myTool', { input }, result)
|
|
717
|
+
return result
|
|
718
|
+
}
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
2. When running under ElasticDash, all calls to `recordToolCall` will be captured in the workflow trace. When running locally or outside the runner, this function is a no-op and will not affect your code.
|
|
722
|
+
|
|
723
|
+
**This approach is robust and works with both imported and global tools.**
|
|
724
|
+
|
|
725
|
+
**Summary:**
|
|
726
|
+
- Use tools as globals (no import) for full traceability and automatic tool call capture.
|
|
727
|
+
- This approach keeps your workflow code clean and enables powerful debugging and analytics in ElasticDash.
|
|
728
|
+
|
|
484
729
|
## Non-Goals
|
|
485
730
|
|
|
486
731
|
This runner intentionally does not support:
|
package/dist/browser-ui.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
|
-
import path from 'node:path';
|
|
3
2
|
import os from 'node:os';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
3
|
import { spawn } from 'node:child_process';
|
|
6
4
|
const defaultHtml = `<!doctype html>
|
|
7
5
|
<html lang="en">
|
|
@@ -149,8 +147,6 @@ const defaultHtml = `<!doctype html>
|
|
|
149
147
|
export async function startBrowserUiServer(opts = {}) {
|
|
150
148
|
const autoOpen = opts.autoOpen !== false;
|
|
151
149
|
let port = opts.port ?? 4571;
|
|
152
|
-
// Ensure base dir for potential static assets (none now)
|
|
153
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
154
150
|
const clients = [];
|
|
155
151
|
const eventBuffer = [];
|
|
156
152
|
const handler = (req, res) => {
|
package/dist/browser-ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-ui.js","sourceRoot":"","sources":["../src/browser-ui.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"browser-ui.js","sourceRoot":"","sources":["../src/browser-ui.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type WorkflowEventType = 'ai' | 'tool' | 'http' | 'db' | 'side_effect';
|
|
2
|
+
export interface WorkflowEvent {
|
|
3
|
+
id: number;
|
|
4
|
+
type: WorkflowEventType;
|
|
5
|
+
name: string;
|
|
6
|
+
input: unknown;
|
|
7
|
+
output: unknown;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
durationMs: number;
|
|
10
|
+
/** Optional: ID of the agent task that produced this event */
|
|
11
|
+
agentTaskId?: string;
|
|
12
|
+
/** Optional: Zero-based index of the agent task that produced this event */
|
|
13
|
+
agentTaskIndex?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface WorkflowTrace {
|
|
16
|
+
traceId: string;
|
|
17
|
+
events: WorkflowEvent[];
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/capture/event.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,aAAa,CAAA;AAE7E,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,iBAAiB,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,aAAa,EAAE,CAAA;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/capture/event.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capture/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/capture/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { WorkflowEvent, WorkflowTrace } from './event.js';
|
|
2
|
+
import type { ReplayController } from './replay.js';
|
|
3
|
+
export declare class TraceRecorder {
|
|
4
|
+
events: WorkflowEvent[];
|
|
5
|
+
private _counter;
|
|
6
|
+
private _sideEffectCounter;
|
|
7
|
+
record(event: WorkflowEvent): void;
|
|
8
|
+
nextId(): number;
|
|
9
|
+
/** Separate counter for Date.now / Math.random — never shares IDs with main events. */
|
|
10
|
+
nextSideEffectId(): number;
|
|
11
|
+
toTrace(traceId?: string): WorkflowTrace;
|
|
12
|
+
}
|
|
13
|
+
export interface CaptureContext {
|
|
14
|
+
recorder: TraceRecorder;
|
|
15
|
+
replay: ReplayController;
|
|
16
|
+
}
|
|
17
|
+
export declare function setCaptureContext(ctx: CaptureContext | undefined): void;
|
|
18
|
+
export declare function getCaptureContext(): CaptureContext | undefined;
|
|
19
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/capture/recorder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,qBAAa,aAAa;IACxB,MAAM,EAAE,aAAa,EAAE,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,kBAAkB,CAAI;IAE9B,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAIlC,MAAM,IAAI,MAAM;IAIhB,uFAAuF;IACvF,gBAAgB,IAAI,MAAM;IAI1B,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa;CAMzC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,MAAM,EAAE,gBAAgB,CAAA;CACzB;AASD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAEvE;AAED,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
export class TraceRecorder {
|
|
4
|
+
events = [];
|
|
5
|
+
_counter = 0;
|
|
6
|
+
_sideEffectCounter = 0;
|
|
7
|
+
record(event) {
|
|
8
|
+
this.events.push(event);
|
|
9
|
+
}
|
|
10
|
+
nextId() {
|
|
11
|
+
return ++this._counter;
|
|
12
|
+
}
|
|
13
|
+
/** Separate counter for Date.now / Math.random — never shares IDs with main events. */
|
|
14
|
+
nextSideEffectId() {
|
|
15
|
+
return ++this._sideEffectCounter;
|
|
16
|
+
}
|
|
17
|
+
toTrace(traceId) {
|
|
18
|
+
return {
|
|
19
|
+
traceId: traceId ?? randomUUID(),
|
|
20
|
+
events: [...this.events],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const g = globalThis;
|
|
25
|
+
const CAPTURE_ALS_KEY = '__elasticdash_capture_als__';
|
|
26
|
+
const captureAls = g[CAPTURE_ALS_KEY] ??
|
|
27
|
+
new AsyncLocalStorage();
|
|
28
|
+
if (!g[CAPTURE_ALS_KEY])
|
|
29
|
+
g[CAPTURE_ALS_KEY] = captureAls;
|
|
30
|
+
export function setCaptureContext(ctx) {
|
|
31
|
+
captureAls.enterWith(ctx);
|
|
32
|
+
}
|
|
33
|
+
export function getCaptureContext() {
|
|
34
|
+
return captureAls.getStore();
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/capture/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,MAAM,OAAO,aAAa;IACxB,MAAM,GAAoB,EAAE,CAAA;IACpB,QAAQ,GAAG,CAAC,CAAA;IACZ,kBAAkB,GAAG,CAAC,CAAA;IAE9B,MAAM,CAAC,KAAoB;QACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAA;IACxB,CAAC;IAED,uFAAuF;IACvF,gBAAgB;QACd,OAAO,EAAE,IAAI,CAAC,kBAAkB,CAAA;IAClC,CAAC;IAED,OAAO,CAAC,OAAgB;QACtB,OAAO;YACL,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE;YAChC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;SACzB,CAAA;IACH,CAAC;CACF;AAOD,MAAM,CAAC,GAAG,UAAqC,CAAA;AAC/C,MAAM,eAAe,GAAG,6BAA6B,CAAA;AACrD,MAAM,UAAU,GACb,CAAC,CAAC,eAAe,CAAmD;IACrE,IAAI,iBAAiB,EAA8B,CAAA;AACrD,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;IAAE,CAAC,CAAC,eAAe,CAAC,GAAG,UAAU,CAAA;AAExD,MAAM,UAAU,iBAAiB,CAAC,GAA+B;IAC/D,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAA;AAC9B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { WorkflowEvent } from './event.js';
|
|
2
|
+
export declare class ReplayController {
|
|
3
|
+
replayMode: boolean;
|
|
4
|
+
checkpoint: number;
|
|
5
|
+
history: WorkflowEvent[];
|
|
6
|
+
private historyMap;
|
|
7
|
+
/** Side effects keyed by their assigned sideEffectId, independent of main event IDs */
|
|
8
|
+
private sideEffectMap;
|
|
9
|
+
constructor(replayMode: boolean, checkpoint: number, history: WorkflowEvent[]);
|
|
10
|
+
shouldReplay(eventId: number): boolean;
|
|
11
|
+
getRecordedEvent(eventId: number): WorkflowEvent | undefined;
|
|
12
|
+
getRecordedResult(eventId: number): unknown;
|
|
13
|
+
/** Returns true if the side effect with this sideEffectId has a recorded value to replay */
|
|
14
|
+
shouldReplaySideEffect(n: number): boolean;
|
|
15
|
+
getSideEffectResult(n: number): unknown;
|
|
16
|
+
getRecordedSideEffectEvent(n: number): WorkflowEvent | undefined;
|
|
17
|
+
shouldReplaySideEffectOfType(n: number, expectedName: string): boolean;
|
|
18
|
+
getSideEffectResultOfType(n: number, expectedName: string): unknown;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/capture/replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,qBAAa,gBAAgB;IAMlB,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,aAAa,EAAE;IAPjC,OAAO,CAAC,UAAU,CAA4B;IAC9C,uFAAuF;IACvF,OAAO,CAAC,aAAa,CAA4B;gBAGxC,UAAU,EAAE,OAAO,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,EAAE;IAQjC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAItC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI5D,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI3C,4FAA4F;IAC5F,sBAAsB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAI1C,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAIvC,0BAA0B,CAAC,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIhE,4BAA4B,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAMtE,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;CAKpE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class ReplayController {
|
|
2
|
+
replayMode;
|
|
3
|
+
checkpoint;
|
|
4
|
+
history;
|
|
5
|
+
historyMap;
|
|
6
|
+
/** Side effects keyed by their assigned sideEffectId, independent of main event IDs */
|
|
7
|
+
sideEffectMap;
|
|
8
|
+
constructor(replayMode, checkpoint, history) {
|
|
9
|
+
this.replayMode = replayMode;
|
|
10
|
+
this.checkpoint = checkpoint;
|
|
11
|
+
this.history = history;
|
|
12
|
+
this.historyMap = new Map(history.map(e => [e.id, e]));
|
|
13
|
+
this.sideEffectMap = new Map(history.filter(e => e.type === 'side_effect').map(e => [e.id, e]));
|
|
14
|
+
}
|
|
15
|
+
shouldReplay(eventId) {
|
|
16
|
+
return this.replayMode && eventId <= this.checkpoint;
|
|
17
|
+
}
|
|
18
|
+
getRecordedEvent(eventId) {
|
|
19
|
+
return this.historyMap.get(eventId);
|
|
20
|
+
}
|
|
21
|
+
getRecordedResult(eventId) {
|
|
22
|
+
return this.historyMap.get(eventId)?.output;
|
|
23
|
+
}
|
|
24
|
+
/** Returns true if the side effect with this sideEffectId has a recorded value to replay */
|
|
25
|
+
shouldReplaySideEffect(n) {
|
|
26
|
+
return this.replayMode && this.sideEffectMap.has(n);
|
|
27
|
+
}
|
|
28
|
+
getSideEffectResult(n) {
|
|
29
|
+
return this.sideEffectMap.get(n)?.output;
|
|
30
|
+
}
|
|
31
|
+
getRecordedSideEffectEvent(n) {
|
|
32
|
+
return this.sideEffectMap.get(n);
|
|
33
|
+
}
|
|
34
|
+
shouldReplaySideEffectOfType(n, expectedName) {
|
|
35
|
+
if (!this.replayMode)
|
|
36
|
+
return false;
|
|
37
|
+
const event = this.sideEffectMap.get(n);
|
|
38
|
+
return !!event && event.type === 'side_effect' && event.name === expectedName;
|
|
39
|
+
}
|
|
40
|
+
getSideEffectResultOfType(n, expectedName) {
|
|
41
|
+
const event = this.sideEffectMap.get(n);
|
|
42
|
+
if (!event || event.type !== 'side_effect' || event.name !== expectedName)
|
|
43
|
+
return undefined;
|
|
44
|
+
return event.output;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../src/capture/replay.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAgB;IAMlB;IACA;IACA;IAPD,UAAU,CAA4B;IAC9C,uFAAuF;IAC/E,aAAa,CAA4B;IAEjD,YACS,UAAmB,EACnB,UAAkB,EAClB,OAAwB;QAFxB,eAAU,GAAV,UAAU,CAAS;QACnB,eAAU,GAAV,UAAU,CAAQ;QAClB,YAAO,GAAP,OAAO,CAAiB;QAE/B,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACtD,IAAI,CAAC,aAAa,GAAG,IAAI,GAAG,CAC1B,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAClE,CAAA;IACH,CAAC;IAED,YAAY,CAAC,OAAe;QAC1B,OAAO,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,IAAI,CAAC,UAAU,CAAA;IACtD,CAAC;IAED,gBAAgB,CAAC,OAAe;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACrC,CAAC;IAED,iBAAiB,CAAC,OAAe;QAC/B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IAC7C,CAAC;IAED,4FAA4F;IAC5F,sBAAsB,CAAC,CAAS;QAC9B,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACrD,CAAC;IAED,mBAAmB,CAAC,CAAS;QAC3B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAA;IAC1C,CAAC;IAED,0BAA0B,CAAC,CAAS;QAClC,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAClC,CAAC;IAED,4BAA4B,CAAC,CAAS,EAAE,YAAoB;QAC1D,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAA;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACvC,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAA;IAC/E,CAAC;IAED,yBAAyB,CAAC,CAAS,EAAE,YAAoB;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO,SAAS,CAAA;QAC3F,OAAO,KAAK,CAAC,MAAM,CAAA;IACrB,CAAC;CACF"}
|
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent state serialization, deserialization, and utility functions.
|
|
3
|
+
*
|
|
4
|
+
* Enables capturing agent plan state during execution and resuming
|
|
5
|
+
* agents from any task in the plan without re-executing completed steps.
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentPlan, AgentState, AgentTask } from '../types/agent.js';
|
|
8
|
+
import type { WorkflowEvent } from '../capture/event.js';
|
|
9
|
+
/**
|
|
10
|
+
* Serializes an agent plan and its captured trace events into an AgentState
|
|
11
|
+
* that can be persisted and later used for resumption.
|
|
12
|
+
*
|
|
13
|
+
* The resumeFromTaskIndex is automatically determined as the index of the
|
|
14
|
+
* first non-completed task. If all tasks are completed, it equals tasks.length.
|
|
15
|
+
*/
|
|
16
|
+
export declare function serializeAgentState(plan: AgentPlan, trace: WorkflowEvent[]): AgentState;
|
|
17
|
+
/**
|
|
18
|
+
* Validates and hydrates an AgentState from a parsed JSON object.
|
|
19
|
+
* Throws if the state is invalid or cannot be safely used for resumption.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deserializeAgentState(raw: unknown): AgentState;
|
|
22
|
+
/**
|
|
23
|
+
* Extracts all completed task outputs into a flat map keyed by task ID.
|
|
24
|
+
* Used for resolving placeholder references in subsequent task inputs.
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractTaskOutputs(plan: AgentPlan): Record<string, unknown>;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves placeholder references in a task input.
|
|
29
|
+
*
|
|
30
|
+
* Placeholders use the form: `{ $ref: "task-N.output.fieldName" }`
|
|
31
|
+
* where "task-N" is a task ID and "fieldName" is a dot-separated path
|
|
32
|
+
* into that task's output.
|
|
33
|
+
*
|
|
34
|
+
* Example:
|
|
35
|
+
* previousOutputs = { "task-1": { userId: "abc" } }
|
|
36
|
+
* input = { $ref: "task-1.output.userId" }
|
|
37
|
+
* → returns "abc"
|
|
38
|
+
*
|
|
39
|
+
* Works recursively on nested objects and arrays.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveTaskInput(input: unknown, previousOutputs: Record<string, unknown>): unknown;
|
|
42
|
+
/**
|
|
43
|
+
* Clones a task and marks it as completed with the given output.
|
|
44
|
+
* Used internally when skipping already-completed tasks during resumption.
|
|
45
|
+
*/
|
|
46
|
+
export declare function markTaskCompleted(task: AgentTask, output: unknown): AgentTask;
|
|
47
|
+
//# sourceMappingURL=agent-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-state.d.ts","sourceRoot":"","sources":["../../src/core/agent-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,UAAU,CASvF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAuC9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvC,OAAO,CAwBT;AAsBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,CAO7E"}
|