@smooai/smooth-operator-core 0.23.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
> The
|
|
18
|
+
> ### The agent brain you can point at production — right in your Node process.
|
|
19
|
+
>
|
|
20
|
+
> Most agent frameworks hand the model a pile of tools and hope. This one gives you the loop **and the brakes**: draw hard lines the model can never cross, then let it run.
|
|
19
21
|
|
|
20
|
-
`@smooai/smooth-operator-core` is the
|
|
22
|
+
`@smooai/smooth-operator-core` is the agent engine itself, in-process — an observe→think→act loop over any OpenAI-compatible client, with typed tools, streaming, checkpointing, cost budgets, and a permission gate you control. Not a client to a remote server: the agent *is* your process.
|
|
21
23
|
|
|
22
|
-
It's
|
|
24
|
+
It's the native TypeScript port of the [Rust reference engine](https://github.com/SmooAI/smooth-operator-core) — one of five siblings (Rust, TypeScript, Python, Go, C#/.NET) that share one wire spec and one eval suite. **The same agent brain, the same guarantees, wherever your stack already lives.** Every surface is covered by fast, offline tests on a deterministic `MockLlmProvider`, so the loop is verified — not vibe-coded.
|
|
23
25
|
|
|
24
26
|
## Install
|
|
25
27
|
|
|
@@ -29,19 +31,34 @@ npm install @smooai/smooth-operator-core
|
|
|
29
31
|
|
|
30
32
|
## Quickstart
|
|
31
33
|
|
|
32
|
-
A complete agent — no credentials needed — using the deterministic mock provider the engine's own tests run on:
|
|
34
|
+
A complete agent with one tool — no credentials needed — using the deterministic mock provider the engine's own tests run on. The mock is scripted to call the tool, then answer:
|
|
33
35
|
|
|
34
36
|
```ts
|
|
35
|
-
import { SmoothAgent, MockLlmProvider } from '@smooai/smooth-operator-core';
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
import { SmoothAgent, MockLlmProvider, type Tool } from '@smooai/smooth-operator-core';
|
|
38
|
+
|
|
39
|
+
const getWeather: Tool = {
|
|
40
|
+
name: 'get_weather',
|
|
41
|
+
description: 'Get the current weather for a city',
|
|
42
|
+
parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
|
|
43
|
+
async execute(args) {
|
|
44
|
+
return `Weather in ${args.city}: 72F, sunny`;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const provider = new MockLlmProvider()
|
|
49
|
+
.pushToolCall('call_1', 'get_weather', JSON.stringify({ city: 'Tokyo' }))
|
|
50
|
+
.pushText("It's 72F and sunny in Tokyo.");
|
|
51
|
+
|
|
52
|
+
const agent = new SmoothAgent(provider, {
|
|
53
|
+
instructions: 'You are a helpful assistant',
|
|
54
|
+
tools: [getWeather],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const response = await agent.run("what's the weather in Tokyo?");
|
|
41
58
|
console.log(response.text);
|
|
42
59
|
```
|
|
43
60
|
|
|
44
|
-
`SmoothAgent`'s constructor takes a `ChatClientLike` (the `MockLlmProvider` implements it — swap in any OpenAI-compatible client) and an `AgentOptions` object. `run` returns an `AgentRunResponse` whose `text` is the final answer.
|
|
61
|
+
`SmoothAgent`'s constructor takes a `ChatClientLike` (the `MockLlmProvider` implements it — swap in any OpenAI-compatible client) and an `AgentOptions` object. A `Tool` is a `{ name, description, parameters, execute }` object. `run` returns an `AgentRunResponse` whose `text` is the final answer.
|
|
45
62
|
|
|
46
63
|
## Features
|
|
47
64
|
|
|
@@ -57,6 +74,7 @@ The full parity surface — every engine in the [polyglot set](https://github.co
|
|
|
57
74
|
- **Rerank** — `LexicalReranker` reranks retrieved hits before injection.
|
|
58
75
|
- **Sub-agents / delegation** — `delegateTool` spawns child agents for sub-tasks.
|
|
59
76
|
- **Cast + clearance** — `Cast`, `Clearance`, `makeRole` for per-role tool-access policy.
|
|
77
|
+
- **Permissions + deny-policy** — a tool-call gate (`AutoMode`: ask / accept-edits / deny-unmatched / bypass) with hard circuit-breakers (`rm -rf /`, credential paths, pipe-to-shell, dangerous domains), a persisted allow-list, and a consumer `DenyPolicy` — declarative TOML rules plus semantic predicates for what strings can't express.
|
|
60
78
|
- **Human-in-the-loop gate** — `HumanGate` requires approval before designated tool calls run.
|
|
61
79
|
- **Conversation thread** — `SmoothAgentThread` carries a conversation across multiple `run` calls.
|
|
62
80
|
- **`LlmProvider` seam + `MockLlmProvider`** — inject any OpenAI-compatible client; the record/replay mock drives the offline tests.
|
|
@@ -66,6 +84,36 @@ The full parity surface — every engine in the [polyglot set](https://github.co
|
|
|
66
84
|
- **Retry / backoff** — retry transient model-call failures with exponential backoff.
|
|
67
85
|
- **Streaming** — stream incremental text, tool calls, and tool results as the turn runs.
|
|
68
86
|
|
|
87
|
+
## Permissions & deny-policy — lines the agent can't cross
|
|
88
|
+
|
|
89
|
+
This is what makes an agent safe to point at real infrastructure: **you** decide what it can never do, and no prompt or model mistake talks it out of that. Every tool call passes through a gate. `AutoMode` sets the posture — read-only calls **allow**, mutating calls **ask**, dangerous calls **deny** — and hard circuit-breakers (`rm -rf /`, credential paths, pipe-to-shell, dangerous domains) fire in every mode, `Bypass` included. Attach a `DenyPolicy` on top: declarative TOML rules for the lines you can name, semantic predicates for the ones you can't. A match is a hard deny no stored grant and no mode can waive.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { SmoothAgent, AutoMode, DenyPolicy, type DenyPredicate } from '@smooai/smooth-operator-core';
|
|
93
|
+
|
|
94
|
+
// Declarative rules (TOML): never the prod AWS profile, never a prod host.
|
|
95
|
+
const policy = DenyPolicy.fromToml(`
|
|
96
|
+
schema_version = 1
|
|
97
|
+
[bash]
|
|
98
|
+
deny_patterns = ["aws * --profile prod"]
|
|
99
|
+
[network]
|
|
100
|
+
deny_hosts = ["*.prod.internal"]
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
// Predicate for what strings can't express — return a reason to deny, or undefined to allow.
|
|
104
|
+
const denyDbWriter: DenyPredicate = (call) =>
|
|
105
|
+
call.name === 'db_query' && /writer/.test(JSON.stringify(call.arguments))
|
|
106
|
+
? 'DB writer endpoint is off-limits — reads go to the replica'
|
|
107
|
+
: undefined;
|
|
108
|
+
|
|
109
|
+
const agent = new SmoothAgent(provider, {
|
|
110
|
+
instructions: 'You are a careful assistant',
|
|
111
|
+
tools: [getWeather],
|
|
112
|
+
permissionMode: AutoMode.Ask, // read allow · mutate ask · dangerous deny
|
|
113
|
+
denyPolicy: policy.withPredicate(denyDbWriter),
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
69
117
|
## Streaming
|
|
70
118
|
|
|
71
119
|
`runStream` is an async generator over a `StreamEvent` tagged union (discriminated on `type`): `text` deltas as the model produces them, each `tool_call` before dispatch, each `tool_result` after it finishes, and a terminal `done` event carrying the same response `run` would have returned.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smooai/smooth-operator-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Native TypeScript implementation of the smooth-operator agent engine — an in-process, OpenAI-compatible agentic tool-calling loop with knowledge grounding. The TypeScript sibling of the Rust reference engine, the C# core, and the Python core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|