@agenteer/stdlib 1.0.0-rc.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 +98 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/context/context_curator.d.ts +115 -0
- package/dist/context/context_curator.d.ts.map +1 -0
- package/dist/context/context_curator.js +132 -0
- package/dist/context/context_curator.js.map +1 -0
- package/dist/humans/approval_gate.d.ts +82 -0
- package/dist/humans/approval_gate.d.ts.map +1 -0
- package/dist/humans/approval_gate.js +76 -0
- package/dist/humans/approval_gate.js.map +1 -0
- package/dist/humans/ask_user.d.ts +79 -0
- package/dist/humans/ask_user.d.ts.map +1 -0
- package/dist/humans/ask_user.js +101 -0
- package/dist/humans/ask_user.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/meta/cross_check.d.ts +96 -0
- package/dist/meta/cross_check.d.ts.map +1 -0
- package/dist/meta/cross_check.js +165 -0
- package/dist/meta/cross_check.js.map +1 -0
- package/dist/meta/judge_with_stripped_ctx.d.ts +75 -0
- package/dist/meta/judge_with_stripped_ctx.d.ts.map +1 -0
- package/dist/meta/judge_with_stripped_ctx.js +151 -0
- package/dist/meta/judge_with_stripped_ctx.js.map +1 -0
- package/dist/meta/parallel_fanout.d.ts +83 -0
- package/dist/meta/parallel_fanout.d.ts.map +1 -0
- package/dist/meta/parallel_fanout.js +120 -0
- package/dist/meta/parallel_fanout.js.map +1 -0
- package/dist/meta/repair_loop.d.ts +90 -0
- package/dist/meta/repair_loop.d.ts.map +1 -0
- package/dist/meta/repair_loop.js +230 -0
- package/dist/meta/repair_loop.js.map +1 -0
- package/dist/planner/default_planner.d.ts +88 -0
- package/dist/planner/default_planner.d.ts.map +1 -0
- package/dist/planner/default_planner.js +156 -0
- package/dist/planner/default_planner.js.map +1 -0
- package/dist/primitives/file_read.d.ts +60 -0
- package/dist/primitives/file_read.d.ts.map +1 -0
- package/dist/primitives/file_read.js +68 -0
- package/dist/primitives/file_read.js.map +1 -0
- package/dist/primitives/file_write.d.ts +60 -0
- package/dist/primitives/file_write.d.ts.map +1 -0
- package/dist/primitives/file_write.js +66 -0
- package/dist/primitives/file_write.js.map +1 -0
- package/dist/primitives/llm_call.d.ts +85 -0
- package/dist/primitives/llm_call.d.ts.map +1 -0
- package/dist/primitives/llm_call.js +99 -0
- package/dist/primitives/llm_call.js.map +1 -0
- package/dist/primitives/shell_exec.d.ts +66 -0
- package/dist/primitives/shell_exec.d.ts.map +1 -0
- package/dist/primitives/shell_exec.js +106 -0
- package/dist/primitives/shell_exec.js.map +1 -0
- package/dist/primitives/tool_call.d.ts +62 -0
- package/dist/primitives/tool_call.d.ts.map +1 -0
- package/dist/primitives/tool_call.js +69 -0
- package/dist/primitives/tool_call.js.map +1 -0
- package/dist/shared/capture.d.ts +34 -0
- package/dist/shared/capture.d.ts.map +1 -0
- package/dist/shared/capture.js +124 -0
- package/dist/shared/capture.js.map +1 -0
- package/dist/shared/ctx.d.ts +14 -0
- package/dist/shared/ctx.d.ts.map +1 -0
- package/dist/shared/ctx.js +48 -0
- package/dist/shared/ctx.js.map +1 -0
- package/dist/shared/index.d.ts +4 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/validator.d.ts +47 -0
- package/dist/shared/validator.d.ts.map +1 -0
- package/dist/shared/validator.js +41 -0
- package/dist/shared/validator.js.map +1 -0
- package/dist/validators/compile.d.ts +83 -0
- package/dist/validators/compile.d.ts.map +1 -0
- package/dist/validators/compile.js +126 -0
- package/dist/validators/compile.js.map +1 -0
- package/dist/validators/json_schema_validate.d.ts +72 -0
- package/dist/validators/json_schema_validate.d.ts.map +1 -0
- package/dist/validators/json_schema_validate.js +85 -0
- package/dist/validators/json_schema_validate.js.map +1 -0
- package/dist/validators/regex_check.d.ts +64 -0
- package/dist/validators/regex_check.d.ts.map +1 -0
- package/dist/validators/regex_check.js +85 -0
- package/dist/validators/regex_check.js.map +1 -0
- package/dist/validators/test_run.d.ts +74 -0
- package/dist/validators/test_run.d.ts.map +1 -0
- package/dist/validators/test_run.js +149 -0
- package/dist/validators/test_run.js.map +1 -0
- package/dist/validators/typecheck.d.ts +61 -0
- package/dist/validators/typecheck.d.ts.map +1 -0
- package/dist/validators/typecheck.js +89 -0
- package/dist/validators/typecheck.js.map +1 -0
- package/package.json +61 -0
- package/src/context/context_curator.ts +154 -0
- package/src/humans/approval_gate.ts +101 -0
- package/src/humans/ask_user.ts +137 -0
- package/src/index.ts +149 -0
- package/src/meta/cross_check.ts +219 -0
- package/src/meta/judge_with_stripped_ctx.ts +171 -0
- package/src/meta/parallel_fanout.ts +142 -0
- package/src/meta/repair_loop.ts +267 -0
- package/src/planner/default_planner.ts +182 -0
- package/src/primitives/file_read.ts +82 -0
- package/src/primitives/file_write.ts +80 -0
- package/src/primitives/llm_call.ts +113 -0
- package/src/primitives/shell_exec.ts +122 -0
- package/src/primitives/tool_call.ts +84 -0
- package/src/shared/capture.ts +155 -0
- package/src/shared/ctx.ts +45 -0
- package/src/shared/index.ts +17 -0
- package/src/shared/validator.ts +51 -0
- package/src/validators/compile.ts +175 -0
- package/src/validators/json_schema_validate.ts +103 -0
- package/src/validators/regex_check.ts +107 -0
- package/src/validators/test_run.ts +199 -0
- package/src/validators/typecheck.ts +121 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@agenteer/node-typecheck` — thin wrapper over `compile` with
|
|
3
|
+
* types-only flags (sub-plan 03 §10).
|
|
4
|
+
*
|
|
5
|
+
* v1 supports TypeScript (`tsc --noEmit --strict`). The wrapper exists
|
|
6
|
+
* separately from `compile` so planners can distinguish "types only"
|
|
7
|
+
* from "full build"; the underlying adapter is shared.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { makeManifest, authorizeOperation, } from "@agenteer/core";
|
|
11
|
+
import { failOutput, passOutput, runCommand, tailOf, ValidatorOutputSchema, } from "../shared/index.js";
|
|
12
|
+
const MANIFEST = makeManifest({
|
|
13
|
+
id: "@agenteer/node-typecheck",
|
|
14
|
+
name: "typecheck",
|
|
15
|
+
description: "Types-only check (v1: TypeScript tsc --noEmit --strict).",
|
|
16
|
+
determinism: "deterministic",
|
|
17
|
+
required_actions: ["shell.exec:"],
|
|
18
|
+
dynamic_actions: true,
|
|
19
|
+
dynamic_action_spec: "fs.read:${input.cwd}",
|
|
20
|
+
tags: ["validator"],
|
|
21
|
+
});
|
|
22
|
+
const InputSchema = z.object({
|
|
23
|
+
language: z.enum(["typescript"]),
|
|
24
|
+
cwd: z.string().min(1),
|
|
25
|
+
tsconfig: z.string().optional(),
|
|
26
|
+
strict: z.boolean().default(true),
|
|
27
|
+
timeout_ms: z.number().int().min(1).max(600_000).default(300_000),
|
|
28
|
+
});
|
|
29
|
+
const TSC_ISSUE_RE = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+TS\d+:\s+(.*)$/gm;
|
|
30
|
+
export function typecheckFactory() {
|
|
31
|
+
return {
|
|
32
|
+
manifest: MANIFEST,
|
|
33
|
+
inputSchema: InputSchema,
|
|
34
|
+
outputSchema: ValidatorOutputSchema,
|
|
35
|
+
ctx: [],
|
|
36
|
+
model: null,
|
|
37
|
+
async execute(input, handle) {
|
|
38
|
+
const { cwd, tsconfig } = input.original;
|
|
39
|
+
const strict = input.original.strict ?? true;
|
|
40
|
+
const timeout_ms = input.original.timeout_ms ?? 300_000;
|
|
41
|
+
try {
|
|
42
|
+
authorizeOperation(handle.granted, { kind: "shell.exec" });
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return {
|
|
46
|
+
kind: "failed",
|
|
47
|
+
reason: `permission_denied: ${err instanceof Error ? err.message : String(err)}`,
|
|
48
|
+
retryable: false,
|
|
49
|
+
evidence: { verdict: "fail" },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const tsconfigArg = tsconfig ? `--project ${JSON.stringify(tsconfig)}` : "";
|
|
53
|
+
const strictFlag = strict ? "--strict" : "";
|
|
54
|
+
const command = `npx -y tsc --noEmit ${tsconfigArg} ${strictFlag}`.replace(/\s+/g, " ").trim();
|
|
55
|
+
try {
|
|
56
|
+
const r = await runCommand(command, { cwd, timeout_ms, signal: handle.signal });
|
|
57
|
+
const combined = `${r.stdout}\n${r.stderr}`;
|
|
58
|
+
const issues = [];
|
|
59
|
+
for (const m of combined.matchAll(TSC_ISSUE_RE)) {
|
|
60
|
+
issues.push({
|
|
61
|
+
path: `${m[1]}:${m[2]}:${m[3]}`,
|
|
62
|
+
message: m[5].trim(),
|
|
63
|
+
code: "TS",
|
|
64
|
+
severity: m[4] === "warning" ? "warning" : "error",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const verdict = r.exit_code === 0 && issues.length === 0 ? "pass" : "fail";
|
|
68
|
+
const out = verdict === "pass"
|
|
69
|
+
? { ...passOutput(`typecheck clean (${r.duration_ms}ms)`), exit_code: r.exit_code }
|
|
70
|
+
: failOutput(issues.length ? issues : [{ message: `tsc exited ${r.exit_code}`, severity: "error" }], `typecheck: ${issues.length} issue(s)`, {
|
|
71
|
+
exit_code: r.exit_code,
|
|
72
|
+
stdout_tail: tailOf(r.stdout),
|
|
73
|
+
stderr_tail: tailOf(r.stderr),
|
|
74
|
+
});
|
|
75
|
+
return { kind: "output", value: out, evidence: { verdict } };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return {
|
|
79
|
+
kind: "failed",
|
|
80
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
81
|
+
retryable: true,
|
|
82
|
+
evidence: { verdict: "fail" },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export const typecheckManifest = MANIFEST;
|
|
89
|
+
//# sourceMappingURL=typecheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typecheck.js","sourceRoot":"","sources":["../../src/validators/typecheck.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agenteer/stdlib",
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
|
+
"description": "Agenteer standard library — 19 hardened, publishable nodes (primitives, validators, meta, humans, planner, context).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agenteer",
|
|
7
|
+
"agent",
|
|
8
|
+
"stdlib",
|
|
9
|
+
"nodes",
|
|
10
|
+
"primitives",
|
|
11
|
+
"validators",
|
|
12
|
+
"planner",
|
|
13
|
+
"llm",
|
|
14
|
+
"tools"
|
|
15
|
+
],
|
|
16
|
+
"author": "salehsquared (https://github.com/salehsquared)",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"homepage": "https://github.com/salehsquared/agenteer#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/salehsquared/agenteer/issues"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/salehsquared/agenteer.git",
|
|
25
|
+
"directory": "packages/stdlib"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -b",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"test": "vitest run"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@agenteer/core": "1.0.0-rc.1",
|
|
55
|
+
"@agenteer/trust": "1.0.0-rc.1",
|
|
56
|
+
"zod": "^4.3.6"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"vitest": "^3.2.4"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@agenteer/node-context-curator` — the 18th v1 node (master plan open
|
|
3
|
+
* #15 — **shipped in v1**). Sub-plan 01 §7.
|
|
4
|
+
*
|
|
5
|
+
* v1 scope: **query curator** only (sub-plan 01 §7.3 mode A). Given a
|
|
6
|
+
* task description and a target manifest, emit a deterministic
|
|
7
|
+
* `SliceSpec` that downstream nodes / planners can consume. Condensing
|
|
8
|
+
* and pedagogical modes are v2.
|
|
9
|
+
*
|
|
10
|
+
* Emits a `Decision` item recording the curation choice so a later
|
|
11
|
+
* `judge_with_stripped_ctx` can detect motivated reasoning.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import {
|
|
16
|
+
asArtifact,
|
|
17
|
+
makeManifest,
|
|
18
|
+
type Node,
|
|
19
|
+
type NodeInput,
|
|
20
|
+
type NodeManifest,
|
|
21
|
+
type NodeResult,
|
|
22
|
+
} from "@agenteer/core";
|
|
23
|
+
|
|
24
|
+
const MANIFEST: NodeManifest = makeManifest({
|
|
25
|
+
id: "@agenteer/node-context-curator",
|
|
26
|
+
name: "context_curator",
|
|
27
|
+
description:
|
|
28
|
+
"Emit a deterministic SliceSpec for a downstream node based on task description and target manifest.",
|
|
29
|
+
determinism: "deterministic",
|
|
30
|
+
required_actions: [],
|
|
31
|
+
tags: ["meta", "curator"],
|
|
32
|
+
side_effects: {
|
|
33
|
+
writes_fs: false,
|
|
34
|
+
network: false,
|
|
35
|
+
mutates_ctx: true,
|
|
36
|
+
emits_ctx_variants: ["decision.curation", "artifact.slice_spec"],
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const ContextItemTypeSchema = z.enum([
|
|
41
|
+
"artifact",
|
|
42
|
+
"observation",
|
|
43
|
+
"decision",
|
|
44
|
+
"evidence_ref",
|
|
45
|
+
"claim",
|
|
46
|
+
"reference",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const SelectorSchema = z.object({
|
|
50
|
+
ids: z.array(z.string()).optional(),
|
|
51
|
+
types: z.array(ContextItemTypeSchema).optional(),
|
|
52
|
+
tags: z
|
|
53
|
+
.object({
|
|
54
|
+
any: z.array(z.string()).optional(),
|
|
55
|
+
all: z.array(z.string()).optional(),
|
|
56
|
+
none: z.array(z.string()).optional(),
|
|
57
|
+
})
|
|
58
|
+
.optional(),
|
|
59
|
+
limit: z.number().int().positive().optional(),
|
|
60
|
+
order: z.enum(["oldest", "newest"]).optional(),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
type Selector = z.infer<typeof SelectorSchema>;
|
|
64
|
+
|
|
65
|
+
const InputSchema = z.object({
|
|
66
|
+
task: z.string().min(1),
|
|
67
|
+
target_manifest_id: z.string().min(1),
|
|
68
|
+
/** Optional hints; the curator treats these as selector constraints. */
|
|
69
|
+
hints: z
|
|
70
|
+
.object({
|
|
71
|
+
include_tags: z.array(z.string()).optional(),
|
|
72
|
+
exclude_tags: z.array(z.string()).optional(),
|
|
73
|
+
types: z.array(ContextItemTypeSchema).optional(),
|
|
74
|
+
limit: z.number().int().positive().optional(),
|
|
75
|
+
})
|
|
76
|
+
.optional(),
|
|
77
|
+
/** Name under which the slice spec is emitted to ctx. */
|
|
78
|
+
emit_as: z.string().default("slice_spec"),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const SliceSpecSchema = z.object({
|
|
82
|
+
name: z.string(),
|
|
83
|
+
selector: SelectorSchema,
|
|
84
|
+
stale_policy: z.enum(["reject", "recompute", "warn", "allow"]),
|
|
85
|
+
freeze: z.enum(["snapshot", "live"]),
|
|
86
|
+
budget: z
|
|
87
|
+
.object({
|
|
88
|
+
max_items: z.number().int().positive().optional(),
|
|
89
|
+
max_bytes: z.number().int().positive().optional(),
|
|
90
|
+
})
|
|
91
|
+
.optional(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const OutputSchema = z.object({
|
|
95
|
+
slice_spec: SliceSpecSchema,
|
|
96
|
+
rationale: z.string(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
type Input = z.input<typeof InputSchema>;
|
|
100
|
+
type Output = z.infer<typeof OutputSchema>;
|
|
101
|
+
|
|
102
|
+
export function contextCuratorFactory(): Node<Input, Output> {
|
|
103
|
+
return {
|
|
104
|
+
manifest: MANIFEST,
|
|
105
|
+
inputSchema: InputSchema,
|
|
106
|
+
outputSchema: OutputSchema,
|
|
107
|
+
ctx: [],
|
|
108
|
+
model: null,
|
|
109
|
+
async execute(input: NodeInput<Input>): Promise<NodeResult<Output>> {
|
|
110
|
+
const i = input.original;
|
|
111
|
+
const hints = i.hints ?? {};
|
|
112
|
+
const emitAs = i.emit_as ?? "slice_spec";
|
|
113
|
+
|
|
114
|
+
const selector: Selector = { order: "newest" };
|
|
115
|
+
if (hints.types && hints.types.length) selector.types = hints.types;
|
|
116
|
+
if (hints.include_tags?.length || hints.exclude_tags?.length) {
|
|
117
|
+
const tags: NonNullable<Selector["tags"]> = {};
|
|
118
|
+
if (hints.include_tags?.length) tags.any = hints.include_tags;
|
|
119
|
+
if (hints.exclude_tags?.length) tags.none = hints.exclude_tags;
|
|
120
|
+
selector.tags = tags;
|
|
121
|
+
}
|
|
122
|
+
if (hints.limit !== undefined) selector.limit = hints.limit;
|
|
123
|
+
|
|
124
|
+
const spec = {
|
|
125
|
+
name: `curated.${i.target_manifest_id.replace(/[^a-z0-9]+/gi, "_")}`,
|
|
126
|
+
selector,
|
|
127
|
+
stale_policy: "warn" as const,
|
|
128
|
+
freeze: "snapshot" as const,
|
|
129
|
+
...(hints.limit !== undefined ? { budget: { max_items: hints.limit } } : {}),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const rationale =
|
|
133
|
+
`Curated a ${spec.freeze} slice for ${i.target_manifest_id} targeting "${i.task.slice(0, 80)}"` +
|
|
134
|
+
(hints.include_tags ? `; include_tags=[${hints.include_tags.join(",")}]` : "") +
|
|
135
|
+
(hints.exclude_tags ? `; exclude_tags=[${hints.exclude_tags.join(",")}]` : "") +
|
|
136
|
+
(hints.types ? `; types=[${hints.types.join(",")}]` : "");
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
kind: "output",
|
|
140
|
+
value: { slice_spec: spec, rationale },
|
|
141
|
+
ctx_patch: {
|
|
142
|
+
// Decision item recording the curation choice — judge-friendly.
|
|
143
|
+
set: {
|
|
144
|
+
[`curator.choice.${spec.name}`]: rationale,
|
|
145
|
+
[emitAs]: asArtifact(spec, { media_type: "application/json" }),
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
evidence: { verdict: "pass" },
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const contextCuratorManifest = MANIFEST;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@agenteer/node-approval-gate` — human yes/no gate (sub-plan 03 §16).
|
|
3
|
+
*
|
|
4
|
+
* M4 scope (Q3-C decision): ship this, defer `ask_user` to M5 where the
|
|
5
|
+
* CLI can prompt a real user. The approval_gate is deterministic given
|
|
6
|
+
* its recorded decision, so tests can inject an `ApprovalResolver` that
|
|
7
|
+
* returns a canned answer. Session resume wires a real CLI prompt in M5.
|
|
8
|
+
*
|
|
9
|
+
* When the resolver is absent, the node returns `NeedsUser` with a
|
|
10
|
+
* prompt — that's the production-pause-for-CLI path.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import {
|
|
15
|
+
makeManifest,
|
|
16
|
+
type Node,
|
|
17
|
+
type NodeInput,
|
|
18
|
+
type NodeManifest,
|
|
19
|
+
type NodeResult,
|
|
20
|
+
} from "@agenteer/core";
|
|
21
|
+
|
|
22
|
+
const MANIFEST: NodeManifest = makeManifest({
|
|
23
|
+
id: "@agenteer/node-approval-gate",
|
|
24
|
+
name: "approval_gate",
|
|
25
|
+
description:
|
|
26
|
+
"Yes/no gate. When an ApprovalResolver is injected, resolves synchronously (tests + replay); otherwise returns NeedsUser.",
|
|
27
|
+
determinism: "deterministic",
|
|
28
|
+
tags: ["human"],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const InputSchema = z.object({
|
|
32
|
+
prompt: z.string().min(1),
|
|
33
|
+
/**
|
|
34
|
+
* Deterministic marker — when `decision` is present, the gate echoes
|
|
35
|
+
* it immediately without asking. Enables replay of a recorded session.
|
|
36
|
+
*/
|
|
37
|
+
decision: z.enum(["approve", "deny"]).optional(),
|
|
38
|
+
/** Opaque id the resolver / CLI uses to look up the canned answer. */
|
|
39
|
+
decision_id: z.string().optional(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const OutputSchema = z.object({
|
|
43
|
+
decision: z.enum(["approve", "deny"]),
|
|
44
|
+
/** Echo of the prompt for audit. */
|
|
45
|
+
prompt: z.string(),
|
|
46
|
+
source: z.enum(["recorded", "resolver", "user"]),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
type Input = z.input<typeof InputSchema>;
|
|
50
|
+
type Output = z.infer<typeof OutputSchema>;
|
|
51
|
+
|
|
52
|
+
export interface ApprovalResolver {
|
|
53
|
+
/** Return `null` to fall through to `NeedsUser`. */
|
|
54
|
+
resolve(opts: { prompt: string; decision_id?: string }): "approve" | "deny" | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function approvalGateFactory(
|
|
58
|
+
resolver?: ApprovalResolver,
|
|
59
|
+
): () => Node<Input, Output> {
|
|
60
|
+
return () => ({
|
|
61
|
+
manifest: MANIFEST,
|
|
62
|
+
inputSchema: InputSchema,
|
|
63
|
+
outputSchema: OutputSchema,
|
|
64
|
+
ctx: [],
|
|
65
|
+
model: null,
|
|
66
|
+
async execute(input: NodeInput<Input>): Promise<NodeResult<Output>> {
|
|
67
|
+
const i = input.original;
|
|
68
|
+
|
|
69
|
+
if (i.decision !== undefined) {
|
|
70
|
+
return {
|
|
71
|
+
kind: "output",
|
|
72
|
+
value: { decision: i.decision, prompt: i.prompt, source: "recorded" },
|
|
73
|
+
evidence: { verdict: i.decision === "approve" ? "pass" : "fail" },
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (resolver) {
|
|
78
|
+
const ans = resolver.resolve({
|
|
79
|
+
prompt: i.prompt,
|
|
80
|
+
...(i.decision_id !== undefined ? { decision_id: i.decision_id } : {}),
|
|
81
|
+
});
|
|
82
|
+
if (ans !== null) {
|
|
83
|
+
return {
|
|
84
|
+
kind: "output",
|
|
85
|
+
value: { decision: ans, prompt: i.prompt, source: "resolver" },
|
|
86
|
+
evidence: { verdict: ans === "approve" ? "pass" : "fail" },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
kind: "needs_user",
|
|
93
|
+
prompt: i.prompt,
|
|
94
|
+
schema: OutputSchema,
|
|
95
|
+
resume_hint: i.decision_id ?? "approval_gate",
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const approvalGateManifest = MANIFEST;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@agenteer/node-ask-user` — freeform user prompt (sub-plan 03 §16, M5).
|
|
3
|
+
*
|
|
4
|
+
* M5 scope (Q3-C follow-through from M4): now that session resume + CLI
|
|
5
|
+
* ship together, we can pair `approval_gate`'s yes/no with `ask_user`'s
|
|
6
|
+
* freeform answer. Both share the same resolver model — a pre-recorded
|
|
7
|
+
* answer wins, else a resolver answers synchronously, else `NeedsUser`.
|
|
8
|
+
*
|
|
9
|
+
* The schema for the answer is caller-provided. Tests and production can
|
|
10
|
+
* validate the string with a Zod schema passed at factory time.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { z, type ZodType } from "zod";
|
|
14
|
+
import {
|
|
15
|
+
makeManifest,
|
|
16
|
+
type Node,
|
|
17
|
+
type NodeInput,
|
|
18
|
+
type NodeManifest,
|
|
19
|
+
type NodeResult,
|
|
20
|
+
} from "@agenteer/core";
|
|
21
|
+
|
|
22
|
+
const MANIFEST: NodeManifest = makeManifest({
|
|
23
|
+
id: "@agenteer/node-ask-user",
|
|
24
|
+
name: "ask_user",
|
|
25
|
+
description:
|
|
26
|
+
"Freeform user prompt. Recorded answer → resolver → NeedsUser fallback. Schema validates answers.",
|
|
27
|
+
determinism: "deterministic",
|
|
28
|
+
tags: ["human"],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const InputSchema = z.object({
|
|
32
|
+
prompt: z.string().min(1),
|
|
33
|
+
/** Deterministic marker — echo back the recorded answer without asking. */
|
|
34
|
+
answer: z.unknown().optional(),
|
|
35
|
+
/**
|
|
36
|
+
* Opaque identifier used to match a resolver / CLI answer. If absent,
|
|
37
|
+
* the NodeResult's resume_hint defaults to `ask_user:<prompt-slug>`.
|
|
38
|
+
*/
|
|
39
|
+
question_id: z.string().optional(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
type Input = z.input<typeof InputSchema>;
|
|
43
|
+
|
|
44
|
+
export interface AskUserResolver {
|
|
45
|
+
/**
|
|
46
|
+
* Return the recorded answer for this prompt, or `null` to fall
|
|
47
|
+
* through to NeedsUser. Implementations typically bind to a session
|
|
48
|
+
* state; see `recordedAnswerResolver` from `@agenteer/core/session`.
|
|
49
|
+
*/
|
|
50
|
+
resolve(opts: { prompt: string; question_id?: string }): unknown | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface AskUserFactoryOptions<T = string> {
|
|
54
|
+
resolver?: AskUserResolver;
|
|
55
|
+
/** Zod schema to validate any resolved/recorded answer. Defaults to string. */
|
|
56
|
+
answerSchema?: ZodType<T>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function askUserFactory<T = string>(
|
|
60
|
+
options: AskUserFactoryOptions<T> = {},
|
|
61
|
+
): () => Node<Input, { answer: T; prompt: string; source: "recorded" | "resolver" | "user" }> {
|
|
62
|
+
const answerSchema = (options.answerSchema ?? z.string()) as ZodType<T>;
|
|
63
|
+
const OutputSchema = z.object({
|
|
64
|
+
answer: answerSchema,
|
|
65
|
+
prompt: z.string(),
|
|
66
|
+
source: z.enum(["recorded", "resolver", "user"]),
|
|
67
|
+
});
|
|
68
|
+
type Output = { answer: T; prompt: string; source: "recorded" | "resolver" | "user" };
|
|
69
|
+
|
|
70
|
+
return () => ({
|
|
71
|
+
manifest: MANIFEST,
|
|
72
|
+
inputSchema: InputSchema,
|
|
73
|
+
outputSchema: OutputSchema,
|
|
74
|
+
ctx: [],
|
|
75
|
+
model: null,
|
|
76
|
+
async execute(input: NodeInput<Input>): Promise<NodeResult<Output>> {
|
|
77
|
+
const i = input.original;
|
|
78
|
+
const resumeHint = i.question_id ?? slugHint(i.prompt);
|
|
79
|
+
|
|
80
|
+
if (i.answer !== undefined) {
|
|
81
|
+
const parsed = answerSchema.safeParse(i.answer);
|
|
82
|
+
if (!parsed.success) {
|
|
83
|
+
return {
|
|
84
|
+
kind: "failed",
|
|
85
|
+
reason: `recorded_answer_schema_violation: ${parsed.error.message}`,
|
|
86
|
+
retryable: false,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
kind: "output",
|
|
91
|
+
value: { answer: parsed.data, prompt: i.prompt, source: "recorded" },
|
|
92
|
+
evidence: { verdict: "pass" },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (options.resolver) {
|
|
97
|
+
const raw = options.resolver.resolve({
|
|
98
|
+
prompt: i.prompt,
|
|
99
|
+
...(i.question_id !== undefined ? { question_id: i.question_id } : {}),
|
|
100
|
+
});
|
|
101
|
+
if (raw !== null && raw !== undefined) {
|
|
102
|
+
const parsed = answerSchema.safeParse(raw);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "failed",
|
|
106
|
+
reason: `resolver_answer_schema_violation: ${parsed.error.message}`,
|
|
107
|
+
retryable: false,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
kind: "output",
|
|
112
|
+
value: { answer: parsed.data, prompt: i.prompt, source: "resolver" },
|
|
113
|
+
evidence: { verdict: "pass" },
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
kind: "needs_user",
|
|
120
|
+
prompt: i.prompt,
|
|
121
|
+
schema: answerSchema,
|
|
122
|
+
resume_hint: resumeHint,
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const askUserManifest = MANIFEST;
|
|
129
|
+
|
|
130
|
+
function slugHint(prompt: string): string {
|
|
131
|
+
const slug = prompt
|
|
132
|
+
.toLowerCase()
|
|
133
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
134
|
+
.replace(/^_|_$/g, "")
|
|
135
|
+
.slice(0, 40);
|
|
136
|
+
return `ask_user:${slug || "prompt"}`;
|
|
137
|
+
}
|