@aaep/typescript-producer 1.0.1
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 +170 -0
- package/dist/agent.d.ts +41 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +336 -0
- package/dist/agent.js.map +1 -0
- package/dist/coalescer.d.ts +57 -0
- package/dist/coalescer.d.ts.map +1 -0
- package/dist/coalescer.js +124 -0
- package/dist/coalescer.js.map +1 -0
- package/dist/emitter.d.ts +111 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +320 -0
- package/dist/emitter.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +23 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +258 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +208 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +45 -0
- package/dist/types.js.map +1 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# TypeScript Minimal Producer Example
|
|
2
|
+
|
|
3
|
+
A minimal but complete AAEP producer implementation in TypeScript, designed for Node.js (18+). Demonstrates the manual loop pattern with full safety machinery and HTTP/SSE transport.
|
|
4
|
+
|
|
5
|
+
If you're building AAEP producers in the TypeScript/Node.js ecosystem — whether for browser-side agents, Electron-based AT software, or Node backend agents — this is the example to copy.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What this example demonstrates
|
|
10
|
+
|
|
11
|
+
- A complete AAEP emitter implemented in idiomatic TypeScript
|
|
12
|
+
- Strict type definitions for every event type (matching the JSON Schemas)
|
|
13
|
+
- Native Node.js streams and `fetch` (no framework dependency)
|
|
14
|
+
- HTTP/SSE transport using Node's `http` module
|
|
15
|
+
- The same safety machinery as the Python examples: irreversible+high MUST default reject, secret redaction, critical urgency on errored events
|
|
16
|
+
- Confirmation flow with `Promise`-based blocking
|
|
17
|
+
- Streaming output with sentence-boundary coalescing
|
|
18
|
+
- All 12 core AAEP event types emitted from a typical agent session
|
|
19
|
+
- Conformance Level 2 compatible
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## When to use this example
|
|
24
|
+
|
|
25
|
+
**Pick this pattern when:**
|
|
26
|
+
- You're building agents in Node.js, TypeScript, or modern JavaScript
|
|
27
|
+
- You need AAEP support in browser-side agent UIs
|
|
28
|
+
- You're building AT software in Electron, Tauri, or similar frameworks
|
|
29
|
+
- You want strict type definitions for AAEP events in your codebase
|
|
30
|
+
|
|
31
|
+
**Pick a Python example when:** your agent stack is Python.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd examples/producers/typescript-minimal
|
|
39
|
+
npm install
|
|
40
|
+
npm run build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Requires Node.js 18 or newer.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { AAEPEmitter, AgentLoop, makeId } from 'aaep-typescript-producer';
|
|
51
|
+
|
|
52
|
+
// Step 1: create an emitter with your transport
|
|
53
|
+
const emitter = new AAEPEmitter({
|
|
54
|
+
sendEvent: (event) => {
|
|
55
|
+
// Forward to subscribers (HTTP/SSE, WebSocket, etc.)
|
|
56
|
+
console.log(JSON.stringify(event));
|
|
57
|
+
},
|
|
58
|
+
agentId: 'my-agent',
|
|
59
|
+
agentName: 'My Agent',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Step 2: create an agent loop
|
|
63
|
+
const agent = new AgentLoop(emitter);
|
|
64
|
+
|
|
65
|
+
// Step 3: run a session
|
|
66
|
+
const sessionId = await agent.run('Tell me about retirement planning.');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The agent emits AAEP events through the configured transport as it runs. The same safety guarantees as Python: irreversible actions trigger confirmation events, errors emit critical-urgency terminal events, streaming output coalesces at sentence boundaries.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Running the included demo
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm run demo
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Runs three scenarios (basic query, tool use, irreversible tool with confirmation) and prints every emitted AAEP event in real time.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Running the conformance suite against it
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Terminal 1: start the server (port 8084)
|
|
87
|
+
npm run server
|
|
88
|
+
|
|
89
|
+
# Terminal 2: run the Python conformance suite against the TypeScript server
|
|
90
|
+
aaep-conformance producer --endpoint http://localhost:8084 --level 2
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The Python `aaep-conformance` package can verify any HTTP/SSE AAEP producer regardless of implementation language. Cross-language conformance is mechanically demonstrated.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Project layout
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
typescript-minimal/
|
|
101
|
+
├── README.md
|
|
102
|
+
├── package.json
|
|
103
|
+
├── tsconfig.json
|
|
104
|
+
├── src/
|
|
105
|
+
│ ├── index.ts # Public API exports
|
|
106
|
+
│ ├── types.ts # TypeScript types matching the AAEP schemas
|
|
107
|
+
│ ├── emitter.ts # AAEPEmitter class
|
|
108
|
+
│ ├── coalescer.ts # StreamCoalescer for sentence-boundary buffering
|
|
109
|
+
│ ├── agent.ts # AgentLoop with mock LLM
|
|
110
|
+
│ └── server.ts # HTTP/SSE server
|
|
111
|
+
└── test/
|
|
112
|
+
├── emitter.test.ts
|
|
113
|
+
└── coalescer.test.ts
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Key design decisions
|
|
119
|
+
|
|
120
|
+
### 1. Strict TypeScript types
|
|
121
|
+
|
|
122
|
+
Every AAEP event has a corresponding TypeScript interface (`SessionStartedEvent`, `ToolInvokedEvent`, etc.). The emitter methods are strongly typed so editor autocomplete shows valid field names and the compiler catches missing required fields. Types match the JSON Schemas exactly.
|
|
123
|
+
|
|
124
|
+
### 2. No framework dependencies
|
|
125
|
+
|
|
126
|
+
The example uses only the Node.js standard library plus type definitions. No Express, no Fastify, no agent framework — just the language and runtime. This makes the integration pattern clear and ensures the example survives ecosystem churn.
|
|
127
|
+
|
|
128
|
+
### 3. Runtime safety enforcement (same as Python)
|
|
129
|
+
|
|
130
|
+
The `awaitConfirmation` method throws if called with `irreversible: true`, `riskLevel: 'high'`, and `defaultDecision: 'accept'`. This matches the JSON Schema's if/then rule AND the Python emitter's runtime check. The safety contract is enforced at every layer.
|
|
131
|
+
|
|
132
|
+
### 4. Promise-based confirmation blocking
|
|
133
|
+
|
|
134
|
+
JavaScript doesn't have Python's `asyncio.Future`, but `Promise` works the same way. `awaitConfirmation()` returns a reply token; calling code does `await emitter.waitForDecision(token)` to block until the subscriber replies via `submitReply()`.
|
|
135
|
+
|
|
136
|
+
### 5. Compatible with browser-side agents
|
|
137
|
+
|
|
138
|
+
The emitter code is environment-agnostic (no Node-specific imports in `emitter.ts`). You can use the same `AAEPEmitter` in a browser bundle for browser-side agents. Only `server.ts` is Node-specific.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Cross-language conformance verification
|
|
143
|
+
|
|
144
|
+
This example demonstrates that **AAEP is genuinely language-agnostic**. The same `aaep-conformance` Python tool that verifies the Python examples also verifies this TypeScript example without modification.
|
|
145
|
+
|
|
146
|
+
To prove this end-to-end:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Run all 5 servers simultaneously
|
|
150
|
+
python -m aaep_minimal_producer.server --port 8080 & # Python manual loop
|
|
151
|
+
python -m aaep_langchain.server --port 8081 & # LangChain callback
|
|
152
|
+
python -m aaep_anthropic_sdk.server --port 8082 & # Anthropic SDK
|
|
153
|
+
python -m aaep_maf.server --port 8083 & # Microsoft Agent Framework
|
|
154
|
+
npm run server & # TypeScript (port 8084)
|
|
155
|
+
|
|
156
|
+
# Run conformance against all 5
|
|
157
|
+
for port in 8080 8081 8082 8083 8084; do
|
|
158
|
+
aaep-conformance producer --endpoint http://localhost:$port --level 2
|
|
159
|
+
done
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
If all 5 pass Level 2, AAEP is interoperable across 4 Python integration patterns AND TypeScript. That's the mechanical proof of "AAEP is language-agnostic" — not a claim, a result.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## See also
|
|
167
|
+
|
|
168
|
+
- [`../python-minimal/`](../python-minimal/) — the Python reference (same machinery, different language)
|
|
169
|
+
- [Implementer's Guide §3.7](../../../guides/IMPLEMENTERS_GUIDE.md) — TypeScript integration specifics
|
|
170
|
+
- [Node.js SSE documentation](https://nodejs.org/api/http.html) — upstream reference for the transport
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentLoop — TypeScript port of the Python python-minimal AgentLoop.
|
|
3
|
+
*
|
|
4
|
+
* Production-grade reference of the manual loop pattern with a mock LLM
|
|
5
|
+
* for portability. Replace `_MockLLM` with a real client (Anthropic, OpenAI,
|
|
6
|
+
* Azure OpenAI, etc.) for production use.
|
|
7
|
+
*/
|
|
8
|
+
import { AAEPEmitter } from "./emitter.js";
|
|
9
|
+
export interface ToolDescriptor {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
handler: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
13
|
+
riskLevel?: "low" | "medium" | "high";
|
|
14
|
+
irreversible?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface AgentLoopOptions {
|
|
17
|
+
emitter: AAEPEmitter;
|
|
18
|
+
tools?: ToolDescriptor[];
|
|
19
|
+
}
|
|
20
|
+
export declare class AgentLoop {
|
|
21
|
+
private readonly emitter;
|
|
22
|
+
private readonly tools;
|
|
23
|
+
private readonly llm;
|
|
24
|
+
private readonly activeSessions;
|
|
25
|
+
constructor(options: AgentLoopOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Run a complete agent session. Returns the session_id.
|
|
28
|
+
*
|
|
29
|
+
* The session is fully owned by this method: it emits all lifecycle
|
|
30
|
+
* events including the terminal event, even on exception.
|
|
31
|
+
*/
|
|
32
|
+
run(userMessage: string, opts?: {
|
|
33
|
+
userId?: string;
|
|
34
|
+
}): Promise<string>;
|
|
35
|
+
/** Cancel an in-progress session. Returns true if cancellation was issued. */
|
|
36
|
+
cancel(sessionId: string): boolean;
|
|
37
|
+
private runLoop;
|
|
38
|
+
private executeTool;
|
|
39
|
+
private defaultTools;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACH,WAAW,EAKd,MAAM,cAAc,CAAC;AAMtB,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAKD,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC5B;AAKD,qBAAa,SAAS;IAClB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8B;IACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAW;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;gBAE9D,OAAO,EAAE,gBAAgB;IAQrC;;;;;OAKG;IACG,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB/E,8EAA8E;IAC9E,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;YASpB,OAAO;YAgIP,WAAW;IAuFzB,OAAO,CAAC,YAAY;CAuCvB"}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentLoop — TypeScript port of the Python python-minimal AgentLoop.
|
|
3
|
+
*
|
|
4
|
+
* Production-grade reference of the manual loop pattern with a mock LLM
|
|
5
|
+
* for portability. Replace `_MockLLM` with a real client (Anthropic, OpenAI,
|
|
6
|
+
* Azure OpenAI, etc.) for production use.
|
|
7
|
+
*/
|
|
8
|
+
import { classifyErrorCategory, classifyRisk, makeId, safeArgsSummary, } from "./emitter.js";
|
|
9
|
+
import { StreamCoalescer } from "./coalescer.js";
|
|
10
|
+
// === The agent loop ===
|
|
11
|
+
export class AgentLoop {
|
|
12
|
+
emitter;
|
|
13
|
+
tools;
|
|
14
|
+
llm;
|
|
15
|
+
activeSessions = new Map();
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.emitter = options.emitter;
|
|
18
|
+
this.tools = new Map((options.tools ?? this.defaultTools()).map((t) => [t.name, t]));
|
|
19
|
+
this.llm = new _MockLLM();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Run a complete agent session. Returns the session_id.
|
|
23
|
+
*
|
|
24
|
+
* The session is fully owned by this method: it emits all lifecycle
|
|
25
|
+
* events including the terminal event, even on exception.
|
|
26
|
+
*/
|
|
27
|
+
async run(userMessage, opts = {}) {
|
|
28
|
+
const sessionId = this.emitter.startSession({
|
|
29
|
+
summaryNormal: `Processing: ${userMessage.slice(0, 80)}`,
|
|
30
|
+
requestText: userMessage,
|
|
31
|
+
requestedBy: opts.userId ? `user:${opts.userId}` : undefined,
|
|
32
|
+
toolsAvailable: Array.from(this.tools.keys()),
|
|
33
|
+
});
|
|
34
|
+
const abortController = new AbortController();
|
|
35
|
+
this.activeSessions.set(sessionId, abortController);
|
|
36
|
+
try {
|
|
37
|
+
await this.runLoop(sessionId, userMessage, abortController.signal);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
this.activeSessions.delete(sessionId);
|
|
41
|
+
}
|
|
42
|
+
return sessionId;
|
|
43
|
+
}
|
|
44
|
+
/** Cancel an in-progress session. Returns true if cancellation was issued. */
|
|
45
|
+
cancel(sessionId) {
|
|
46
|
+
const controller = this.activeSessions.get(sessionId);
|
|
47
|
+
if (!controller)
|
|
48
|
+
return false;
|
|
49
|
+
controller.abort();
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
// === Internal: the thinking-and-acting loop ===
|
|
53
|
+
async runLoop(sessionId, userMessage, abortSignal) {
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
let currentState = "idle";
|
|
56
|
+
let toolCount = 0;
|
|
57
|
+
let coalescer = null;
|
|
58
|
+
const messages = [
|
|
59
|
+
{ role: "user", content: userMessage },
|
|
60
|
+
];
|
|
61
|
+
try {
|
|
62
|
+
while (true) {
|
|
63
|
+
if (abortSignal.aborted) {
|
|
64
|
+
throw new DOMException("Cancelled", "AbortError");
|
|
65
|
+
}
|
|
66
|
+
// Transition to thinking
|
|
67
|
+
if (currentState !== "thinking") {
|
|
68
|
+
this.emitter.stateChanged({
|
|
69
|
+
sessionId,
|
|
70
|
+
fromState: currentState,
|
|
71
|
+
toState: "thinking",
|
|
72
|
+
summaryNormal: "Considering the request.",
|
|
73
|
+
});
|
|
74
|
+
currentState = "thinking";
|
|
75
|
+
}
|
|
76
|
+
const response = await this.llm.complete(messages, Array.from(this.tools.values()));
|
|
77
|
+
if (response.toolCalls.length > 0) {
|
|
78
|
+
// Tools requested
|
|
79
|
+
this.emitter.stateChanged({
|
|
80
|
+
sessionId,
|
|
81
|
+
fromState: "thinking",
|
|
82
|
+
toState: "calling_tool",
|
|
83
|
+
summaryNormal: `Preparing to call ${response.toolCalls.length} tool(s).`,
|
|
84
|
+
});
|
|
85
|
+
currentState = "calling_tool";
|
|
86
|
+
const toolResults = [];
|
|
87
|
+
for (const toolCall of response.toolCalls) {
|
|
88
|
+
const result = await this.executeTool(sessionId, toolCall);
|
|
89
|
+
toolCount++;
|
|
90
|
+
toolResults.push({ tool_call_id: toolCall.id, result });
|
|
91
|
+
}
|
|
92
|
+
messages.push({
|
|
93
|
+
role: "assistant",
|
|
94
|
+
content: response.text,
|
|
95
|
+
});
|
|
96
|
+
messages.push({
|
|
97
|
+
role: "tool_results",
|
|
98
|
+
content: toolResults,
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// No more tools; stream output
|
|
103
|
+
this.emitter.stateChanged({
|
|
104
|
+
sessionId,
|
|
105
|
+
fromState: "thinking",
|
|
106
|
+
toState: "writing_output",
|
|
107
|
+
summaryNormal: "Generating response.",
|
|
108
|
+
});
|
|
109
|
+
currentState = "writing_output";
|
|
110
|
+
const outputId = makeId("out");
|
|
111
|
+
coalescer = new StreamCoalescer({
|
|
112
|
+
emitter: this.emitter,
|
|
113
|
+
sessionId,
|
|
114
|
+
outputId,
|
|
115
|
+
coalesceAt: "sentence",
|
|
116
|
+
});
|
|
117
|
+
for await (const chunk of this.llm.stream(response)) {
|
|
118
|
+
if (abortSignal.aborted) {
|
|
119
|
+
throw new DOMException("Cancelled", "AbortError");
|
|
120
|
+
}
|
|
121
|
+
coalescer.addToken(chunk);
|
|
122
|
+
}
|
|
123
|
+
coalescer.finish();
|
|
124
|
+
coalescer = null;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
// Success
|
|
128
|
+
const durationMs = Date.now() - startTime;
|
|
129
|
+
this.emitter.completeSession({
|
|
130
|
+
sessionId,
|
|
131
|
+
summaryNormal: "Response complete.",
|
|
132
|
+
durationMs,
|
|
133
|
+
toolInvocationsCount: toolCount,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (coalescer) {
|
|
138
|
+
try {
|
|
139
|
+
coalescer.finish();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Already finished or in bad state; swallow
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
146
|
+
this.emitter.cancelledSession({
|
|
147
|
+
sessionId,
|
|
148
|
+
cancelledBy: "system",
|
|
149
|
+
summaryNormal: "Session cancelled.",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
154
|
+
this.emitter.errorSession({
|
|
155
|
+
sessionId,
|
|
156
|
+
errorCategory: classifyErrorCategory(err),
|
|
157
|
+
summaryNormal: `Error: ${err.name}`,
|
|
158
|
+
errorMessage: err.message.slice(0, 1000),
|
|
159
|
+
recoverable: err.name === "TimeoutError" || err.message.toLowerCase().includes("network"),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// === Internal: execute one tool with full AAEP cycle ===
|
|
166
|
+
async executeTool(sessionId, toolCall) {
|
|
167
|
+
const descriptor = this.tools.get(toolCall.name);
|
|
168
|
+
const aaepCallId = makeId("call");
|
|
169
|
+
const args = toolCall.arguments;
|
|
170
|
+
const { riskLevel, irreversible } = descriptor
|
|
171
|
+
? {
|
|
172
|
+
riskLevel: descriptor.riskLevel ?? "low",
|
|
173
|
+
irreversible: descriptor.irreversible ?? false,
|
|
174
|
+
}
|
|
175
|
+
: classifyRisk(toolCall.name);
|
|
176
|
+
// Emit tool.invoked BEFORE side effect
|
|
177
|
+
this.emitter.toolInvoked({
|
|
178
|
+
sessionId,
|
|
179
|
+
tool: toolCall.name,
|
|
180
|
+
toolCallId: aaepCallId,
|
|
181
|
+
argsSummary: safeArgsSummary(args),
|
|
182
|
+
riskLevel,
|
|
183
|
+
irreversible,
|
|
184
|
+
summaryNormal: `Calling ${toolCall.name}.`,
|
|
185
|
+
});
|
|
186
|
+
// Confirmation gating for irreversible or high-risk
|
|
187
|
+
if (irreversible || riskLevel === "high") {
|
|
188
|
+
const replyToken = this.emitter.awaitConfirmation({
|
|
189
|
+
sessionId,
|
|
190
|
+
action: `Call ${toolCall.name} with: ${safeArgsSummary(args, 200)}`,
|
|
191
|
+
consequence: irreversible
|
|
192
|
+
? "This action cannot be easily undone."
|
|
193
|
+
: "This action will be executed.",
|
|
194
|
+
timeoutSeconds: 300,
|
|
195
|
+
defaultDecision: "reject",
|
|
196
|
+
riskLevel,
|
|
197
|
+
irreversible,
|
|
198
|
+
summaryNormal: `Confirm: call ${toolCall.name}?`,
|
|
199
|
+
});
|
|
200
|
+
const decision = await this.emitter.waitForDecision(replyToken);
|
|
201
|
+
if (decision !== "accept") {
|
|
202
|
+
this.emitter.toolCompleted({
|
|
203
|
+
sessionId,
|
|
204
|
+
tool: toolCall.name,
|
|
205
|
+
toolCallId: aaepCallId,
|
|
206
|
+
status: "error",
|
|
207
|
+
errorMessage: "User declined to authorize this action.",
|
|
208
|
+
});
|
|
209
|
+
return `<user declined to call ${toolCall.name}>`;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Execute the tool
|
|
213
|
+
try {
|
|
214
|
+
if (!descriptor) {
|
|
215
|
+
throw new Error(`No handler for tool ${JSON.stringify(toolCall.name)}`);
|
|
216
|
+
}
|
|
217
|
+
const result = await descriptor.handler(args);
|
|
218
|
+
this.emitter.toolCompleted({
|
|
219
|
+
sessionId,
|
|
220
|
+
tool: toolCall.name,
|
|
221
|
+
toolCallId: aaepCallId,
|
|
222
|
+
status: "success",
|
|
223
|
+
summaryNormal: String(result).slice(0, 200),
|
|
224
|
+
});
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
229
|
+
const isTimeout = err.name === "TimeoutError" || err.message.includes("timeout");
|
|
230
|
+
this.emitter.toolCompleted({
|
|
231
|
+
sessionId,
|
|
232
|
+
tool: toolCall.name,
|
|
233
|
+
toolCallId: aaepCallId,
|
|
234
|
+
status: isTimeout ? "timeout" : "error",
|
|
235
|
+
errorMessage: err.message.slice(0, 1000),
|
|
236
|
+
});
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// === Default mock tools ===
|
|
241
|
+
defaultTools() {
|
|
242
|
+
return [
|
|
243
|
+
{
|
|
244
|
+
name: "fetch_balance",
|
|
245
|
+
description: "Look up an account balance",
|
|
246
|
+
handler: async (args) => {
|
|
247
|
+
await sleep(300);
|
|
248
|
+
const account = args.account ?? "checking";
|
|
249
|
+
const balances = {
|
|
250
|
+
checking: "$3,247.18",
|
|
251
|
+
savings: "$12,891.40",
|
|
252
|
+
};
|
|
253
|
+
return balances[account] ?? "$0.00";
|
|
254
|
+
},
|
|
255
|
+
riskLevel: "low",
|
|
256
|
+
irreversible: false,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "send_email",
|
|
260
|
+
description: "Send an email",
|
|
261
|
+
handler: async (args) => {
|
|
262
|
+
await sleep(500);
|
|
263
|
+
return `Email sent to ${args.to ?? "recipient"}`;
|
|
264
|
+
},
|
|
265
|
+
riskLevel: "high",
|
|
266
|
+
irreversible: true,
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: "transfer_funds",
|
|
270
|
+
description: "Move money between accounts",
|
|
271
|
+
handler: async (args) => {
|
|
272
|
+
await sleep(700);
|
|
273
|
+
return `Transferred $${args.amount} from ${args.from} to ${args.to}`;
|
|
274
|
+
},
|
|
275
|
+
riskLevel: "high",
|
|
276
|
+
irreversible: true,
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
class _MockLLM {
|
|
282
|
+
calls = 0;
|
|
283
|
+
async complete(messages, _tools) {
|
|
284
|
+
await sleep(400);
|
|
285
|
+
this.calls++;
|
|
286
|
+
const lastMsg = messages[messages.length - 1];
|
|
287
|
+
const lastText = typeof lastMsg?.content === "string" ? lastMsg.content : "";
|
|
288
|
+
const lower = lastText.toLowerCase();
|
|
289
|
+
// On the first call, sometimes call a tool to exercise the flow
|
|
290
|
+
if (this.calls === 1) {
|
|
291
|
+
if (lower.includes("balance")) {
|
|
292
|
+
return {
|
|
293
|
+
text: "Let me check that for you.",
|
|
294
|
+
toolCalls: [{
|
|
295
|
+
id: `tool_${this.calls}`,
|
|
296
|
+
name: "fetch_balance",
|
|
297
|
+
arguments: { account: "checking" },
|
|
298
|
+
}],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (lower.includes("email")) {
|
|
302
|
+
return {
|
|
303
|
+
text: "I'll send that email.",
|
|
304
|
+
toolCalls: [{
|
|
305
|
+
id: `tool_${this.calls}`,
|
|
306
|
+
name: "send_email",
|
|
307
|
+
arguments: {
|
|
308
|
+
to: "recipient@example.com",
|
|
309
|
+
subject: "Re: your request",
|
|
310
|
+
},
|
|
311
|
+
}],
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// No more tools; return final output (caller will stream the text)
|
|
316
|
+
return { text: "", toolCalls: [] };
|
|
317
|
+
}
|
|
318
|
+
async *stream(_response) {
|
|
319
|
+
const chunks = [
|
|
320
|
+
"Here's what I found. ",
|
|
321
|
+
"Your account is in good standing ",
|
|
322
|
+
"with no pending issues. ",
|
|
323
|
+
"Is there anything else ",
|
|
324
|
+
"I can help you with?",
|
|
325
|
+
];
|
|
326
|
+
for (const chunk of chunks) {
|
|
327
|
+
await sleep(50);
|
|
328
|
+
yield chunk;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// === Helpers ===
|
|
333
|
+
function sleep(ms) {
|
|
334
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamCoalescer — TypeScript port of the Python StreamCoalescer.
|
|
3
|
+
*
|
|
4
|
+
* Buffers streaming tokens and emits agent.output.streaming events at
|
|
5
|
+
* coalesce boundaries (default: sentence boundaries). The boundary
|
|
6
|
+
* detection regexes match the Python implementation exactly.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const coalescer = new StreamCoalescer({
|
|
10
|
+
* emitter,
|
|
11
|
+
* sessionId: "sess_x",
|
|
12
|
+
* outputId: "out_y",
|
|
13
|
+
* coalesceAt: "sentence",
|
|
14
|
+
* });
|
|
15
|
+
* coalescer.addToken("Hello ");
|
|
16
|
+
* coalescer.addToken("world. "); // sentence boundary -> emit
|
|
17
|
+
* coalescer.addToken("Goodbye.");
|
|
18
|
+
* coalescer.finish(); // final emit with complete=true
|
|
19
|
+
*/
|
|
20
|
+
import type { CoalesceHint } from "./types.js";
|
|
21
|
+
import { AAEPEmitter } from "./emitter.js";
|
|
22
|
+
export interface StreamCoalescerOptions {
|
|
23
|
+
emitter: AAEPEmitter;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
outputId: string;
|
|
26
|
+
coalesceAt?: CoalesceHint;
|
|
27
|
+
language?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class StreamCoalescer {
|
|
30
|
+
private readonly emitter;
|
|
31
|
+
private readonly sessionId;
|
|
32
|
+
private readonly outputId;
|
|
33
|
+
private readonly coalesceAt;
|
|
34
|
+
private readonly language;
|
|
35
|
+
private buffer;
|
|
36
|
+
private position;
|
|
37
|
+
private finished;
|
|
38
|
+
constructor(options: StreamCoalescerOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Add a token to the buffer. May emit one or more events if coalesce
|
|
41
|
+
* boundaries are crossed by the addition.
|
|
42
|
+
*/
|
|
43
|
+
addToken(token: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Flush any remaining buffer and emit the final completion event
|
|
46
|
+
* (complete=true). Idempotent; safe to call multiple times.
|
|
47
|
+
*/
|
|
48
|
+
finish(): void;
|
|
49
|
+
/** Inspect the current buffered text (for tests). */
|
|
50
|
+
get bufferedText(): string;
|
|
51
|
+
/** Whether finish() has been called. */
|
|
52
|
+
get isFinished(): boolean;
|
|
53
|
+
private flushAtBoundary;
|
|
54
|
+
private findBoundaries;
|
|
55
|
+
private emitChunk;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=coalescer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coalescer.d.ts","sourceRoot":"","sources":["../src/coalescer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,qBAAa,eAAe;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,sBAAsB;IAQ3C;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ7B;;;OAGG;IACH,MAAM,IAAI,IAAI;IAQd,qDAAqD;IACrD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,wCAAwC;IACxC,IAAI,UAAU,IAAI,OAAO,CAExB;IAID,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,SAAS;CAepB"}
|