@agenticprimitives/orchestration 0.0.0-alpha.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/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/loop.d.ts +25 -0
- package/dist/loop.d.ts.map +1 -0
- package/dist/loop.js +113 -0
- package/dist/loop.js.map +1 -0
- package/dist/planner.d.ts +35 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +33 -0
- package/dist/planner.js.map +1 -0
- package/dist/tool-invoker.d.ts +15 -0
- package/dist/tool-invoker.d.ts.map +1 -0
- package/dist/tool-invoker.js +2 -0
- package/dist/tool-invoker.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
- package/spec.md +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agentic Trust Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @agenticprimitives/orchestration
|
|
2
|
+
|
|
3
|
+
The Ring-0 **agentic orchestration core**: the intent → plan → execute → observe loop, the `Planner` and
|
|
4
|
+
`ToolInvoker` ports, and a deterministic rule-based planner. A first-party app expresses a **goal**; this
|
|
5
|
+
package plans which tools compose to satisfy it and runs them through an injected invoker — the web never
|
|
6
|
+
names a tool (ADR-0044). Pure leaf: no transport, no LLM SDK, no chain. The concrete LLM binding lives behind
|
|
7
|
+
the `Planner` port in [`@agenticprimitives/orchestration-anthropic`](../orchestration-anthropic).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @agenticprimitives/orchestration
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { runIntent, createRuleBasedPlanner, type ToolSpec } from '@agenticprimitives/orchestration';
|
|
19
|
+
|
|
20
|
+
const tools: ToolSpec[] = [
|
|
21
|
+
{ id: 'get_vault_record', description: 'Read one of the principal’s vault records by recordType.' },
|
|
22
|
+
{ id: 'get_pii', description: 'Read the principal’s PII (high sensitivity).' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
// Deterministic default planner — goal → tool. No model, no creds.
|
|
26
|
+
const planner = createRuleBasedPlanner([
|
|
27
|
+
{ match: /profile/, toolId: 'get_vault_record', args: { recordType: 'impact-profile' } },
|
|
28
|
+
{ match: /pii|personal/, toolId: 'get_pii' },
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const result = await runIntent(
|
|
32
|
+
{ goal: 'read my profile' },
|
|
33
|
+
{
|
|
34
|
+
planner,
|
|
35
|
+
tools,
|
|
36
|
+
// The invoker is where authority lives — the a2a skill binds this to the task's delegation.
|
|
37
|
+
invoke: async (toolId, args) => myMcpCall(toolId, args),
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (result.outcome === 'completed') console.log(result.result);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Concepts
|
|
45
|
+
|
|
46
|
+
- **`Intent`** — a declarative goal (`{ goal, constraints?, context? }`), never a tool name or call sequence.
|
|
47
|
+
- **`Planner`** — the injected port that turns a goal + the exposed `ToolSpec[]` into a `Plan`. Swap the
|
|
48
|
+
deterministic `createRuleBasedPlanner` for the LLM adapter without touching the loop.
|
|
49
|
+
- **`ToolInvoker`** — the injected port that executes one step. The orchestration core never holds a token,
|
|
50
|
+
key, or chain client; the invoker carries the on-chain authority (delegation).
|
|
51
|
+
- **`runIntent`** — the loop: plan → execute (with `{ $ref }` result threading) → observe → bounded re-plan on
|
|
52
|
+
failure. Fail-closed: every error path returns a `failed` `RunResult`.
|
|
53
|
+
|
|
54
|
+
## Boundaries
|
|
55
|
+
|
|
56
|
+
Pure leaf. Forbidden imports: `a2a`, `mcp-runtime`, `mcp-protocol`, `delegation`, `service-agent`, and any LLM
|
|
57
|
+
SDK. See `spec.md` + `capability.manifest.json`.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { PACKAGE_NAME, PACKAGE_STATUS, SPEC_REF, OrchestrationError } from './types.js';
|
|
2
|
+
export type { Intent, ToolSpec, PlanStep, Plan, StepObservation, RunOutcome, RunResult, OrchestrationErrorCode, } from './types.js';
|
|
3
|
+
export { createRuleBasedPlanner } from './planner.js';
|
|
4
|
+
export type { Planner, PlanInput, PlanRule } from './planner.js';
|
|
5
|
+
export type { ToolInvoker, InvokeContext } from './tool-invoker.js';
|
|
6
|
+
export { runIntent } from './loop.js';
|
|
7
|
+
export type { RunIntentOpts } from './loop.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACxF,YAAY,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,eAAe,EACf,UAAU,EACV,SAAS,EACT,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEjE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @agenticprimitives/orchestration — public API. The Ring-0 agentic loop: the intent→plan→execute→observe
|
|
2
|
+
// model, the Planner + ToolInvoker PORTS, and `runIntent`. Pure leaf — no transport, no LLM SDK, no chain.
|
|
3
|
+
// The LLM binding lives in `@agenticprimitives/orchestration-anthropic` (behind the Planner port). ADR-0044.
|
|
4
|
+
export { PACKAGE_NAME, PACKAGE_STATUS, SPEC_REF, OrchestrationError } from './types.js';
|
|
5
|
+
export { createRuleBasedPlanner } from './planner.js';
|
|
6
|
+
export { runIntent } from './loop.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,2GAA2G;AAC3G,6GAA6G;AAE7G,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAYxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAKtD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/loop.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Intent, RunResult, StepObservation, ToolSpec } from './types.js';
|
|
2
|
+
import type { Planner } from './planner.js';
|
|
3
|
+
import type { ToolInvoker } from './tool-invoker.js';
|
|
4
|
+
export interface RunIntentOpts {
|
|
5
|
+
/** The injected planning port (deterministic default or the LLM adapter). */
|
|
6
|
+
planner: Planner;
|
|
7
|
+
/** The tools the agent exposes — the plan may only reference these (fail-closed). */
|
|
8
|
+
tools: ToolSpec[];
|
|
9
|
+
/** The injected tool executor (the a2a skill binds it to the task delegation). */
|
|
10
|
+
invoke: ToolInvoker;
|
|
11
|
+
/** Hard cap on total step executions across all (re-)plans. Default 8. */
|
|
12
|
+
maxSteps?: number;
|
|
13
|
+
/** How many times the loop re-plans after a step failure. Default 1 (one observe→re-plan cycle). */
|
|
14
|
+
maxReplans?: number;
|
|
15
|
+
/** Optional observer for each executed step (telemetry; never authority). */
|
|
16
|
+
onObserve?: (obs: StepObservation) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run an intent to a terminal result. Plans, executes the steps in order (threading `ref` results into later
|
|
20
|
+
* args), observes, and re-plans (bounded) when a step throws. Returns `completed` with the last successful
|
|
21
|
+
* step's result, or `failed` with the error. Fail-closed throughout: an unknown tool, an empty plan, no plan,
|
|
22
|
+
* or an exhausted step budget all produce a `failed` RunResult (never a misleading empty success).
|
|
23
|
+
*/
|
|
24
|
+
export declare function runIntent(intent: Intent, opts: RunIntentOpts): Promise<RunResult>;
|
|
25
|
+
//# sourceMappingURL=loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAkB,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE/F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,kFAAkF;IAClF,MAAM,EAAE,WAAW,CAAC;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CAC5C;AAiDD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CA6DvF"}
|
package/dist/loop.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { OrchestrationError } from './types.js';
|
|
2
|
+
/** Read a dot/index path (e.g. `list.record_types.0.record_type`) out of the ref store. A bare name with no
|
|
3
|
+
* dots is a plain top-level lookup. A missing segment short-circuits to `undefined` (the tool then validates
|
|
4
|
+
* its own args — fail-closed, no path traversal / eval). */
|
|
5
|
+
function getPath(root, path) {
|
|
6
|
+
return path.split('.').reduce((acc, seg) => (acc == null ? undefined : acc[seg]), root);
|
|
7
|
+
}
|
|
8
|
+
/** Recursively resolve `{ $ref: 'name.path' }` placeholders (anywhere — nested objects/arrays included)
|
|
9
|
+
* against prior step results. Non-placeholder values pass through unchanged. */
|
|
10
|
+
function resolveValue(v, refs) {
|
|
11
|
+
if (Array.isArray(v))
|
|
12
|
+
return v.map((x) => resolveValue(x, refs));
|
|
13
|
+
if (v && typeof v === 'object') {
|
|
14
|
+
if ('$ref' in v) {
|
|
15
|
+
const ref = v.$ref;
|
|
16
|
+
return typeof ref === 'string' ? getPath(refs, ref) : undefined;
|
|
17
|
+
}
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [k, val] of Object.entries(v))
|
|
20
|
+
out[k] = resolveValue(val, refs);
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
return v;
|
|
24
|
+
}
|
|
25
|
+
/** Resolve `{ $ref }` arg placeholders against prior step results (supports `name.path.0.field`). */
|
|
26
|
+
function resolveArgs(args, refs) {
|
|
27
|
+
return resolveValue(args, refs);
|
|
28
|
+
}
|
|
29
|
+
/** Assert every step targets an exposed tool — fail-closed before any invocation (ADR-0013). */
|
|
30
|
+
function assertKnownTools(plan, toolIds) {
|
|
31
|
+
for (const step of plan.steps) {
|
|
32
|
+
if (!toolIds.has(step.toolId)) {
|
|
33
|
+
throw new OrchestrationError('unknown_tool', `plan selected a tool not exposed by this agent: ${step.toolId}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (plan.steps.length === 0)
|
|
37
|
+
throw new OrchestrationError('empty_plan', 'the planner returned a plan with no steps');
|
|
38
|
+
}
|
|
39
|
+
async function callPlanner(planner, input) {
|
|
40
|
+
try {
|
|
41
|
+
return await planner.plan(input);
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
if (e instanceof OrchestrationError)
|
|
45
|
+
throw e; // no_plan / empty_plan surface as-is
|
|
46
|
+
throw new OrchestrationError('planner_failed', e instanceof Error ? e.message : String(e));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Run an intent to a terminal result. Plans, executes the steps in order (threading `ref` results into later
|
|
51
|
+
* args), observes, and re-plans (bounded) when a step throws. Returns `completed` with the last successful
|
|
52
|
+
* step's result, or `failed` with the error. Fail-closed throughout: an unknown tool, an empty plan, no plan,
|
|
53
|
+
* or an exhausted step budget all produce a `failed` RunResult (never a misleading empty success).
|
|
54
|
+
*/
|
|
55
|
+
export async function runIntent(intent, opts) {
|
|
56
|
+
const maxSteps = opts.maxSteps ?? 8;
|
|
57
|
+
const maxReplans = opts.maxReplans ?? 1;
|
|
58
|
+
const toolIds = new Set(opts.tools.map((t) => t.id));
|
|
59
|
+
const allObservations = [];
|
|
60
|
+
const refs = {};
|
|
61
|
+
let executed = 0;
|
|
62
|
+
let replans = 0;
|
|
63
|
+
let lastResult;
|
|
64
|
+
try {
|
|
65
|
+
let plan = await callPlanner(opts.planner, { intent, tools: opts.tools });
|
|
66
|
+
assertKnownTools(plan, toolIds);
|
|
67
|
+
// The plan loop: walk the current plan; on a step failure, re-plan (bounded) and restart with a fresh plan.
|
|
68
|
+
for (;;) {
|
|
69
|
+
let failedMidPlan = false;
|
|
70
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
71
|
+
if (executed >= maxSteps)
|
|
72
|
+
throw new OrchestrationError('max_steps', `step budget of ${maxSteps} exhausted`);
|
|
73
|
+
const step = plan.steps[i];
|
|
74
|
+
const ctx = { intent, step, index: i };
|
|
75
|
+
const args = resolveArgs(step.args, refs);
|
|
76
|
+
executed++;
|
|
77
|
+
let obs;
|
|
78
|
+
try {
|
|
79
|
+
const result = await opts.invoke(step.toolId, args, ctx);
|
|
80
|
+
obs = { step, ok: true, result };
|
|
81
|
+
if (step.ref)
|
|
82
|
+
refs[step.ref] = result;
|
|
83
|
+
lastResult = result;
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
obs = { step, ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
87
|
+
}
|
|
88
|
+
allObservations.push(obs);
|
|
89
|
+
opts.onObserve?.(obs);
|
|
90
|
+
if (!obs.ok) {
|
|
91
|
+
failedMidPlan = true;
|
|
92
|
+
break; // stop this plan; decide whether to re-plan below
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!failedMidPlan) {
|
|
96
|
+
return { outcome: 'completed', plan, steps: allObservations, result: lastResult };
|
|
97
|
+
}
|
|
98
|
+
if (replans >= maxReplans) {
|
|
99
|
+
const last = allObservations[allObservations.length - 1];
|
|
100
|
+
return { outcome: 'failed', plan, steps: allObservations, error: last?.error ?? 'step failed' };
|
|
101
|
+
}
|
|
102
|
+
replans++;
|
|
103
|
+
plan = await callPlanner(opts.planner, { intent, tools: opts.tools, observations: allObservations });
|
|
104
|
+
assertKnownTools(plan, toolIds);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
const error = e instanceof OrchestrationError ? `${e.code}: ${e.message}` : e instanceof Error ? e.message : String(e);
|
|
109
|
+
// No plan was ever produced if allObservations is empty + we threw before the loop.
|
|
110
|
+
return { outcome: 'failed', plan: { steps: [] }, steps: allObservations, error };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=loop.js.map
|
package/dist/loop.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop.js","sourceRoot":"","sources":["../src/loop.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAmBhD;;6DAE6D;AAC7D,SAAS,OAAO,CAAC,IAA6B,EAAE,IAAY;IAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAU,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAChI,CAAC;AAED;iFACiF;AACjF,SAAS,YAAY,CAAC,CAAU,EAAE,IAA6B;IAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,IAAK,CAA6B,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAI,CAAuB,CAAC,IAAI,CAAC;YAC1C,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,CAAC;QACD,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3E,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,qGAAqG;AACrG,SAAS,WAAW,CAAC,IAA6B,EAAE,IAA6B;IAC/E,OAAO,YAAY,CAAC,IAAI,EAAE,IAAI,CAA4B,CAAC;AAC7D,CAAC;AAED,gGAAgG;AAChG,SAAS,gBAAgB,CAAC,IAAU,EAAE,OAAoB;IACxD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,kBAAkB,CAAC,cAAc,EAAE,mDAAmD,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACjH,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,kBAAkB,CAAC,YAAY,EAAE,2CAA2C,CAAC,CAAC;AACvH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAgB,EAAE,KAAqC;IAChF,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,kBAAkB;YAAE,MAAM,CAAC,CAAC,CAAC,qCAAqC;QACnF,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,IAAmB;IACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAErD,MAAM,eAAe,GAAsB,EAAE,CAAC;IAC9C,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,UAAmB,CAAC;IAExB,IAAI,CAAC;QACH,IAAI,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1E,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEhC,4GAA4G;QAC5G,SAAS,CAAC;YACR,IAAI,aAAa,GAAG,KAAK,CAAC;YAE1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3C,IAAI,QAAQ,IAAI,QAAQ;oBAAE,MAAM,IAAI,kBAAkB,CAAC,WAAW,EAAE,kBAAkB,QAAQ,YAAY,CAAC,CAAC;gBAC5G,MAAM,IAAI,GAAa,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;gBACtC,MAAM,GAAG,GAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC1C,QAAQ,EAAE,CAAC;gBAEX,IAAI,GAAoB,CAAC;gBACzB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;oBACzD,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oBACjC,IAAI,IAAI,CAAC,GAAG;wBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;oBACtC,UAAU,GAAG,MAAM,CAAC;gBACtB,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,GAAG,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/E,CAAC;gBACD,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC1B,IAAI,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC;gBAEtB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,aAAa,GAAG,IAAI,CAAC;oBACrB,MAAM,CAAC,kDAAkD;gBAC3D,CAAC;YACH,CAAC;YAED,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YACpF,CAAC;YAED,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAG,eAAe,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBACzD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,aAAa,EAAE,CAAC;YAClG,CAAC;YACD,OAAO,EAAE,CAAC;YACV,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAAC;YACrG,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,CAAC,YAAY,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvH,oFAAoF;QACpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IACnF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Intent, Plan, PlanStep, ToolSpec, StepObservation } from './types.js';
|
|
2
|
+
/** What a planner is given to produce (or re-produce) a plan. */
|
|
3
|
+
export interface PlanInput {
|
|
4
|
+
intent: Intent;
|
|
5
|
+
/** The tools the agent exposes — the planner MUST pick from these (fail-closed elsewhere). */
|
|
6
|
+
tools: ToolSpec[];
|
|
7
|
+
/** Prior step observations, present only on a re-plan (empty / undefined on the first plan). */
|
|
8
|
+
observations?: StepObservation[];
|
|
9
|
+
}
|
|
10
|
+
/** The injected planning port: turn a goal + the available tools into an ordered tool composition. */
|
|
11
|
+
export interface Planner {
|
|
12
|
+
plan(input: PlanInput): Promise<Plan>;
|
|
13
|
+
}
|
|
14
|
+
/** One deterministic rule. When the goal matches, emit either a single step (`toolId` + `args`) OR a
|
|
15
|
+
* multi-step plan (`steps`) — later steps may reference earlier results via `{ $ref: 'name.path' }`. A rule
|
|
16
|
+
* is SKIPPED if any tool it names is not in the provided `tools` (fail-closed; the next rule is tried). */
|
|
17
|
+
export interface PlanRule {
|
|
18
|
+
/** Regex tested against the lowercased goal, or a predicate over the raw goal. */
|
|
19
|
+
match: RegExp | ((goal: string) => boolean);
|
|
20
|
+
/** Single-step: the tool to invoke. Provide this OR `steps`. */
|
|
21
|
+
toolId?: string;
|
|
22
|
+
/** Static args, or a function of the raw goal. Defaults to `{}`. (Single-step only.) */
|
|
23
|
+
args?: Record<string, unknown> | ((goal: string) => Record<string, unknown>);
|
|
24
|
+
/** Optional ref name to bind the (single) step's result under. (Single-step only.) */
|
|
25
|
+
ref?: string;
|
|
26
|
+
/** Multi-step: an explicit ordered plan, or a function of the raw goal. Provide this OR `toolId`. */
|
|
27
|
+
steps?: PlanStep[] | ((goal: string) => PlanStep[]);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The deterministic default planner: maps a goal to a single-tool plan via the FIRST matching rule whose
|
|
31
|
+
* `toolId` is actually exposed in `tools`. No model, no creds, fully testable — the live default. Throws
|
|
32
|
+
* `OrchestrationError('no_plan')` when nothing matches (fail-closed; never an empty silent success).
|
|
33
|
+
*/
|
|
34
|
+
export declare function createRuleBasedPlanner(rules: PlanRule[]): Planner;
|
|
35
|
+
//# sourceMappingURL=planner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGpF,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,gGAAgG;IAChG,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;CAClC;AAED,sGAAsG;AACtG,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;4GAE4G;AAC5G,MAAM,WAAW,QAAQ;IACvB,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC5C,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qGAAqG;IACrG,KAAK,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,EAAE,CAAC,CAAC;CACrD;AAKD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAqBjE"}
|
package/dist/planner.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { OrchestrationError } from './types.js';
|
|
2
|
+
const matches = (rule, goal) => typeof rule.match === 'function' ? rule.match(goal) : rule.match.test(goal.toLowerCase());
|
|
3
|
+
/**
|
|
4
|
+
* The deterministic default planner: maps a goal to a single-tool plan via the FIRST matching rule whose
|
|
5
|
+
* `toolId` is actually exposed in `tools`. No model, no creds, fully testable — the live default. Throws
|
|
6
|
+
* `OrchestrationError('no_plan')` when nothing matches (fail-closed; never an empty silent success).
|
|
7
|
+
*/
|
|
8
|
+
export function createRuleBasedPlanner(rules) {
|
|
9
|
+
return {
|
|
10
|
+
async plan({ intent, tools }) {
|
|
11
|
+
const available = new Set(tools.map((t) => t.id));
|
|
12
|
+
for (const rule of rules) {
|
|
13
|
+
if (!matches(rule, intent.goal))
|
|
14
|
+
continue;
|
|
15
|
+
// Multi-step rule: emit the full template (skip the rule if any tool isn't exposed).
|
|
16
|
+
if (rule.steps) {
|
|
17
|
+
const steps = typeof rule.steps === 'function' ? rule.steps(intent.goal) : rule.steps;
|
|
18
|
+
if (!steps.every((s) => available.has(s.toolId)))
|
|
19
|
+
continue;
|
|
20
|
+
return { steps, rationale: `rule-based: matched "${intent.goal}" → ${steps.map((s) => s.toolId).join(' + ')}` };
|
|
21
|
+
}
|
|
22
|
+
// Single-step rule.
|
|
23
|
+
if (!rule.toolId || !available.has(rule.toolId))
|
|
24
|
+
continue; // unset or unexposed tool — skip
|
|
25
|
+
const args = typeof rule.args === 'function' ? rule.args(intent.goal) : (rule.args ?? {});
|
|
26
|
+
const step = rule.ref ? { toolId: rule.toolId, args, ref: rule.ref } : { toolId: rule.toolId, args };
|
|
27
|
+
return { steps: [step], rationale: `rule-based: matched "${intent.goal}" → ${rule.toolId}` };
|
|
28
|
+
}
|
|
29
|
+
throw new OrchestrationError('no_plan', `no rule satisfies the goal: "${intent.goal}"`);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgChD,MAAM,OAAO,GAAG,CAAC,IAAc,EAAE,IAAY,EAAW,EAAE,CACxD,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAE5F;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAiB;IACtD,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAa;YACrC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAClD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAC1C,qFAAqF;gBACrF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;oBACtF,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;wBAAE,SAAS;oBAC3D,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,wBAAwB,MAAM,CAAC,IAAI,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBAClH,CAAC;gBACD,oBAAoB;gBACpB,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;oBAAE,SAAS,CAAC,iCAAiC;gBAC5F,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBAC1F,MAAM,IAAI,GAAa,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;gBAC/G,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,wBAAwB,MAAM,CAAC,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YAC/F,CAAC;YACD,MAAM,IAAI,kBAAkB,CAAC,SAAS,EAAE,gCAAgC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;QAC1F,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Intent, PlanStep } from './types.js';
|
|
2
|
+
/** Context handed to the invoker for one step (for logging / args resolution; never authority). */
|
|
3
|
+
export interface InvokeContext {
|
|
4
|
+
intent: Intent;
|
|
5
|
+
step: PlanStep;
|
|
6
|
+
/** 0-based index of this step within the current plan. */
|
|
7
|
+
index: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Execute a single tool. Resolve with the tool's result, or REJECT to signal a step failure (the loop then
|
|
11
|
+
* re-plans, bounded). A resolved value is treated as success — an invoker that wants to surface a tool-level
|
|
12
|
+
* error as a failure must throw (fail-closed; the loop does not inspect result shapes for hidden errors).
|
|
13
|
+
*/
|
|
14
|
+
export type ToolInvoker = (toolId: string, args: Record<string, unknown>, ctx: InvokeContext) => Promise<unknown>;
|
|
15
|
+
//# sourceMappingURL=tool-invoker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-invoker.d.ts","sourceRoot":"","sources":["../src/tool-invoker.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnD,mGAAmG;AACnG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,aAAa,KACf,OAAO,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-invoker.js","sourceRoot":"","sources":["../src/tool-invoker.ts"],"names":[],"mappings":""}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME: "@agenticprimitives/orchestration";
|
|
2
|
+
export declare const PACKAGE_STATUS: "w1-core";
|
|
3
|
+
export declare const SPEC_REF: "packages/orchestration/spec.md";
|
|
4
|
+
/** A declarative goal posted by a first-party surface. NOT a tool name, payload, or call sequence. */
|
|
5
|
+
export interface Intent {
|
|
6
|
+
/** The natural-language / declarative objective, e.g. "read my profile". */
|
|
7
|
+
goal: string;
|
|
8
|
+
/** Optional hard constraints the planner must respect (budget, deadline, scope). Opaque to the loop. */
|
|
9
|
+
constraints?: Record<string, unknown>;
|
|
10
|
+
/** Optional context the planner may use (principal handle, locale…). Opaque to the loop. */
|
|
11
|
+
context?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
/** A capability the agent MAY compose. The planner picks from these; the loop refuses any other tool id. */
|
|
14
|
+
export interface ToolSpec {
|
|
15
|
+
/** Stable tool id (matches the invoker's dispatch key, e.g. an MCP tool name). */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Human/LLM-readable description — what the tool does + when to use it. Untrusted-by-client (SEP). */
|
|
18
|
+
description: string;
|
|
19
|
+
/** Optional JSON-Schema for the tool's args (the LLM planner uses it as the tool-use input schema). */
|
|
20
|
+
inputSchema?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
/** One step of a plan: invoke `toolId` with `args`; optionally bind the result under `ref` for later steps. */
|
|
23
|
+
export interface PlanStep {
|
|
24
|
+
toolId: string;
|
|
25
|
+
args: Record<string, unknown>;
|
|
26
|
+
/** Name to store this step's result under; later steps reference it via `{ $ref: name }` in their args. */
|
|
27
|
+
ref?: string;
|
|
28
|
+
}
|
|
29
|
+
/** The ordered tool composition the planner produced for an intent. */
|
|
30
|
+
export interface Plan {
|
|
31
|
+
steps: PlanStep[];
|
|
32
|
+
/** Optional one-line reasoning (audit/telemetry only — never trusted as authority). */
|
|
33
|
+
rationale?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The recorded outcome of one executed step (fed back into a re-plan). */
|
|
36
|
+
export interface StepObservation {
|
|
37
|
+
step: PlanStep;
|
|
38
|
+
ok: boolean;
|
|
39
|
+
result?: unknown;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
export type RunOutcome = 'completed' | 'failed';
|
|
43
|
+
/** The result of running an intent to completion (or fail-closed). */
|
|
44
|
+
export interface RunResult {
|
|
45
|
+
outcome: RunOutcome;
|
|
46
|
+
/** The final plan that was executed (the last re-plan, if any). */
|
|
47
|
+
plan: Plan;
|
|
48
|
+
/** Every step observation, in execution order, across all (re-)plans. */
|
|
49
|
+
steps: StepObservation[];
|
|
50
|
+
/** The last successful step's result — the composed answer returned to the caller. */
|
|
51
|
+
result?: unknown;
|
|
52
|
+
/** Set when `outcome === 'failed'`. */
|
|
53
|
+
error?: string;
|
|
54
|
+
}
|
|
55
|
+
/** Stable error codes the loop / planners raise (fail-closed; never a silent empty success). */
|
|
56
|
+
export type OrchestrationErrorCode = 'no_plan' | 'unknown_tool' | 'empty_plan' | 'max_steps' | 'planner_failed';
|
|
57
|
+
export declare class OrchestrationError extends Error {
|
|
58
|
+
readonly code: OrchestrationErrorCode;
|
|
59
|
+
constructor(code: OrchestrationErrorCode, message: string);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,YAAY,EAAG,kCAA2C,CAAC;AACxE,eAAO,MAAM,cAAc,EAAG,SAAkB,CAAC;AACjD,eAAO,MAAM,QAAQ,EAAG,gCAAyC,CAAC;AAElE,sGAAsG;AACtG,MAAM,WAAW,MAAM;IACrB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,wGAAwG;IACxG,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,4FAA4F;IAC5F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,4GAA4G;AAC5G,MAAM,WAAW,QAAQ;IACvB,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,uGAAuG;IACvG,WAAW,EAAE,MAAM,CAAC;IACpB,uGAAuG;IACvG,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,+GAA+G;AAC/G,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,2GAA2G;IAC3G,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEhD,sEAAsE;AACtE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,UAAU,CAAC;IACpB,mEAAmE;IACnE,IAAI,EAAE,IAAI,CAAC;IACX,yEAAyE;IACzE,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,gGAAgG;AAChG,MAAM,MAAM,sBAAsB,GAC9B,SAAS,GACT,cAAc,GACd,YAAY,GACZ,WAAW,GACX,gBAAgB,CAAC;AAErB,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,IAAI,EAAE,sBAAsB;gBAA5B,IAAI,EAAE,sBAAsB,EAC5C,OAAO,EAAE,MAAM;CAKlB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @agenticprimitives/orchestration — the intent→plan→execute→observe model (the Ring-0 agentic loop).
|
|
2
|
+
//
|
|
3
|
+
// This is a PURE leaf: the model, the ports, and the loop only. No transport (a2a), no LLM SDK
|
|
4
|
+
// (orchestration-anthropic), no chain reads. A first-party app expresses an `Intent` (a GOAL, never a tool
|
|
5
|
+
// name); a `Planner` decides which `ToolSpec`s compose to satisfy it; the loop executes the resulting `Plan`
|
|
6
|
+
// through an injected `ToolInvoker`, observes, and re-plans (bounded) on failure. The concrete tools are MCP
|
|
7
|
+
// tools the a2a agent exposes — composed AS PRIMITIVES (ADR-0044). The web never names a tool; the agent does.
|
|
8
|
+
export const PACKAGE_NAME = '@agenticprimitives/orchestration';
|
|
9
|
+
export const PACKAGE_STATUS = 'w1-core';
|
|
10
|
+
export const SPEC_REF = 'packages/orchestration/spec.md';
|
|
11
|
+
export class OrchestrationError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
constructor(code, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = 'OrchestrationError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,EAAE;AACF,+FAA+F;AAC/F,2GAA2G;AAC3G,6GAA6G;AAC7G,6GAA6G;AAC7G,+GAA+G;AAE/G,MAAM,CAAC,MAAM,YAAY,GAAG,kCAA2C,CAAC;AACxE,MAAM,CAAC,MAAM,cAAc,GAAG,SAAkB,CAAC;AACjD,MAAM,CAAC,MAAM,QAAQ,GAAG,gCAAyC,CAAC;AAoElE,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAEzB;IADlB,YACkB,IAA4B,EAC5C,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAwB;QAI5C,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agenticprimitives/orchestration",
|
|
3
|
+
"version": "0.0.0-alpha.1",
|
|
4
|
+
"description": "The Ring-0 agentic orchestration core (ADR-0044): the intent→plan→execute→observe loop, the Planner + ToolInvoker ports, and a deterministic rule-based planner. A first-party app expresses a goal; the loop plans which tools compose to satisfy it and runs them through an injected invoker. Pure leaf — no transport, no LLM SDK, no chain; the concrete LLM binding lives behind the Planner port in orchestration-anthropic.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/agentictrustlabs/agenticprimitives.git",
|
|
9
|
+
"directory": "packages/orchestration"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/agentictrustlabs/agenticprimitives/tree/master/packages/orchestration",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/agentictrustlabs/agenticprimitives/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"dist",
|
|
27
|
+
"spec.md",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"vitest": "^4.1.8"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"agentic",
|
|
38
|
+
"primitives",
|
|
39
|
+
"orchestration",
|
|
40
|
+
"planner",
|
|
41
|
+
"intent",
|
|
42
|
+
"agentic-loop"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.build.json",
|
|
46
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:unit": "vitest run test/unit --passWithNoTests",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"clean": "rm -rf dist"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/spec.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# orchestration — the Ring-0 agentic loop
|
|
2
|
+
|
|
3
|
+
**Status:** w1-core. **Doctrine:** [ADR-0044](../../docs/architecture/decisions/0044-a2a-is-the-agentic-orchestration-layer.md)
|
|
4
|
+
+ [docs/architecture/principles.md](../../docs/architecture/principles.md) §1 + memory `feedback_orchestration_is_ring0`.
|
|
5
|
+
|
|
6
|
+
## 1. Why
|
|
7
|
+
|
|
8
|
+
The doctrine: a first-party UI expresses a declarative **intent** (a goal) to an a2a agent, which **plans**
|
|
9
|
+
which capability satisfies it and **composes tools as primitives** — `web → edge → /api/a2a → a2a skills →
|
|
10
|
+
MCP`. The web never names a tool. **Orchestration — the intent→plan→execute→observe loop, the Planner PORT,
|
|
11
|
+
and the tool model — is a Ring-0 primitive** in a pure package; ONLY the concrete LLM binding is an in-repo
|
|
12
|
+
adapter behind the port (the `chain-state` / `chain-state-viem` pattern). This package is that core.
|
|
13
|
+
|
|
14
|
+
## 2. The model
|
|
15
|
+
|
|
16
|
+
- `Intent { goal; constraints?; context? }` — a GOAL, never a tool name / payload / call sequence.
|
|
17
|
+
- `ToolSpec { id; description; inputSchema? }` — a capability the agent MAY compose (an MCP tool, typically).
|
|
18
|
+
- `Plan { steps: PlanStep[]; rationale? }`, `PlanStep { toolId; args; ref? }` — the ordered composition.
|
|
19
|
+
- `StepObservation { step; ok; result?; error? }` — fed back into a re-plan.
|
|
20
|
+
- `RunResult { outcome: 'completed'|'failed'; plan; steps; result?; error? }`.
|
|
21
|
+
|
|
22
|
+
## 3. The ports (injected)
|
|
23
|
+
|
|
24
|
+
- `Planner { plan({ intent, tools, observations? }): Promise<Plan> }` — turns a goal + the exposed tools into
|
|
25
|
+
a plan. The deterministic `createRuleBasedPlanner(rules)` is the default; the LLM adapter implements the SAME
|
|
26
|
+
port. A planner MUST pick only from the provided `tools`.
|
|
27
|
+
- `ToolInvoker (toolId, args, ctx): Promise<unknown>` — executes one step. The a2a `orchestrate` skill binds it
|
|
28
|
+
to `ctx.mcp.callTool` under the **task's delegation**, so every composed call rides on-chain authority.
|
|
29
|
+
|
|
30
|
+
## 4. The loop — `runIntent(intent, opts)`
|
|
31
|
+
|
|
32
|
+
1. Plan with `{ intent, tools }`. Fail-closed: a plan that names an unexposed tool → `unknown_tool`; a
|
|
33
|
+
zero-step plan → `empty_plan`; the planner producing nothing → `no_plan`.
|
|
34
|
+
2. Execute steps in order. Resolve `{ $ref: 'name' }` args from prior step results. Bind a step's result under
|
|
35
|
+
`step.ref` for later steps. Cap total executions at `maxSteps` (default 8).
|
|
36
|
+
3. On a step throw: record the failed observation and **re-plan** (passing all observations), bounded by
|
|
37
|
+
`maxReplans` (default 1). Exhausted → `failed` with the last error.
|
|
38
|
+
4. All steps succeed → `completed` with the last successful result.
|
|
39
|
+
|
|
40
|
+
Fail-closed everywhere (ADR-0013): no plan / empty plan / unknown tool / exhausted budget all return a `failed`
|
|
41
|
+
`RunResult` — never a misleading empty success.
|
|
42
|
+
|
|
43
|
+
## 5. Boundaries
|
|
44
|
+
|
|
45
|
+
Pure leaf (only `@agenticprimitives/types` allowed, currently unused). No transport, no LLM SDK, no chain. The
|
|
46
|
+
LLM binding is `@agenticprimitives/orchestration-anthropic`. `service-agent` is the deterministic compile/run
|
|
47
|
+
target; this core PRODUCES plans and is a separate concern (not folded in).
|
|
48
|
+
|
|
49
|
+
## 6. Out of scope (this wave)
|
|
50
|
+
|
|
51
|
+
Multi-tool graph reasoning beyond linear steps + `$ref` threading; parallel step execution; service-agent as
|
|
52
|
+
the executor; planner-side cost/latency optimization. W1 = linear plans + bounded re-plan, deterministic + LLM
|
|
53
|
+
planners behind one port.
|