@robota-sdk/agent-framework 3.0.0-beta.76 → 3.0.0-beta.78

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,157 @@
1
+ import { Ei as ICommandModule, i as IInteractiveSession, r as InteractiveSession } from "../index-BeYNnJed.js";
2
+ import { ICommandResult, IExecutionResult, IGoalState, IInteractiveSessionEvents, IInteractiveSessionRecord, ITerminalHandoff, IToolSummary, TInteractiveEventName } from "@robota-sdk/agent-interface-transport";
3
+ import { IAIProvider, IUserInteraction, TPermissionMode, TUniversalMessage } from "@robota-sdk/agent-core";
4
+ import { IScriptedProvider, TScriptedTurn, TScriptedTurn as TScriptedTurn$1, createScriptedProvider } from "@robota-sdk/agent-core/testing";
5
+
6
+ //#region src/testing/scripted-session-harness.d.ts
7
+ /** Options for {@link scriptedSession}. Provide exactly one of `turns`, `cassette`, or `record`. */
8
+ interface IScriptedSessionOptions {
9
+ /** Scripted assistant turns replayed deterministically through the real loop. */
10
+ turns?: readonly TScriptedTurn$1[];
11
+ /**
12
+ * CMD-005: answer model-issued questions (AskUserQuestion) programmatically. When set, tool
13
+ * executions receive it as `context.ask`; absent ⇒ the tool reports `unavailable` (headless).
14
+ */
15
+ askHandler?: IUserInteraction['ask'];
16
+ /**
17
+ * Path to a recorded cassette (TEST-005). Replays a real model's captured prompts + tool-use
18
+ * deterministically through the real loop. The workspace path is rewritten/scrubbed automatically.
19
+ */
20
+ cassette?: string;
21
+ /**
22
+ * Record mode (TEST-005): drive the session with a REAL provider and capture every interaction to
23
+ * `toCassette` for later deterministic replay. Used in a one-off keyed record run, not in CI.
24
+ */
25
+ record?: {
26
+ provider: IAIProvider;
27
+ toCassette: string;
28
+ };
29
+ /** Seed files written into the workspace before the session starts (workspace-relative paths). */
30
+ files?: Record<string, string>;
31
+ /** Persist sessions to a real store in the workspace (enables resume/record assertions). */
32
+ persistence?: boolean;
33
+ /**
34
+ * Reuse an existing workspace directory instead of a fresh temp one. Required for multi-session
35
+ * resume/fork (the resumed session must read the same store). The harness does not delete a
36
+ * workspace it did not create.
37
+ */
38
+ cwd?: string;
39
+ /** Resume a persisted session by id (multi-session). Requires `persistence` + the same `cwd`. */
40
+ resumeSessionId?: string;
41
+ /** Fork the resumed session into a new id while restoring its context (multi-session). */
42
+ forkSession?: boolean;
43
+ /** Command modules composed into the session (e.g. the `/goal` module). */
44
+ commandModules?: readonly ICommandModule[];
45
+ /** Permission posture. Defaults to `bypassPermissions` so tools run unattended. */
46
+ permissionMode?: TPermissionMode;
47
+ /** Pre-approved tool names. */
48
+ allowedTools?: string[];
49
+ /** Denied tool names (deny wins over allow). */
50
+ deniedTools?: string[];
51
+ /** Skip AGENTS.md/CLAUDE.md and plugin discovery for determinism. Defaults to `true`. */
52
+ bare?: boolean;
53
+ /** Cap on agentic rounds per submit. */
54
+ maxTurns?: number;
55
+ /** Model override (e.g. when recording against a real provider whose model differs). */
56
+ model?: string;
57
+ /** TERM-001: inject a (fake) terminal-handoff capability to exercise the handoff orchestration. */
58
+ terminalHandoff?: ITerminalHandoff;
59
+ }
60
+ /**
61
+ * A live, scripted, isolated functional-test session. Construct via {@link scriptedSession};
62
+ * always `await dispose()` in a test teardown.
63
+ */
64
+ declare class ScriptedSessionHarness {
65
+ /** Absolute path of the isolated temp workspace. */
66
+ readonly cwd: string;
67
+ /** The real session under test. */
68
+ readonly session: InteractiveSession;
69
+ /** Message arrays of every provider chat() call, in order, for request assertions. */
70
+ readonly requests: TUniversalMessage[][];
71
+ private readonly events;
72
+ private readonly completions;
73
+ private readonly sessionStore?;
74
+ private readonly ownsWorkspace;
75
+ private disposed;
76
+ constructor(options: IScriptedSessionOptions);
77
+ /**
78
+ * Replace the `{{cwd}}` placeholder in scripted tool-call args with the isolated workspace path,
79
+ * so a test can reference absolute workspace paths it cannot know until the harness is built
80
+ * (e.g. `{ filePath: '{{cwd}}/out.txt' }` or a Bash `workingDirectory`). Keeps the harness free of
81
+ * global `process.cwd()` mutation.
82
+ */
83
+ private substituteWorkspacePath;
84
+ private record;
85
+ /** Submit a user prompt and resolve with the completed turn result (rejects on `error`). */
86
+ submit(prompt: string): Promise<IExecutionResult>;
87
+ /**
88
+ * Assign an autonomous goal and resolve with the FINAL stopped goal state once the loop ends
89
+ * (satisfied or a bound). Rejects if the session emits `error` first.
90
+ */
91
+ runGoal(objective: string, options?: {
92
+ maxIterations?: number;
93
+ }): Promise<IGoalState>;
94
+ /** Run a slash command through the real session command pipeline. */
95
+ command(name: string, args?: string): Promise<ICommandResult | null>;
96
+ /**
97
+ * FLOW-002: inject a background/scheduled wake (a non-user `agent-wakeup` turn) and resolve once
98
+ * that turn settles. Mirrors a background task completion or a scheduled fire re-entering the
99
+ * agent loop. Coalesces by `sourceTaskId` exactly as the real wake path does.
100
+ *
101
+ * Returns `null` when the wake was a no-op (coalesced because `sourceTaskId` is already in flight,
102
+ * or the session is shutting down) — no turn runs, so callers must not await one. This prevents the
103
+ * driver from hanging until the test timeout when the wake is dropped.
104
+ */
105
+ wake(instruction: string, sourceTaskId: string): Promise<IExecutionResult | null>;
106
+ /** Resolve with the args of the next `event` (optionally matching `predicate`). */
107
+ awaitEvent<E extends TInteractiveEventName>(event: E, predicate?: (...args: Parameters<IInteractiveSessionEvents[E]>) => boolean): Promise<Parameters<IInteractiveSessionEvents[E]>>;
108
+ private nextSettledTurn;
109
+ /** The current conversation messages. */
110
+ history(): TUniversalMessage[];
111
+ /** The persisted session record (requires `persistence: true`), or undefined. */
112
+ sessionRecord(): IInteractiveSessionRecord | undefined;
113
+ /**
114
+ * Neutral session-log accessor (INFRA-025): the id + full history in the shape analysis
115
+ * tooling consumes. Usage assertions compose it with `summarizeUsageBySource` from
116
+ * `@robota-sdk/agent-session-analytics` in the TEST — the harness itself carries no
117
+ * analytics dependency.
118
+ */
119
+ sessionLog(): {
120
+ id: string;
121
+ history: ReturnType<InteractiveSession['getFullHistory']>;
122
+ };
123
+ /** The real session-log directory the framework writes to (`{cwd}/.robota/logs`). */
124
+ logsDir(): string;
125
+ /** Path of the real JSONL transcript the framework writes for this session. */
126
+ transcriptPath(): string;
127
+ /** Raw contents of the real session transcript (`''` if none was written). */
128
+ transcript(): string;
129
+ /**
130
+ * The real session transcript parsed into structured log entries — the durable record the
131
+ * framework itself writes (`{ timestamp, sessionId, event, ... }` per line). Leverages the
132
+ * system's own logging as a verification surface, not just in-memory state.
133
+ */
134
+ logEntries(): Array<Record<string, unknown>>;
135
+ /** Every tool call the agent made across all completed turns, in order. */
136
+ toolCalls(): IToolSummary[];
137
+ /** Raw collected args of each emission of `event`. */
138
+ emittedEvents<E extends TInteractiveEventName>(event: E): Array<Parameters<IInteractiveSessionEvents[E]>>;
139
+ /** Read a workspace file (UTF-8). */
140
+ readFile(relPath: string): string;
141
+ /** Whether a workspace file exists. */
142
+ exists(relPath: string): boolean;
143
+ /** List workspace files (relative paths), excluding the `.robota` session/log dir. */
144
+ files(): string[];
145
+ /** Shut the session down and remove the temp workspace. Idempotent. */
146
+ dispose(): Promise<void>;
147
+ }
148
+ /** Build a live, isolated, scripted functional-test session (TEST-003). */
149
+ declare function scriptedSession(options: IScriptedSessionOptions): ScriptedSessionHarness;
150
+ //#endregion
151
+ //#region src/testing/create-test-interactive-session.d.ts
152
+ /** Creates a stub IInteractiveSession for use in tests. All methods return sensible defaults.
153
+ * Pass overrides to spy on or replace specific methods. */
154
+ declare function createTestInteractiveSession(overrides?: Partial<IInteractiveSession>): IInteractiveSession;
155
+ //#endregion
156
+ export { type IScriptedProvider, type IScriptedSessionOptions, ScriptedSessionHarness, type TScriptedTurn, createScriptedProvider, createTestInteractiveSession, scriptedSession };
157
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/testing/scripted-session-harness.ts","../../../src/testing/create-test-interactive-session.ts"],"mappings":";;;;;;;UAyDiB,uBAAA;EAOF;EALb,KAAA,YAAiB,eAAA;EAejB;;;;EAVA,UAAA,GAAa,gBAAA;EAYL;;;;EAPR,QAAA;EAqBA;;;;EAhBA,MAAA;IAAW,QAAA,EAAU,WAAA;IAAa,UAAA;EAAA;EA4BlC;EA1BA,KAAA,GAAQ,MAAA;EA4BU;EA1BlB,WAAA;EA0BkC;AAqBpC;;;;EAzCE,GAAA;EAuDqB;EArDrB,eAAA;EA2J8B;EAzJ9B,WAAA;EAmK4E;EAjK5E,cAAA,YAA0B,cAAA;EA2LQ;EAzLlC,cAAA,GAAiB,eAAA;EAsMsC;EApMvD,YAAA;EA4MS;EA1MT,WAAA;EA2M6D;EAzM7D,IAAA;EA0MsB;EAxMtB,QAAA;EAwMW;EAtMX,KAAA;EA0OW;EAxOX,eAAA,GAAkB,gBAAA;AAAA;;;;;cAqBP,sBAAA;EA6QF;EAAA,SA3QA,GAAA;EA4QqC;EAAA,SA1QrC,OAAA,EAAS,kBAAA;EA0Qf;EAAA,SAxQM,QAAA,EAAU,iBAAA;EAAA,iBAEF,MAAA;EAAA,iBACA,WAAA;EAAA,iBACA,YAAA;EAAA,iBACA,aAAA;EAAA,QACT,QAAA;cAEI,OAAA,EAAS,uBAAA;EARF;;;;;;EAAA,QAsFX,uBAAA;EAAA,QAeA,MAAA;EA7FI;EAsGN,MAAA,CAAO,MAAA,WAAiB,OAAA,CAAQ,gBAAA;EAT9B;;;;EAmBF,OAAA,CAAQ,SAAA,UAAmB,OAAA;IAAW,aAAA;EAAA,IAAgC,OAAA,CAAQ,UAAA;EAAxC;EA0B5C,OAAA,CAAQ,IAAA,UAAc,IAAA,YAAY,OAAA,CAAQ,cAAA;EA1BkC;;;;;;;;;EAuCtE,IAAA,CAAK,WAAA,UAAqB,YAAA,WAAuB,OAAA,CAAQ,gBAAA;EAAR;EAOvD,UAAA,WAAqB,qBAAA,EACnB,KAAA,EAAO,CAAA,EACP,SAAA,OAAgB,IAAA,EAAM,UAAA,CAAW,yBAAA,CAA0B,CAAA,iBAC1D,OAAA,CAAQ,UAAA,CAAW,yBAAA,CAA0B,CAAA;EAAA,QAYxC,eAAA;EAfG;EAuCX,OAAA,IAAW,iBAAA;EAtCF;EA2CT,aAAA,IAAiB,yBAAA;EA1CC;;;;;;EAqDlB,UAAA;IAAgB,EAAA;IAAY,OAAA,EAAS,UAAA,CAAW,kBAAA;EAAA;EAhBhD;EAwBA,OAAA;EAnBA;EAwBA,cAAA;EAbA;EAkBA,UAAA;EAlB4B;;;;;EA4B5B,UAAA,IAAc,KAAA,CAAM,MAAA;EAApB;EAQA,SAAA,IAAa,YAAA;EARO;EAapB,aAAA,WAAwB,qBAAA,EACtB,KAAA,EAAO,CAAA,GACN,KAAA,CAAM,UAAA,CAAW,yBAAA,CAA0B,CAAA;EAPjC;EAYb,QAAA,CAAS,OAAA;EAPK;EAYd,MAAA,CAAO,OAAA;EAXE;EAgBT,KAAA;EAfG;EAgCG,OAAA,IAAW,OAAA;AAAA;;iBAcH,eAAA,CAAgB,OAAA,EAAS,uBAAA,GAA0B,sBAAsB;;;;;iBCnZzE,4BAAA,CACd,SAAA,GAAY,OAAA,CAAQ,mBAAA,IACnB,mBAAA"}
@@ -0,0 +1,3 @@
1
+ import{n as e,s as t}from"../interactive-C93XBn4U.js";import{dirname as n,join as r,relative as i,sep as a}from"node:path";import{tmpdir as o}from"node:os";import{existsSync as s,mkdirSync as c,mkdtempSync as l,readFileSync as u,readdirSync as d,rmSync as f,writeFileSync as p}from"node:fs";import{createRecordingProvider as m,createReplayProvider as h,createScriptedProvider as g,createScriptedProvider as _}from"@robota-sdk/agent-core/testing";const v=[`text_delta`,`tool_start`,`tool_end`,`thinking`,`complete`,`interrupted`,`error`,`context_update`,`goal_event`,`turn_source`,`user_message`];var y=class{cwd;session;requests;events=new Map;completions=[];sessionStore;ownsWorkspace;disposed=!1;constructor(i){if(this.ownsWorkspace=i.cwd===void 0,this.cwd=i.cwd??l(r(o(),`robota-fxn-`)),this.ownsWorkspace)for(let[e,t]of Object.entries(i.files??{})){let i=r(this.cwd,e);c(n(i),{recursive:!0}),p(i,t,`utf8`)}if([i.turns,i.cassette,i.record].filter(e=>e!==void 0).length!==1)throw Error("scriptedSession requires exactly one of `turns`, `cassette`, or `record`.");let a;a=i.cassette?h({cassettePath:i.cassette,scrub:[this.cwd],rewriteCwd:this.cwd}):i.record?m({provider:i.record.provider,cassettePath:i.record.toCassette,recordCwd:this.cwd}):_(this.substituteWorkspacePath(i.turns??[])).provider,this.requests=[];let s={...a,chat:(e,t)=>(this.requests.push([...e]),a.chat(e,t))};this.sessionStore=i.persistence?e(this.cwd):void 0,this.session=new t({cwd:this.cwd,provider:s,bare:i.bare??!0,permissionMode:i.permissionMode??`bypassPermissions`,...i.allowedTools?{allowedTools:i.allowedTools}:{},...i.deniedTools?{deniedTools:i.deniedTools}:{},...this.sessionStore?{sessionStore:this.sessionStore}:{},...i.resumeSessionId?{resumeSessionId:i.resumeSessionId}:{},...i.forkSession?{forkSession:i.forkSession}:{},...i.commandModules?{commandModules:i.commandModules}:{},...i.maxTurns===void 0?{}:{maxTurns:i.maxTurns},...i.model===void 0?{}:{model:i.model},...i.terminalHandoff?{terminalHandoff:i.terminalHandoff}:{},...i.askHandler?{askHandler:i.askHandler}:{}});for(let e of v)this.session.on(e,((...t)=>{this.record(e,t),(e===`complete`||e===`interrupted`)&&this.completions.push(t[0])}))}substituteWorkspacePath(e){return e.map(e=>`toolCalls`in e?{toolCalls:e.toolCalls.map(e=>({name:e.name,args:JSON.parse(JSON.stringify(e.args).split(`{{cwd}}`).join(this.cwd))}))}:e)}record(e,t){let n=this.events.get(e)??[];n.push(t),this.events.set(e,n)}async submit(e){let t=this.nextSettledTurn();return await this.session.submit(e),t}async runGoal(e,t={}){let n=new Promise((e,t)=>{let n=t=>{t.type===`goal_stopped`&&(i(),e(t.goal))},r=e=>{i(),t(e)},i=()=>{this.session.off(`goal_event`,n),this.session.off(`error`,r)};this.session.on(`goal_event`,n),this.session.on(`error`,r)});return await this.session.setGoal(e,t.maxIterations?{maxIterations:t.maxIterations}:{}),n}command(e,t=``){return this.session.executeCommand(e,t)}async wake(e,t){return this.session.requestWakeup(e,t)?this.nextSettledTurn():null}awaitEvent(e,t){return new Promise(n=>{let r=((...i)=>{let a=i;t&&!t(...a)||(this.session.off(e,r),n(a))});this.session.on(e,r)})}nextSettledTurn(){return new Promise((e,t)=>{let n=t=>{i(),e(t)},r=e=>{i(),t(e)},i=()=>{this.session.off(`complete`,n),this.session.off(`interrupted`,n),this.session.off(`error`,r)};this.session.on(`complete`,n),this.session.on(`interrupted`,n),this.session.on(`error`,r)})}history(){return this.session.getMessages()}sessionRecord(){if(this.sessionStore)return this.sessionStore.load(this.session.getSession().getSessionId())}sessionLog(){return{id:this.session.getSession().getSessionId(),history:this.session.getFullHistory()}}logsDir(){return r(this.cwd,`.robota`,`logs`)}transcriptPath(){return r(this.logsDir(),`${this.session.getSession().getSessionId()}.jsonl`)}transcript(){let e=this.transcriptPath();return s(e)?u(e,`utf8`):``}logEntries(){return this.transcript().split(`
2
+ `).filter(e=>e.trim().length>0).map(e=>JSON.parse(e))}toolCalls(){return this.completions.flatMap(e=>e.toolSummaries)}emittedEvents(e){return this.events.get(e)??[]}readFile(e){return u(r(this.cwd,e),`utf8`)}exists(e){return s(r(this.cwd,e))}files(){let e=[],t=n=>{for(let o of d(n,{withFileTypes:!0})){if(o.name===`.robota`)continue;let s=r(n,o.name);o.isDirectory()?t(s):e.push(i(this.cwd,s).split(a).join(`/`))}};return t(this.cwd),e.sort()}async dispose(){this.disposed||(this.disposed=!0,await this.session.shutdown({reason:`other`,message:`functional test complete`}),this.ownsWorkspace&&f(this.cwd,{recursive:!0,force:!0,maxRetries:5,retryDelay:20}))}};function b(e){return new y(e)}const x={usedTokens:0,maxTokens:2e5,usedPercentage:0,remainingPercentage:100},S={sessionId:`test-session-id`,updatedAt:new Date().toISOString(),entries:[]},C={id:`test-goal`,objective:`test goal`,status:`active`,iterations:0,maxIterations:25,startedAt:new Date().toISOString(),progress:[]},w={id:``,parentSessionId:`test-session-id`,waitPolicy:`wait_all`,taskIds:[],status:`completed`,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),results:[]};function T(e){return{submit:()=>Promise.resolve(),abort:()=>{},cancelQueue:()=>{},shutdown:()=>Promise.resolve(),isExecuting:()=>!1,getPendingPrompt:()=>null,getMessages:()=>[],getContextState:()=>({...x}),getSession:()=>({getSessionId:()=>`test-session-id`}),getCwd:()=>`/workspace`,executeCommand:()=>Promise.resolve(null),listCommands:()=>[],on:()=>{},off:()=>{},listBackgroundTasks:()=>[],getBackgroundTask:()=>void 0,cancelBackgroundTask:()=>Promise.resolve(),closeBackgroundTask:()=>Promise.resolve(),sendBackgroundTask:()=>Promise.resolve(),readBackgroundTaskLog:()=>Promise.resolve({taskId:``,lines:[]}),listBackgroundJobGroups:()=>[],getBackgroundJobGroup:()=>void 0,createBackgroundJobGroup:()=>({...w}),waitBackgroundJobGroup:()=>Promise.resolve({...w}),getExecutionWorkspaceSnapshot:()=>({...S}),listAgentDefinitions:()=>[],listAgentJobs:()=>[],spawnAgentJob:()=>Promise.resolve({id:`agent_1`,type:`general-purpose`,label:`general-purpose`,parentSessionId:`test-session-id`,status:`running`,mode:`background`,depth:1,cwd:`/workspace`,promptPreview:``,updatedAt:new Date().toISOString()}),sendAgentJob:()=>Promise.resolve(),cancelAgentJob:()=>Promise.resolve(),closeAgentJob:()=>Promise.resolve(),setGoal:()=>Promise.resolve({...C}),getGoalState:()=>null,cancelGoal:()=>null,...e}}export{y as ScriptedSessionHarness,g as createScriptedProvider,T as createTestInteractiveSession,b as scriptedSession};
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["createScriptedProvider"],"sources":["../../../src/testing/scripted-session-harness.ts","../../../src/testing/create-test-interactive-session.ts"],"sourcesContent":["/**\n * TEST-003: framework-level functional session harness.\n *\n * Builds a REAL {@link InteractiveSession} — real agent loop, builtin tools, persistence, events —\n * driven by the deterministic scripted provider (no CLI, no network, no live LLM), in an isolated\n * temp workspace. This is the agent's standard way to prove a framework capability actually works\n * end to end; the CLI is a thin wrapper and must not be the place feature behaviour is verified.\n *\n * The kit is organized for long-term growth: a builder ({@link scriptedSession}), composable\n * drivers ({@link ScriptedSessionHarness.submit}/{@link ScriptedSessionHarness.runGoal}/\n * {@link ScriptedSessionHarness.awaitEvent}), and inspectors (history, session record, files,\n * tool calls, events). New capability drivers/inspectors are added as methods without breaking\n * callers. Exported only via `@robota-sdk/agent-framework/testing`; never import from runtime code.\n */\n\nimport {\n mkdtempSync,\n mkdirSync,\n writeFileSync,\n readFileSync,\n readdirSync,\n rmSync,\n existsSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname, join, relative, sep } from 'node:path';\n\nimport {\n createScriptedProvider,\n createReplayProvider,\n createRecordingProvider,\n} from '@robota-sdk/agent-core/testing';\n\nimport { InteractiveSession } from '../interactive/index.js';\nimport { createProjectSessionStore } from '../interactive/index.js';\n\nimport type { ICommandModule } from '../command-api/index.js';\nimport type {\n IAIProvider,\n IUserInteraction,\n TPermissionMode,\n TUniversalMessage,\n} from '@robota-sdk/agent-core';\nimport type { TScriptedTurn } from '@robota-sdk/agent-core/testing';\nimport type {\n ICommandResult,\n IExecutionResult,\n IGoalState,\n IInteractiveSessionEvents,\n IInteractiveSessionRecord,\n IInteractiveSessionStore,\n ITerminalHandoff,\n IToolSummary,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-transport';\n\n/** Options for {@link scriptedSession}. Provide exactly one of `turns`, `cassette`, or `record`. */\nexport interface IScriptedSessionOptions {\n /** Scripted assistant turns replayed deterministically through the real loop. */\n turns?: readonly TScriptedTurn[];\n /**\n * CMD-005: answer model-issued questions (AskUserQuestion) programmatically. When set, tool\n * executions receive it as `context.ask`; absent ⇒ the tool reports `unavailable` (headless).\n */\n askHandler?: IUserInteraction['ask'];\n /**\n * Path to a recorded cassette (TEST-005). Replays a real model's captured prompts + tool-use\n * deterministically through the real loop. The workspace path is rewritten/scrubbed automatically.\n */\n cassette?: string;\n /**\n * Record mode (TEST-005): drive the session with a REAL provider and capture every interaction to\n * `toCassette` for later deterministic replay. Used in a one-off keyed record run, not in CI.\n */\n record?: { provider: IAIProvider; toCassette: string };\n /** Seed files written into the workspace before the session starts (workspace-relative paths). */\n files?: Record<string, string>;\n /** Persist sessions to a real store in the workspace (enables resume/record assertions). */\n persistence?: boolean;\n /**\n * Reuse an existing workspace directory instead of a fresh temp one. Required for multi-session\n * resume/fork (the resumed session must read the same store). The harness does not delete a\n * workspace it did not create.\n */\n cwd?: string;\n /** Resume a persisted session by id (multi-session). Requires `persistence` + the same `cwd`. */\n resumeSessionId?: string;\n /** Fork the resumed session into a new id while restoring its context (multi-session). */\n forkSession?: boolean;\n /** Command modules composed into the session (e.g. the `/goal` module). */\n commandModules?: readonly ICommandModule[];\n /** Permission posture. Defaults to `bypassPermissions` so tools run unattended. */\n permissionMode?: TPermissionMode;\n /** Pre-approved tool names. */\n allowedTools?: string[];\n /** Denied tool names (deny wins over allow). */\n deniedTools?: string[];\n /** Skip AGENTS.md/CLAUDE.md and plugin discovery for determinism. Defaults to `true`. */\n bare?: boolean;\n /** Cap on agentic rounds per submit. */\n maxTurns?: number;\n /** Model override (e.g. when recording against a real provider whose model differs). */\n model?: string;\n /** TERM-001: inject a (fake) terminal-handoff capability to exercise the handoff orchestration. */\n terminalHandoff?: ITerminalHandoff;\n}\n\nconst COLLECTED_EVENTS: readonly TInteractiveEventName[] = [\n 'text_delta',\n 'tool_start',\n 'tool_end',\n 'thinking',\n 'complete',\n 'interrupted',\n 'error',\n 'context_update',\n 'goal_event',\n 'turn_source',\n 'user_message',\n];\n\n/**\n * A live, scripted, isolated functional-test session. Construct via {@link scriptedSession};\n * always `await dispose()` in a test teardown.\n */\nexport class ScriptedSessionHarness {\n /** Absolute path of the isolated temp workspace. */\n readonly cwd: string;\n /** The real session under test. */\n readonly session: InteractiveSession;\n /** Message arrays of every provider chat() call, in order, for request assertions. */\n readonly requests: TUniversalMessage[][];\n\n private readonly events = new Map<TInteractiveEventName, unknown[][]>();\n private readonly completions: IExecutionResult[] = [];\n private readonly sessionStore?: IInteractiveSessionStore;\n private readonly ownsWorkspace: boolean;\n private disposed = false;\n\n constructor(options: IScriptedSessionOptions) {\n this.ownsWorkspace = options.cwd === undefined;\n this.cwd = options.cwd ?? mkdtempSync(join(tmpdir(), 'robota-fxn-'));\n if (this.ownsWorkspace) {\n for (const [relPath, content] of Object.entries(options.files ?? {})) {\n const abs = join(this.cwd, relPath);\n mkdirSync(dirname(abs), { recursive: true });\n writeFileSync(abs, content, 'utf8');\n }\n }\n\n const modes = [options.turns, options.cassette, options.record].filter(\n (mode) => mode !== undefined,\n );\n if (modes.length !== 1) {\n throw new Error('scriptedSession requires exactly one of `turns`, `cassette`, or `record`.');\n }\n let base: IAIProvider;\n if (options.cassette) {\n base = createReplayProvider({\n cassettePath: options.cassette,\n scrub: [this.cwd],\n rewriteCwd: this.cwd,\n });\n } else if (options.record) {\n base = createRecordingProvider({\n provider: options.record.provider,\n cassettePath: options.record.toCassette,\n recordCwd: this.cwd,\n });\n } else {\n base = createScriptedProvider(this.substituteWorkspacePath(options.turns ?? [])).provider;\n }\n // Capture every request uniformly (works for both scripted and cassette providers).\n this.requests = [];\n const provider: IAIProvider = {\n ...base,\n chat: (messages, chatOptions) => {\n this.requests.push([...messages]);\n return base.chat(messages, chatOptions);\n },\n };\n\n this.sessionStore = options.persistence ? createProjectSessionStore(this.cwd) : undefined;\n\n this.session = new InteractiveSession({\n cwd: this.cwd,\n provider,\n bare: options.bare ?? true,\n permissionMode: options.permissionMode ?? 'bypassPermissions',\n ...(options.allowedTools ? { allowedTools: options.allowedTools } : {}),\n ...(options.deniedTools ? { deniedTools: options.deniedTools } : {}),\n ...(this.sessionStore ? { sessionStore: this.sessionStore } : {}),\n ...(options.resumeSessionId ? { resumeSessionId: options.resumeSessionId } : {}),\n ...(options.forkSession ? { forkSession: options.forkSession } : {}),\n ...(options.commandModules ? { commandModules: options.commandModules } : {}),\n ...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),\n ...(options.model !== undefined ? { model: options.model } : {}),\n ...(options.terminalHandoff ? { terminalHandoff: options.terminalHandoff } : {}),\n ...(options.askHandler ? { askHandler: options.askHandler } : {}),\n });\n\n for (const name of COLLECTED_EVENTS) {\n this.session.on(name, ((...args: unknown[]) => {\n this.record(name, args);\n if (name === 'complete' || name === 'interrupted') {\n this.completions.push(args[0] as IExecutionResult);\n }\n }) as IInteractiveSessionEvents[typeof name]);\n }\n }\n\n /**\n * Replace the `{{cwd}}` placeholder in scripted tool-call args with the isolated workspace path,\n * so a test can reference absolute workspace paths it cannot know until the harness is built\n * (e.g. `{ filePath: '{{cwd}}/out.txt' }` or a Bash `workingDirectory`). Keeps the harness free of\n * global `process.cwd()` mutation.\n */\n private substituteWorkspacePath(turns: readonly TScriptedTurn[]): TScriptedTurn[] {\n return turns.map((turn) => {\n if (!('toolCalls' in turn)) return turn;\n return {\n toolCalls: turn.toolCalls.map((call) => ({\n name: call.name,\n args: JSON.parse(JSON.stringify(call.args).split('{{cwd}}').join(this.cwd)) as Record<\n string,\n unknown\n >,\n })),\n };\n });\n }\n\n private record(name: TInteractiveEventName, args: unknown[]): void {\n const bucket = this.events.get(name) ?? [];\n bucket.push(args);\n this.events.set(name, bucket);\n }\n\n // ── Drivers ────────────────────────────────────────────────\n\n /** Submit a user prompt and resolve with the completed turn result (rejects on `error`). */\n async submit(prompt: string): Promise<IExecutionResult> {\n const settled = this.nextSettledTurn();\n await this.session.submit(prompt);\n return settled;\n }\n\n /**\n * Assign an autonomous goal and resolve with the FINAL stopped goal state once the loop ends\n * (satisfied or a bound). Rejects if the session emits `error` first.\n */\n async runGoal(objective: string, options: { maxIterations?: number } = {}): Promise<IGoalState> {\n const stopped = new Promise<IGoalState>((resolve, reject) => {\n const onGoal = (event: { type: string; goal: IGoalState }): void => {\n if (event.type !== 'goal_stopped') return;\n cleanup();\n resolve(event.goal);\n };\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n const cleanup = (): void => {\n this.session.off('goal_event', onGoal as IInteractiveSessionEvents['goal_event']);\n this.session.off('error', onError);\n };\n this.session.on('goal_event', onGoal as IInteractiveSessionEvents['goal_event']);\n this.session.on('error', onError);\n });\n await this.session.setGoal(\n objective,\n options.maxIterations ? { maxIterations: options.maxIterations } : {},\n );\n return stopped;\n }\n\n /** Run a slash command through the real session command pipeline. */\n command(name: string, args = ''): Promise<ICommandResult | null> {\n return this.session.executeCommand(name, args);\n }\n\n /**\n * FLOW-002: inject a background/scheduled wake (a non-user `agent-wakeup` turn) and resolve once\n * that turn settles. Mirrors a background task completion or a scheduled fire re-entering the\n * agent loop. Coalesces by `sourceTaskId` exactly as the real wake path does.\n *\n * Returns `null` when the wake was a no-op (coalesced because `sourceTaskId` is already in flight,\n * or the session is shutting down) — no turn runs, so callers must not await one. This prevents the\n * driver from hanging until the test timeout when the wake is dropped.\n */\n async wake(instruction: string, sourceTaskId: string): Promise<IExecutionResult | null> {\n const queued = this.session.requestWakeup(instruction, sourceTaskId);\n if (!queued) return null;\n return this.nextSettledTurn();\n }\n\n /** Resolve with the args of the next `event` (optionally matching `predicate`). */\n awaitEvent<E extends TInteractiveEventName>(\n event: E,\n predicate?: (...args: Parameters<IInteractiveSessionEvents[E]>) => boolean,\n ): Promise<Parameters<IInteractiveSessionEvents[E]>> {\n return new Promise((resolve) => {\n const handler = ((...args: unknown[]) => {\n const typed = args as Parameters<IInteractiveSessionEvents[E]>;\n if (predicate && !predicate(...typed)) return;\n this.session.off(event, handler);\n resolve(typed);\n }) as IInteractiveSessionEvents[E];\n this.session.on(event, handler);\n });\n }\n\n private nextSettledTurn(): Promise<IExecutionResult> {\n return new Promise((resolve, reject) => {\n const onComplete = (result: IExecutionResult): void => {\n cleanup();\n resolve(result);\n };\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n const cleanup = (): void => {\n this.session.off('complete', onComplete);\n this.session.off('interrupted', onComplete);\n this.session.off('error', onError);\n };\n this.session.on('complete', onComplete);\n this.session.on('interrupted', onComplete);\n this.session.on('error', onError);\n });\n }\n\n // ── Inspectors ─────────────────────────────────────────────\n\n /** The current conversation messages. */\n history(): TUniversalMessage[] {\n return this.session.getMessages();\n }\n\n /** The persisted session record (requires `persistence: true`), or undefined. */\n sessionRecord(): IInteractiveSessionRecord | undefined {\n if (!this.sessionStore) return undefined;\n return this.sessionStore.load(this.session.getSession().getSessionId());\n }\n\n /**\n * Neutral session-log accessor (INFRA-025): the id + full history in the shape analysis\n * tooling consumes. Usage assertions compose it with `summarizeUsageBySource` from\n * `@robota-sdk/agent-session-analytics` in the TEST — the harness itself carries no\n * analytics dependency.\n */\n sessionLog(): { id: string; history: ReturnType<InteractiveSession['getFullHistory']> } {\n return {\n id: this.session.getSession().getSessionId(),\n history: this.session.getFullHistory(),\n };\n }\n\n /** The real session-log directory the framework writes to (`{cwd}/.robota/logs`). */\n logsDir(): string {\n return join(this.cwd, '.robota', 'logs');\n }\n\n /** Path of the real JSONL transcript the framework writes for this session. */\n transcriptPath(): string {\n return join(this.logsDir(), `${this.session.getSession().getSessionId()}.jsonl`);\n }\n\n /** Raw contents of the real session transcript (`''` if none was written). */\n transcript(): string {\n const path = this.transcriptPath();\n return existsSync(path) ? readFileSync(path, 'utf8') : '';\n }\n\n /**\n * The real session transcript parsed into structured log entries — the durable record the\n * framework itself writes (`{ timestamp, sessionId, event, ... }` per line). Leverages the\n * system's own logging as a verification surface, not just in-memory state.\n */\n logEntries(): Array<Record<string, unknown>> {\n return this.transcript()\n .split('\\n')\n .filter((line) => line.trim().length > 0)\n .map((line) => JSON.parse(line) as Record<string, unknown>);\n }\n\n /** Every tool call the agent made across all completed turns, in order. */\n toolCalls(): IToolSummary[] {\n return this.completions.flatMap((result) => result.toolSummaries);\n }\n\n /** Raw collected args of each emission of `event`. */\n emittedEvents<E extends TInteractiveEventName>(\n event: E,\n ): Array<Parameters<IInteractiveSessionEvents[E]>> {\n return (this.events.get(event) ?? []) as Array<Parameters<IInteractiveSessionEvents[E]>>;\n }\n\n /** Read a workspace file (UTF-8). */\n readFile(relPath: string): string {\n return readFileSync(join(this.cwd, relPath), 'utf8');\n }\n\n /** Whether a workspace file exists. */\n exists(relPath: string): boolean {\n return existsSync(join(this.cwd, relPath));\n }\n\n /** List workspace files (relative paths), excluding the `.robota` session/log dir. */\n files(): string[] {\n const out: string[] = [];\n const walk = (dir: string): void => {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === '.robota') continue;\n const abs = join(dir, entry.name);\n if (entry.isDirectory()) walk(abs);\n else out.push(relative(this.cwd, abs).split(sep).join('/'));\n }\n };\n walk(this.cwd);\n return out.sort();\n }\n\n // ── Lifecycle ──────────────────────────────────────────────\n\n /** Shut the session down and remove the temp workspace. Idempotent. */\n async dispose(): Promise<void> {\n if (this.disposed) return;\n this.disposed = true;\n await this.session.shutdown({ reason: 'other', message: 'functional test complete' });\n // Only remove a workspace this harness created — a shared/injected `cwd` (resume/fork) is the\n // caller's to clean up. `maxRetries` rides out the occasional ENOTEMPTY race when a just-written\n // file (e.g. a tool wrote into the workspace) is still settling at teardown.\n if (this.ownsWorkspace) {\n rmSync(this.cwd, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });\n }\n }\n}\n\n/** Build a live, isolated, scripted functional-test session (TEST-003). */\nexport function scriptedSession(options: IScriptedSessionOptions): ScriptedSessionHarness {\n return new ScriptedSessionHarness(options);\n}\n","import type { IInteractiveSession } from '../interactive/i-interactive-session.js';\n\nconst EMPTY_CONTEXT_STATE = {\n usedTokens: 0,\n maxTokens: 200000,\n usedPercentage: 0,\n remainingPercentage: 100,\n};\n\nconst EMPTY_EXECUTION_WORKSPACE = {\n sessionId: 'test-session-id',\n updatedAt: new Date().toISOString(),\n entries: [] as [],\n};\n\nconst EMPTY_GOAL_STATE = {\n id: 'test-goal',\n objective: 'test goal',\n status: 'active' as const,\n iterations: 0,\n maxIterations: 25,\n startedAt: new Date().toISOString(),\n progress: [] as [],\n};\n\nconst EMPTY_BACKGROUND_GROUP = {\n id: '',\n parentSessionId: 'test-session-id',\n waitPolicy: 'wait_all' as const,\n taskIds: [],\n status: 'completed' as const,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n results: [],\n};\n\n/** Creates a stub IInteractiveSession for use in tests. All methods return sensible defaults.\n * Pass overrides to spy on or replace specific methods. */\nexport function createTestInteractiveSession(\n overrides?: Partial<IInteractiveSession>,\n): IInteractiveSession {\n const base: IInteractiveSession = {\n submit: () => Promise.resolve(),\n abort: () => {},\n cancelQueue: () => {},\n shutdown: () => Promise.resolve(),\n isExecuting: () => false,\n getPendingPrompt: () => null,\n getMessages: () => [],\n getContextState: () => ({ ...EMPTY_CONTEXT_STATE }),\n getSession: () => ({ getSessionId: () => 'test-session-id' }),\n getCwd: () => '/workspace',\n executeCommand: () => Promise.resolve(null),\n listCommands: () => [],\n on: () => {},\n off: () => {},\n listBackgroundTasks: () => [],\n getBackgroundTask: () => undefined,\n cancelBackgroundTask: () => Promise.resolve(),\n closeBackgroundTask: () => Promise.resolve(),\n sendBackgroundTask: () => Promise.resolve(),\n readBackgroundTaskLog: () => Promise.resolve({ taskId: '', lines: [] }),\n listBackgroundJobGroups: () => [],\n getBackgroundJobGroup: () => undefined,\n createBackgroundJobGroup: () => ({ ...EMPTY_BACKGROUND_GROUP }),\n waitBackgroundJobGroup: () => Promise.resolve({ ...EMPTY_BACKGROUND_GROUP }),\n getExecutionWorkspaceSnapshot: () => ({ ...EMPTY_EXECUTION_WORKSPACE }),\n listAgentDefinitions: () => [],\n listAgentJobs: () => [],\n spawnAgentJob: () =>\n Promise.resolve({\n id: 'agent_1',\n type: 'general-purpose',\n label: 'general-purpose',\n parentSessionId: 'test-session-id',\n status: 'running' as const,\n mode: 'background' as const,\n depth: 1,\n cwd: '/workspace',\n promptPreview: '',\n updatedAt: new Date().toISOString(),\n }),\n sendAgentJob: () => Promise.resolve(),\n cancelAgentJob: () => Promise.resolve(),\n closeAgentJob: () => Promise.resolve(),\n setGoal: () => Promise.resolve({ ...EMPTY_GOAL_STATE }),\n getGoalState: () => null,\n cancelGoal: () => null,\n ...overrides,\n };\n return base;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-framework",
3
- "version": "3.0.0-beta.76",
3
+ "version": "3.0.0-beta.78",
4
4
  "description": "Programmatic SDK for building AI agents with Robota — provides InteractiveSession, createQuery(), command APIs, permissions, hooks, and context loading",
5
5
  "type": "module",
6
6
  "main": "dist/node/index.js",
@@ -17,6 +17,18 @@
17
17
  "import": "./dist/node/index.js",
18
18
  "require": "./dist/node/index.cjs"
19
19
  }
20
+ },
21
+ "./testing": {
22
+ "types": "./dist/node/testing/index.d.ts",
23
+ "source": "./src/testing/index.ts",
24
+ "node": {
25
+ "import": "./dist/node/testing/index.js",
26
+ "require": "./dist/node/testing/index.cjs"
27
+ },
28
+ "default": {
29
+ "import": "./dist/node/testing/index.js",
30
+ "require": "./dist/node/testing/index.cjs"
31
+ }
20
32
  }
21
33
  },
22
34
  "repository": {
@@ -32,17 +44,18 @@
32
44
  ],
33
45
  "dependencies": {
34
46
  "zod": "^3.25.76",
35
- "@robota-sdk/agent-core": "3.0.0-beta.76",
36
- "@robota-sdk/agent-executor": "3.0.0-beta.76",
37
- "@robota-sdk/agent-interface-transport": "3.0.0-beta.76",
38
- "@robota-sdk/agent-tools": "3.0.0-beta.76",
39
- "@robota-sdk/agent-session": "3.0.0-beta.76"
47
+ "@robota-sdk/agent-core": "3.0.0-beta.78",
48
+ "@robota-sdk/agent-executor": "3.0.0-beta.78",
49
+ "@robota-sdk/agent-session": "3.0.0-beta.78",
50
+ "@robota-sdk/agent-tools": "3.0.0-beta.78",
51
+ "@robota-sdk/agent-interface-transport": "3.0.0-beta.78"
40
52
  },
41
53
  "devDependencies": {
42
54
  "rimraf": "^5.0.10",
43
55
  "tsdown": "^0.22.2",
44
56
  "typescript": "^5.9.3",
45
- "vitest": "^3.2.6"
57
+ "vitest": "^3.2.6",
58
+ "@robota-sdk/agent-session-analytics": "3.0.0-beta.78"
46
59
  },
47
60
  "keywords": [
48
61
  "ai",
@@ -61,7 +74,7 @@
61
74
  "interactive-session",
62
75
  "robota"
63
76
  ],
64
- "license": "MIT",
77
+ "license": "AGPL-3.0-only OR LicenseRef-Commercial",
65
78
  "publishConfig": {
66
79
  "access": "public"
67
80
  },