toolcraft 0.0.121 → 0.0.123
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 +23 -16
- package/composition.json +2 -2
- package/dist/cli.compile-check.js +4 -1
- package/dist/cli.d.ts +3 -2
- package/dist/cli.js +10 -14
- package/dist/composition.json +2 -2
- package/dist/human-in-loop/approval-tasks.d.ts +5 -2
- package/dist/human-in-loop/approvals-commands.d.ts +2 -2
- package/dist/human-in-loop/approvals-commands.js +10 -6
- package/dist/human-in-loop/gate.d.ts +3 -3
- package/dist/human-in-loop/gate.js +1 -21
- package/dist/human-in-loop/index.d.ts +4 -1
- package/dist/human-in-loop/index.js +2 -0
- package/dist/human-in-loop/runner.d.ts +1 -1
- package/dist/human-in-loop/runner.js +1 -2
- package/dist/human-in-loop/runtime-options.d.ts +19 -0
- package/dist/human-in-loop/runtime-options.js +1 -0
- package/dist/human-in-loop/runtime.d.ts +2 -0
- package/dist/human-in-loop/runtime.js +14 -0
- package/dist/human-in-loop/spawn.d.ts +2 -2
- package/dist/human-in-loop/types.d.ts +9 -14
- package/dist/human-in-loop/wiring.d.ts +11 -0
- package/dist/human-in-loop/wiring.js +36 -0
- package/dist/index.d.ts +2 -2
- package/dist/mcp.compile-check.js +4 -1
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +16 -10
- package/dist/sdk.compile-check.js +4 -1
- package/dist/sdk.d.ts +2 -2
- package/dist/sdk.js +11 -8
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +1 -2
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +1 -4
- package/node_modules/@poe-code/task-list/dist/schema/ids.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/schema/ids.js +6 -0
- package/node_modules/tiny-http-mcp-server/dist/cli.js +5 -1
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -10
- package/dist/agent-human-in-loop.d.ts +0 -1
- package/dist/agent-human-in-loop.js +0 -1
- package/dist/task-list.d.ts +0 -1
- package/dist/task-list.js +0 -1
- package/node_modules/@poe-code/task-list/dist/schema/store.schema.json +0 -32
- package/node_modules/@poe-code/task-list/dist/schema/task.schema.json +0 -33
package/README.md
CHANGED
|
@@ -425,22 +425,24 @@ Modes:
|
|
|
425
425
|
- `sync` — handler waits for approval before running.
|
|
426
426
|
- `async` — toolcraft enqueues the command, returns a pending marker, and runs it in a fresh process when an operator approves via the reserved `approvals` group.
|
|
427
427
|
|
|
428
|
-
|
|
428
|
+
The runtime never ships with the core entrypoints — it loads only through the `toolcraft/human-in-loop` export, and providers are surfaced from the same export. Wire the same runtime into every entrypoint:
|
|
429
429
|
|
|
430
430
|
```ts
|
|
431
|
-
|
|
432
|
-
|
|
431
|
+
import { createHumanInLoop, osascriptProvider } from "toolcraft/human-in-loop";
|
|
432
|
+
|
|
433
|
+
const humanInLoop = createHumanInLoop({
|
|
434
|
+
provider: osascriptProvider({ title: "Approval needed" }), // required
|
|
433
435
|
taskList: { dir: ".toolcraft/approvals.yaml", format: "yaml-file" as const }
|
|
434
|
-
};
|
|
436
|
+
});
|
|
435
437
|
|
|
436
|
-
await runCLI(root, { humanInLoop });
|
|
437
|
-
createMCPServer(root, { name: "mytool", version: "0.1.0", humanInLoop });
|
|
438
|
-
const sdk = createSDK(root, { humanInLoop });
|
|
438
|
+
await runCLI(root, { humanInLoop, approvals: true });
|
|
439
|
+
createMCPServer(root, { name: "mytool", version: "0.1.0", humanInLoop, approvals: true });
|
|
440
|
+
const sdk = createSDK(root, { humanInLoop, approvals: true });
|
|
439
441
|
```
|
|
440
442
|
|
|
441
|
-
|
|
443
|
+
`provider` is required — there is no implicit platform default. `defaultProviderForPlatform()` (osascript on macOS, a throwing stub elsewhere) is exported for callers that want that behavior explicitly. A command that declares `humanInLoop` config while no runtime is wired fails at startup, as does `approvals: true` without a runtime.
|
|
442
444
|
|
|
443
|
-
|
|
445
|
+
With `approvals: true`, the built-in `approvals` group is merged into the root:
|
|
444
446
|
|
|
445
447
|
- `approvals list` — list pending tasks (CLI, MCP, SDK).
|
|
446
448
|
- `approvals show --approval-id <id>` — show one task.
|
|
@@ -448,7 +450,7 @@ A built-in `approvals` group is auto-merged into every root:
|
|
|
448
450
|
|
|
449
451
|
The name `approvals` is reserved. Defining your own `approvals` group fails at startup.
|
|
450
452
|
|
|
451
|
-
The async runner re-execs your binary (`process.execPath` + `process.argv[1]` by default; override via `
|
|
453
|
+
The async runner re-execs your binary (`process.execPath` + `process.argv[1]` by default; override via the `binPath` option of `createHumanInLoop`). Re-exec calls the same toolcraft entrypoint with the same wired runtime — do not branch on `argv` before calling `runCLI`/`runMCP`/`createSDK`.
|
|
452
454
|
|
|
453
455
|
Async results must be JSON-serializable; non-serializable returns mark the approval as failed instead of being persisted.
|
|
454
456
|
|
|
@@ -459,7 +461,7 @@ import type {
|
|
|
459
461
|
ApprovalRequest,
|
|
460
462
|
ApprovalResult,
|
|
461
463
|
HumanInLoopProvider
|
|
462
|
-
} from "
|
|
464
|
+
} from "toolcraft/human-in-loop";
|
|
463
465
|
|
|
464
466
|
export function slackApprovalProvider(opts: {
|
|
465
467
|
channel: string;
|
|
@@ -545,7 +547,7 @@ esbuild records dependency paths relative to the build layout. A standalone inst
|
|
|
545
547
|
|
|
546
548
|
## Configuration Options
|
|
547
549
|
|
|
548
|
-
Toolcraft configuration is code-first. Use `defineCommand(config)` and `defineGroup(config)` for the command tree, then pass runtime options to `runCLI`, `createSDK`, `createMCPServer`, or `runMCP`. MCP proxy schemas are cached under `.toolcraft/mcp`, and
|
|
550
|
+
Toolcraft configuration is code-first. Use `defineCommand(config)` and `defineGroup(config)` for the command tree, then pass runtime options to `runCLI`, `createSDK`, `createMCPServer`, or `runMCP`. MCP proxy schemas are cached under `.toolcraft/mcp`, and human-in-loop approvals are wired explicitly with `createHumanInLoop` from `toolcraft/human-in-loop`.
|
|
549
551
|
|
|
550
552
|
## API reference
|
|
551
553
|
|
|
@@ -589,7 +591,8 @@ additional long CLI flags. For example, `rawResponse` normally maps to `--raw-re
|
|
|
589
591
|
- `presets?: boolean` — enables `--preset <path>` for loading parameter defaults from JSON files.
|
|
590
592
|
- `controls?: { debug?, logLevel?, output?, verbose?, yes? }` — enables generated global CLI controls. Set `output: true` to expose `--output <rich|md|markdown|json>`.
|
|
591
593
|
- `apiVersion?: string` — for `requires.apiVersion`.
|
|
592
|
-
- `humanInLoop?:
|
|
594
|
+
- `humanInLoop?: HumanInLoopRuntime` — from `createHumanInLoop` (`toolcraft/human-in-loop`); required for commands with `humanInLoop` config.
|
|
595
|
+
- `approvals?: boolean` — merge the built-in `approvals` group; requires `humanInLoop`.
|
|
593
596
|
- `errorReports?: boolean | { dir?: string }`
|
|
594
597
|
- `projectRoot?: string` — root used for MCP proxy cache files (`.toolcraft/mcp/*.json`).
|
|
595
598
|
|
|
@@ -609,17 +612,21 @@ additional long CLI flags. For example, `rawResponse` normally maps to `--raw-re
|
|
|
609
612
|
- `omitRootToolNamePrefix?: boolean` — defaults to `false`. Set to `true` to omit the root group name from single-root MCP tool names (`bot__create`).
|
|
610
613
|
- `casing?: "snake" | "camel"` — affects MCP input-schema property names, output-schema property names, and structured result keys. Tool names always stay `__`-joined snake_case.
|
|
611
614
|
|
|
612
|
-
### `
|
|
615
|
+
### `createHumanInLoop(options)` (from `toolcraft/human-in-loop`)
|
|
613
616
|
|
|
614
617
|
```ts
|
|
618
|
+
import { createHumanInLoop, defaultProviderForPlatform, osascriptProvider } from "toolcraft/human-in-loop";
|
|
619
|
+
|
|
615
620
|
type HumanInLoopRuntimeOptions = {
|
|
616
|
-
provider
|
|
621
|
+
provider: HumanInLoopProvider; // required
|
|
617
622
|
taskList?: TaskList | { dir: string; format: "markdown-dir" | "yaml-file" };
|
|
618
623
|
listName?: string; // defaults to "approvals"
|
|
619
624
|
binPath?: { execPath: string; entryArgs: readonly string[] };
|
|
620
625
|
};
|
|
621
626
|
```
|
|
622
627
|
|
|
628
|
+
Returns the `HumanInLoopRuntime` accepted by `runCLI` / `createMCPServer` / `createSDK`.
|
|
629
|
+
|
|
623
630
|
### Handler context
|
|
624
631
|
|
|
625
632
|
- `params` — inferred from the command `params` schema.
|
|
@@ -635,7 +642,7 @@ type HumanInLoopRuntimeOptions = {
|
|
|
635
642
|
- `defineCommand`, `defineGroup`
|
|
636
643
|
- `S`, `toJsonSchema`, type helpers — re-exported from `toolcraft-schema`
|
|
637
644
|
- `UserError`, `ApprovalDeclinedError`, `HttpError` and the HTTP error subclasses.
|
|
638
|
-
- Type exports: `Command`, `Group`, `Scope`, `HandlerContext`, `HumanInLoopConfig`, `HumanInLoopPending`, `
|
|
645
|
+
- Type exports: `Command`, `Group`, `Scope`, `HandlerContext`, `HumanInLoopConfig`, `HumanInLoopPending`, `HumanInLoopRuntime`, schema types from `toolcraft-schema`.
|
|
639
646
|
|
|
640
647
|
Subpath imports:
|
|
641
648
|
|
package/composition.json
CHANGED
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"name": "toolcraft",
|
|
106
|
-
"version": "0.0.
|
|
106
|
+
"version": "0.0.123",
|
|
107
107
|
"license": "MIT"
|
|
108
108
|
},
|
|
109
109
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft-schema",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.123",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -27,7 +27,10 @@ const ignoredOptions = {
|
|
|
27
27
|
logger: (event) => {
|
|
28
28
|
event.level;
|
|
29
29
|
},
|
|
30
|
-
humanInLoop: {
|
|
30
|
+
humanInLoop: {
|
|
31
|
+
invoke: async (node, ctx) => node.handler(ctx),
|
|
32
|
+
mergeApprovalsGroup: (root) => root,
|
|
33
|
+
},
|
|
31
34
|
version: "1.0.0",
|
|
32
35
|
};
|
|
33
36
|
const ignoredServiceOptions = {
|
package/dist/cli.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import "./node-require-shim.js";
|
|
|
2
2
|
import { configureTheme } from "toolcraft-design";
|
|
3
3
|
import type { Command, Group, HandlerFs, LogLevel, RenderPrimitives, RuntimeLoggerInput } from "./index.js";
|
|
4
4
|
import { type ErrorReportsOption } from "./error-report.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { HumanInLoopRuntime } from "./human-in-loop/types.js";
|
|
6
6
|
export { renderErrorReport } from "./error-report.js";
|
|
7
7
|
export type { ErrorReportRenderContext, ErrorReportRenderResult } from "./error-report.js";
|
|
8
8
|
export { configureTheme };
|
|
@@ -34,7 +34,7 @@ export interface RunCLIOptions<TServices extends object = Record<string, unknown
|
|
|
34
34
|
env?: Record<string, string>;
|
|
35
35
|
fetch?: typeof globalThis.fetch;
|
|
36
36
|
fs?: HandlerFs;
|
|
37
|
-
humanInLoop?:
|
|
37
|
+
humanInLoop?: HumanInLoopRuntime;
|
|
38
38
|
logLevel?: LogLevel;
|
|
39
39
|
logger?: RuntimeLoggerInput;
|
|
40
40
|
outputEmitter?: (entry: string) => void;
|
|
@@ -92,6 +92,7 @@ export interface CLICommandTreeSnapshotOptions {
|
|
|
92
92
|
argv?: readonly string[];
|
|
93
93
|
casing?: Casing;
|
|
94
94
|
controls?: CLIControls;
|
|
95
|
+
humanInLoop?: HumanInLoopRuntime;
|
|
95
96
|
presets?: boolean;
|
|
96
97
|
version?: string;
|
|
97
98
|
}
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { cancel, configureTheme, confirm, createLogger, formatCommandList, forma
|
|
|
7
7
|
import { ApprovalDeclinedError, ToolcraftBugError, UserError, assertCommandRequirements, getCommandSourcePath, hasMcpProxyConfig, resolveCommandSecrets } from "./index.js";
|
|
8
8
|
import { hasOwnErrorCode } from "./error-codes.js";
|
|
9
9
|
import { writeErrorReport } from "./error-report.js";
|
|
10
|
+
import { assertHumanInLoopWired, mergeApprovalsRoot } from "./human-in-loop/wiring.js";
|
|
10
11
|
import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
|
|
11
12
|
import { findEntrypointPackageMetadata } from "./package-metadata.js";
|
|
12
13
|
import { redactHttpBody, redactHttpHeaderValue } from "./redaction.js";
|
|
@@ -24,8 +25,6 @@ configureTheme({ brand: "blue", label: "Toolcraft" });
|
|
|
24
25
|
export { configureTheme };
|
|
25
26
|
const NULL_OPTION_VALUE = Symbol("toolcraft.cli.null");
|
|
26
27
|
const optionalModulePaths = {
|
|
27
|
-
approvals: "./human-in-loop/approvals-commands.js",
|
|
28
|
-
humanInLoop: "./human-in-loop/gate.js",
|
|
29
28
|
mcpProxy: "./mcp-proxy.js"
|
|
30
29
|
};
|
|
31
30
|
function importOptionalModule(specifier) {
|
|
@@ -42,9 +41,7 @@ function importOptionalModule(specifier) {
|
|
|
42
41
|
export async function createCLICommandTreeSnapshot(roots, options = {}) {
|
|
43
42
|
const argv = [...(options.argv ?? ["node", "toolcraft"])];
|
|
44
43
|
const normalizedRoot = normalizeRoots(roots, argv);
|
|
45
|
-
const root = options
|
|
46
|
-
? (await importOptionalModule(optionalModulePaths.approvals)).mergeApprovalsGroup(normalizedRoot)
|
|
47
|
-
: normalizedRoot;
|
|
44
|
+
const root = mergeApprovalsRoot(normalizedRoot, options);
|
|
48
45
|
const controls = resolveCLIControls(options.controls);
|
|
49
46
|
const presetsEnabled = options.presets === true;
|
|
50
47
|
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled, options.version !== undefined, controls);
|
|
@@ -3028,7 +3025,7 @@ function getResolvedFlags(command) {
|
|
|
3028
3025
|
const flags = command.optsWithGlobals();
|
|
3029
3026
|
return flags;
|
|
3030
3027
|
}
|
|
3031
|
-
async function executeCommand(state, services, requirementOptions, runtimeFetch,
|
|
3028
|
+
async function executeCommand(state, services, requirementOptions, runtimeFetch, humanInLoop, runtimeEnv, runtimeFs, outputEmitter, outputFormats, promptStreams, diagnosticsOptions, onErrorReportContext) {
|
|
3032
3029
|
const logger = createLogger(outputEmitter);
|
|
3033
3030
|
const optionValues = state.actionCommand.optsWithGlobals();
|
|
3034
3031
|
const resolvedFlags = optionValues;
|
|
@@ -3162,8 +3159,8 @@ async function executeCommand(state, services, requirementOptions, runtimeFetch,
|
|
|
3162
3159
|
throw new UserError("Operation cancelled.");
|
|
3163
3160
|
}
|
|
3164
3161
|
}
|
|
3165
|
-
const result = state.command.humanInLoop
|
|
3166
|
-
? await
|
|
3162
|
+
const result = state.command.humanInLoop && humanInLoop !== undefined
|
|
3163
|
+
? await humanInLoop.invoke(state.command, context, state.commandPath)
|
|
3167
3164
|
: await state.command.handler(context);
|
|
3168
3165
|
if (output === "rich" && runtime.isFixture) {
|
|
3169
3166
|
writeRichHeader(`${state.command.name} (fixture)`);
|
|
@@ -3729,20 +3726,19 @@ export async function runCLI(roots, options = {}) {
|
|
|
3729
3726
|
let errorReportContext;
|
|
3730
3727
|
try {
|
|
3731
3728
|
const normalizedRoot = normalizeRoots(roots, argv);
|
|
3732
|
-
const root = options
|
|
3733
|
-
|
|
3734
|
-
: normalizedRoot;
|
|
3729
|
+
const root = mergeApprovalsRoot(normalizedRoot, options);
|
|
3730
|
+
assertHumanInLoopWired(root, options.humanInLoop);
|
|
3735
3731
|
if (hasMcpProxyConfig(root)) {
|
|
3736
3732
|
await (await importOptionalModule(optionalModulePaths.mcpProxy)).resolveMcpProxies(root, { projectRoot: options.projectRoot });
|
|
3737
3733
|
}
|
|
3738
3734
|
const casing = options.casing ?? "kebab";
|
|
3739
3735
|
const services = (options.services ?? {});
|
|
3740
|
-
const
|
|
3736
|
+
const humanInLoop = options.humanInLoop;
|
|
3741
3737
|
const runtimeFetch = options.fetch ?? globalThis.fetch;
|
|
3742
3738
|
version = options.version ?? findEntrypointPackageMetadata(argv[1])?.version;
|
|
3743
3739
|
const servicesWithBuiltIns = {
|
|
3744
3740
|
...services,
|
|
3745
|
-
|
|
3741
|
+
humanInLoop,
|
|
3746
3742
|
root
|
|
3747
3743
|
};
|
|
3748
3744
|
const requirementOptions = {
|
|
@@ -3777,7 +3773,7 @@ export async function runCLI(roots, options = {}) {
|
|
|
3777
3773
|
const execute = async (state) => {
|
|
3778
3774
|
lastActionCommand = state.actionCommand;
|
|
3779
3775
|
resolvedCommandPath = formatCliCommandPath(state.commandPath);
|
|
3780
|
-
await executeCommand(state, servicesWithBuiltIns, requirementOptions, runtimeFetch,
|
|
3776
|
+
await executeCommand(state, servicesWithBuiltIns, requirementOptions, runtimeFetch, humanInLoop, options.env, options.fs, options.outputEmitter, controls.outputFormats, {
|
|
3781
3777
|
input: options.promptInput,
|
|
3782
3778
|
output: options.promptOutput
|
|
3783
3779
|
}, {
|
package/dist/composition.json
CHANGED
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"name": "toolcraft",
|
|
106
|
-
"version": "0.0.
|
|
106
|
+
"version": "0.0.123",
|
|
107
107
|
"license": "MIT"
|
|
108
108
|
},
|
|
109
109
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft-schema",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.123",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { OpenTaskListOptions, TaskList, Tasks } from "@poe-code/task-list";
|
|
2
|
-
import type { HumanInLoopPending
|
|
2
|
+
import type { HumanInLoopPending } from "./types.js";
|
|
3
|
+
import type { HumanInLoopRuntimeOptions } from "./runtime-options.js";
|
|
4
|
+
type ApprovalListOptions = Pick<HumanInLoopRuntimeOptions, "taskList" | "listName">;
|
|
3
5
|
import { type ApprovalPlanValue } from "./plan-hash.js";
|
|
4
6
|
export interface ApprovalPayload {
|
|
5
7
|
approvalId?: string;
|
|
@@ -14,7 +16,7 @@ export interface ApprovalPayload {
|
|
|
14
16
|
result?: unknown;
|
|
15
17
|
error?: unknown;
|
|
16
18
|
}
|
|
17
|
-
export declare function ensureApprovalList(runtimeOptions:
|
|
19
|
+
export declare function ensureApprovalList(runtimeOptions: ApprovalListOptions | undefined, deps?: {
|
|
18
20
|
create?: boolean;
|
|
19
21
|
openTaskList?: (options: OpenTaskListOptions) => Promise<TaskList>;
|
|
20
22
|
}): Promise<{
|
|
@@ -33,3 +35,4 @@ export declare function loadApproval(ctx: {
|
|
|
33
35
|
tasks: Tasks;
|
|
34
36
|
approvalId: string;
|
|
35
37
|
}): Promise<ApprovalPayload | undefined>;
|
|
38
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CommandNode, Group } from "../index.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { HumanInLoopRuntimeInstance } from "./runtime-options.js";
|
|
3
3
|
interface ApprovalBuiltInServices {
|
|
4
|
-
|
|
4
|
+
humanInLoop: HumanInLoopRuntimeInstance;
|
|
5
5
|
root: CommandNode<any>;
|
|
6
6
|
}
|
|
7
7
|
export declare const approvalsGroup: Group<ApprovalBuiltInServices> & {
|
|
@@ -25,9 +25,11 @@ export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
|
25
25
|
description: "List queued approvals.",
|
|
26
26
|
scope: listScope,
|
|
27
27
|
params: listParams,
|
|
28
|
-
handler: async ({ params,
|
|
28
|
+
handler: async ({ params, humanInLoop }) => {
|
|
29
29
|
try {
|
|
30
|
-
const { tasks } = await ensureApprovalList(runtimeOptions, {
|
|
30
|
+
const { tasks } = await ensureApprovalList(humanInLoop.runtimeOptions, {
|
|
31
|
+
create: false
|
|
32
|
+
});
|
|
31
33
|
return loadApprovals(tasks, params.state);
|
|
32
34
|
}
|
|
33
35
|
catch (error) {
|
|
@@ -48,9 +50,11 @@ export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
|
48
50
|
description: "Show one approval.",
|
|
49
51
|
scope: listScope,
|
|
50
52
|
params: showParams,
|
|
51
|
-
handler: async ({ params,
|
|
53
|
+
handler: async ({ params, humanInLoop }) => {
|
|
52
54
|
try {
|
|
53
|
-
const { tasks } = await ensureApprovalList(runtimeOptions, {
|
|
55
|
+
const { tasks } = await ensureApprovalList(humanInLoop.runtimeOptions, {
|
|
56
|
+
create: false
|
|
57
|
+
});
|
|
54
58
|
return tasks.get(params.approvalId);
|
|
55
59
|
}
|
|
56
60
|
catch (error) {
|
|
@@ -71,8 +75,8 @@ export const approvalsGroup = markApprovalsBuiltIn(defineGroup({
|
|
|
71
75
|
description: "Run one queued approval.",
|
|
72
76
|
scope: runScope,
|
|
73
77
|
params: runParams,
|
|
74
|
-
handler: async ({ params,
|
|
75
|
-
return runApproval(params.approvalId, runtimeOptions, root);
|
|
78
|
+
handler: async ({ params, humanInLoop, root }) => {
|
|
79
|
+
return runApproval(params.approvalId, humanInLoop.runtimeOptions, root);
|
|
76
80
|
},
|
|
77
81
|
render: {
|
|
78
82
|
rich: (result, primitives) => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Command, HandlerContext } from "../index.js";
|
|
2
2
|
import { enqueueApproval } from "./approval-tasks.js";
|
|
3
|
-
import type { HumanInLoopPending
|
|
4
|
-
|
|
5
|
-
export declare function invokeWithHumanInLoop<T>(node: Command<any, any, any, T>, ctx: HandlerContext<any, any, any>, runtimeOptions: HumanInLoopRuntimeOptions
|
|
3
|
+
import type { HumanInLoopPending } from "./types.js";
|
|
4
|
+
import type { HumanInLoopRuntimeOptions } from "./runtime-options.js";
|
|
5
|
+
export declare function invokeWithHumanInLoop<T>(node: Command<any, any, any, T>, ctx: HandlerContext<any, any, any>, runtimeOptions: HumanInLoopRuntimeOptions, commandPath: string, options?: {
|
|
6
6
|
enqueueApproval?: typeof enqueueApproval;
|
|
7
7
|
spawnRunner?: boolean;
|
|
8
8
|
}): Promise<T | HumanInLoopPending>;
|
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import { enqueueApproval, ensureApprovalList } from "./approval-tasks.js";
|
|
2
|
-
import { defaultProviderForPlatform } from "./default-provider.js";
|
|
3
2
|
import { spawnApprovalRunner } from "./spawn.js";
|
|
4
3
|
import { ApprovalDeclinedError } from "./types.js";
|
|
5
4
|
import { assertApprovalPlanHash, createApprovalPlan, formatApprovalMessage } from "./plan-hash.js";
|
|
6
|
-
const providersByRuntime = new WeakMap();
|
|
7
|
-
let providerWithoutRuntime;
|
|
8
|
-
export function resolveProvider(runtimeOptions) {
|
|
9
|
-
if (runtimeOptions?.provider !== undefined) {
|
|
10
|
-
return runtimeOptions.provider;
|
|
11
|
-
}
|
|
12
|
-
if (runtimeOptions === undefined) {
|
|
13
|
-
providerWithoutRuntime ??= defaultProviderForPlatform();
|
|
14
|
-
return providerWithoutRuntime;
|
|
15
|
-
}
|
|
16
|
-
const cachedProvider = providersByRuntime.get(runtimeOptions);
|
|
17
|
-
if (cachedProvider !== undefined) {
|
|
18
|
-
return cachedProvider;
|
|
19
|
-
}
|
|
20
|
-
const provider = defaultProviderForPlatform();
|
|
21
|
-
providersByRuntime.set(runtimeOptions, provider);
|
|
22
|
-
return provider;
|
|
23
|
-
}
|
|
24
5
|
export async function invokeWithHumanInLoop(node, ctx, runtimeOptions, commandPath, options = {}) {
|
|
25
6
|
if (!node.humanInLoop) {
|
|
26
7
|
return node.handler(ctx);
|
|
@@ -54,8 +35,7 @@ export async function invokeWithHumanInLoop(node, ctx, runtimeOptions, commandPa
|
|
|
54
35
|
}
|
|
55
36
|
return pending;
|
|
56
37
|
}
|
|
57
|
-
const
|
|
58
|
-
const result = await provider.requestApproval({
|
|
38
|
+
const result = await runtimeOptions.provider.requestApproval({
|
|
59
39
|
message,
|
|
60
40
|
declineInputPrompt: node.humanInLoop.declineInputPrompt
|
|
61
41
|
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
export { createHumanInLoop } from "./runtime.js";
|
|
1
2
|
export { defaultProviderForPlatform } from "./default-provider.js";
|
|
3
|
+
export { osascriptProvider } from "@poe-code/agent-human-in-loop";
|
|
2
4
|
export { invokeWithHumanInLoop } from "./gate.js";
|
|
3
5
|
export { approvalStateMachine } from "./state-machine.js";
|
|
4
6
|
export { ApprovalDeclinedError } from "./types.js";
|
|
5
7
|
export type { ApprovalEvent, ApprovalState } from "./state-machine.js";
|
|
6
|
-
export type { HumanInLoopConfig, HumanInLoopPending,
|
|
8
|
+
export type { HumanInLoopConfig, HumanInLoopPending, HumanInLoopRuntime } from "./types.js";
|
|
9
|
+
export type { HumanInLoopRuntimeInstance, HumanInLoopRuntimeOptions } from "./runtime-options.js";
|
|
7
10
|
export type { HumanInLoopProvider } from "@poe-code/agent-human-in-loop";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export { createHumanInLoop } from "./runtime.js";
|
|
1
2
|
export { defaultProviderForPlatform } from "./default-provider.js";
|
|
3
|
+
export { osascriptProvider } from "@poe-code/agent-human-in-loop";
|
|
2
4
|
export { invokeWithHumanInLoop } from "./gate.js";
|
|
3
5
|
export { approvalStateMachine } from "./state-machine.js";
|
|
4
6
|
export { ApprovalDeclinedError } from "./types.js";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CommandNode } from "../index.js";
|
|
2
|
-
import type { HumanInLoopRuntimeOptions } from "./
|
|
2
|
+
import type { HumanInLoopRuntimeOptions } from "./runtime-options.js";
|
|
3
3
|
export declare function runApproval(approvalId: string, runtimeOptions: HumanInLoopRuntimeOptions, root: CommandNode<any>): Promise<void>;
|
|
@@ -2,7 +2,6 @@ import { InvalidTransitionError } from "@poe-code/task-list";
|
|
|
2
2
|
import { UserError, resolveCommandSecrets } from "../index.js";
|
|
3
3
|
import { createEnv, createFs } from "../runtime/io.js";
|
|
4
4
|
import { ensureApprovalList } from "./approval-tasks.js";
|
|
5
|
-
import { resolveProvider } from "./gate.js";
|
|
6
5
|
import { createRuntimeLogger } from "../runtime-logging.js";
|
|
7
6
|
import { assertApprovalPlanHash, createApprovalPlan, formatApprovalMessage, isApprovalPlanValue } from "./plan-hash.js";
|
|
8
7
|
const MAX_AVAILABLE_COMMAND_PATHS = 20;
|
|
@@ -13,7 +12,7 @@ export async function runApproval(approvalId, runtimeOptions, root) {
|
|
|
13
12
|
return;
|
|
14
13
|
}
|
|
15
14
|
const approval = readApprovalPayload(task);
|
|
16
|
-
const provider =
|
|
15
|
+
const provider = runtimeOptions.provider;
|
|
17
16
|
try {
|
|
18
17
|
await tasks.fire(approvalId, "claim", {
|
|
19
18
|
metadataPatch: {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HumanInLoopProvider } from "@poe-code/agent-human-in-loop";
|
|
2
|
+
import type { TaskList } from "@poe-code/task-list";
|
|
3
|
+
import type { HumanInLoopRuntime } from "./types.js";
|
|
4
|
+
/** The concrete runtime built by `createHumanInLoop` — carries its options for the approvals built-ins. */
|
|
5
|
+
export interface HumanInLoopRuntimeInstance extends HumanInLoopRuntime {
|
|
6
|
+
readonly runtimeOptions: HumanInLoopRuntimeOptions;
|
|
7
|
+
}
|
|
8
|
+
export interface HumanInLoopRuntimeOptions {
|
|
9
|
+
provider: HumanInLoopProvider;
|
|
10
|
+
taskList?: TaskList | {
|
|
11
|
+
dir: string;
|
|
12
|
+
format: "markdown-dir" | "yaml-file";
|
|
13
|
+
};
|
|
14
|
+
listName?: string;
|
|
15
|
+
binPath?: {
|
|
16
|
+
execPath: string;
|
|
17
|
+
entryArgs: readonly string[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { UserError } from "../user-error.js";
|
|
2
|
+
import { mergeApprovalsGroup } from "./approvals-commands.js";
|
|
3
|
+
import { invokeWithHumanInLoop } from "./gate.js";
|
|
4
|
+
export function createHumanInLoop(options) {
|
|
5
|
+
if (options?.provider === undefined) {
|
|
6
|
+
throw new UserError('createHumanInLoop requires a provider — import one from "toolcraft/human-in-loop" (e.g. osascriptProvider) or pass your own');
|
|
7
|
+
}
|
|
8
|
+
const runtimeOptions = { ...options };
|
|
9
|
+
return {
|
|
10
|
+
runtimeOptions,
|
|
11
|
+
invoke: (node, ctx, commandPath) => invokeWithHumanInLoop(node, ctx, runtimeOptions, commandPath),
|
|
12
|
+
mergeApprovalsGroup
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { HumanInLoopRuntimeOptions } from "./
|
|
2
|
-
export declare function spawnApprovalRunner(approvalId: string, runtimeOptions: HumanInLoopRuntimeOptions, spawnFn?: typeof import("node:child_process").spawn): void;
|
|
1
|
+
import type { HumanInLoopRuntimeOptions } from "./runtime-options.js";
|
|
2
|
+
export declare function spawnApprovalRunner(approvalId: string, runtimeOptions: Pick<HumanInLoopRuntimeOptions, "binPath">, spawnFn?: typeof import("node:child_process").spawn): void;
|
|
3
3
|
export default spawnApprovalRunner;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { HumanInLoopProvider } from "@poe-code/agent-human-in-loop";
|
|
2
|
-
import type { TaskList } from "@poe-code/task-list";
|
|
3
1
|
import type { ObjectSchema, Static } from "toolcraft-schema";
|
|
2
|
+
import type { Command, Group, HandlerContext } from "../index.js";
|
|
4
3
|
import { UserError } from "../user-error.js";
|
|
5
4
|
export interface HumanInLoopConfig<TParamsSchema extends ObjectSchema<any>> {
|
|
6
5
|
mode: "sync" | "async";
|
|
@@ -14,17 +13,14 @@ export interface HumanInLoopConfig<TParamsSchema extends ObjectSchema<any>> {
|
|
|
14
13
|
}) => unknown | Promise<unknown>;
|
|
15
14
|
declineInputPrompt?: string;
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
execPath: string;
|
|
26
|
-
entryArgs: readonly string[];
|
|
27
|
-
};
|
|
16
|
+
/**
|
|
17
|
+
* The wired human-in-loop runtime. Core entrypoints only know this interface;
|
|
18
|
+
* the implementation ships behind the `toolcraft/human-in-loop` export and is
|
|
19
|
+
* created with `createHumanInLoop({ provider, ... })`.
|
|
20
|
+
*/
|
|
21
|
+
export interface HumanInLoopRuntime {
|
|
22
|
+
invoke<T>(node: Command<any, any, any, T>, ctx: HandlerContext<any, any, any>, commandPath: string): Promise<T | HumanInLoopPending>;
|
|
23
|
+
mergeApprovalsGroup<TServices extends object>(root: Group<TServices>): Group<TServices>;
|
|
28
24
|
}
|
|
29
25
|
export interface HumanInLoopPending {
|
|
30
26
|
status: "pending-approval";
|
|
@@ -43,4 +39,3 @@ export declare class ApprovalDeclinedError extends UserError {
|
|
|
43
39
|
approvalId?: string;
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
|
-
export type { HumanInLoopProvider } from "@poe-code/agent-human-in-loop";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CommandNode, Group } from "../index.js";
|
|
2
|
+
import type { HumanInLoopRuntime } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Human-in-loop config on a command is only allowed when a runtime is wired.
|
|
5
|
+
* Called by every entrypoint (CLI, MCP, SDK) after the root is normalized.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertHumanInLoopWired(root: CommandNode<any>, humanInLoop: HumanInLoopRuntime | undefined): void;
|
|
8
|
+
export declare function mergeApprovalsRoot<TServices extends object>(root: Group<TServices>, options: {
|
|
9
|
+
approvals?: boolean;
|
|
10
|
+
humanInLoop?: HumanInLoopRuntime;
|
|
11
|
+
}): Group<TServices>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { UserError } from "../user-error.js";
|
|
2
|
+
const WIRING_HINT = 'pass { humanInLoop: createHumanInLoop({ provider, ... }) } from "toolcraft/human-in-loop"';
|
|
3
|
+
/**
|
|
4
|
+
* Human-in-loop config on a command is only allowed when a runtime is wired.
|
|
5
|
+
* Called by every entrypoint (CLI, MCP, SDK) after the root is normalized.
|
|
6
|
+
*/
|
|
7
|
+
export function assertHumanInLoopWired(root, humanInLoop) {
|
|
8
|
+
if (humanInLoop !== undefined) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const commandPath = findHumanInLoopCommandPath(root, []);
|
|
12
|
+
if (commandPath !== undefined) {
|
|
13
|
+
throw new UserError(`command '${commandPath}' declares humanInLoop but no runtime is wired — ${WIRING_HINT}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function mergeApprovalsRoot(root, options) {
|
|
17
|
+
if (options.approvals !== true) {
|
|
18
|
+
return root;
|
|
19
|
+
}
|
|
20
|
+
if (options.humanInLoop === undefined) {
|
|
21
|
+
throw new UserError(`approvals: true requires a wired humanInLoop runtime — ${WIRING_HINT}`);
|
|
22
|
+
}
|
|
23
|
+
return options.humanInLoop.mergeApprovalsGroup(root);
|
|
24
|
+
}
|
|
25
|
+
function findHumanInLoopCommandPath(node, path) {
|
|
26
|
+
if (node.kind === "command") {
|
|
27
|
+
return node.humanInLoop ? path.join(".") || node.name : undefined;
|
|
28
|
+
}
|
|
29
|
+
for (const child of node.children) {
|
|
30
|
+
const found = findHumanInLoopCommandPath(child, [...path, child.name]);
|
|
31
|
+
if (found !== undefined) {
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { McpServerConfig } from "@poe-code/agent-mcp-config";
|
|
|
2
2
|
import type { AnySchema, ObjectSchema, Static } from "toolcraft-schema";
|
|
3
3
|
import type { LoggerOutput, RenderTableOptions, ThemePalette } from "toolcraft-design";
|
|
4
4
|
import { ApprovalDeclinedError } from "./human-in-loop/types.js";
|
|
5
|
-
import type { HumanInLoopConfig, HumanInLoopPending,
|
|
5
|
+
import type { HumanInLoopConfig, HumanInLoopPending, HumanInLoopRuntime } from "./human-in-loop/types.js";
|
|
6
6
|
import { ToolcraftBugError, UserError } from "./user-error.js";
|
|
7
7
|
import type { RuntimeLogger } from "./runtime-logging.js";
|
|
8
8
|
import type { StreamStatusEvent, ToolcraftStream } from "./stream.js";
|
|
@@ -235,4 +235,4 @@ export type { FileChangeRendererOptions, FileChangeResult } from "./file-change-
|
|
|
235
235
|
export type { FileChange, FileChangeDisplayMode, FileChangeKind } from "toolcraft-design";
|
|
236
236
|
export type { DiagnosticLogEvent, LogLevel, RuntimeLogger, RuntimeLoggerInput } from "./runtime-logging.js";
|
|
237
237
|
export type { AnySchema, ArraySchema, BooleanSchema, CliMissingParameterChoice, CliMissingParameterContext, CliMissingParameterResolution, CliOutputMode, CliSchemaOptions, EnumSchema, JsonSchema, JsonSchemaDocument, JsonSchemaDocumentOptions, JsonValue, JsonValueSchema, NumberSchema, ObjectSchema, OneOfSchema, OptionalSchema, RecordSchema, SchemaBase, Static, StringSchema, UnionSchema, ValidationIssue, ValidationResult } from "./schema.js";
|
|
238
|
-
export type { HumanInLoopConfig, HumanInLoopPending,
|
|
238
|
+
export type { HumanInLoopConfig, HumanInLoopPending, HumanInLoopRuntime };
|
|
@@ -21,7 +21,10 @@ const ignoredOptions = {
|
|
|
21
21
|
fetch: globalThis.fetch,
|
|
22
22
|
tools: ["usage"],
|
|
23
23
|
casing: "snake",
|
|
24
|
-
humanInLoop: {
|
|
24
|
+
humanInLoop: {
|
|
25
|
+
invoke: async (node, ctx) => node.handler(ctx),
|
|
26
|
+
mergeApprovalsGroup: (root) => root,
|
|
27
|
+
},
|
|
25
28
|
};
|
|
26
29
|
const ignoredServer = createMCPServer(ignoredRoot, ignoredOptions);
|
|
27
30
|
const ignoredServerArray = createMCPServer([ignoredRoot], ignoredOptions);
|
package/dist/mcp.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import "./node-require-shim.js";
|
|
|
2
2
|
import { type SDKTransport, type Server as TinyServer } from "tiny-stdio-mcp-server";
|
|
3
3
|
import type { Group, HandlerFs, LogLevel, RuntimeLoggerInput } from "./index.js";
|
|
4
4
|
import { type ErrorReportsOption } from "./error-report.js";
|
|
5
|
-
import { type
|
|
5
|
+
import { type HumanInLoopRuntime } from "./human-in-loop/types.js";
|
|
6
6
|
type Casing = "snake" | "camel";
|
|
7
7
|
type CmdkitServer = Omit<TinyServer, "connect"> & {
|
|
8
8
|
connect(transport: SDKTransport): Promise<void>;
|
|
@@ -21,7 +21,7 @@ export interface RunMCPOptions<TServices extends object = Record<string, unknown
|
|
|
21
21
|
fs?: HandlerFs;
|
|
22
22
|
name: string;
|
|
23
23
|
version?: string;
|
|
24
|
-
humanInLoop?:
|
|
24
|
+
humanInLoop?: HumanInLoopRuntime;
|
|
25
25
|
projectRoot?: string;
|
|
26
26
|
logLevel?: LogLevel;
|
|
27
27
|
logger?: RuntimeLoggerInput;
|
package/dist/mcp.js
CHANGED
|
@@ -4,8 +4,8 @@ import { toJsonSchema } from "toolcraft-schema";
|
|
|
4
4
|
import { createHttpErrorEnvelope, isHttpErrorLike } from "./api-error-summary.js";
|
|
5
5
|
import { ToolcraftBugError, UserError, assertCommandRequirements, resolveCommandSecrets } from "./index.js";
|
|
6
6
|
import { writeErrorReport } from "./error-report.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { ApprovalDeclinedError } from "./human-in-loop/types.js";
|
|
8
|
+
import { assertHumanInLoopWired, mergeApprovalsRoot } from "./human-in-loop/wiring.js";
|
|
9
9
|
import { hasMcpProxyGroups, resolveMcpProxies } from "./mcp-proxy.js";
|
|
10
10
|
import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
|
|
11
11
|
import { findEntrypointPackageMetadata } from "./package-metadata.js";
|
|
@@ -717,7 +717,7 @@ function toToolError(error, reportPath) {
|
|
|
717
717
|
function createResolvedMCPServer(root, options, runtime = {}) {
|
|
718
718
|
const casing = options.casing ?? "snake";
|
|
719
719
|
const services = (options.services ?? {});
|
|
720
|
-
const
|
|
720
|
+
const humanInLoop = options.humanInLoop;
|
|
721
721
|
const runtimeFetch = options.fetch ?? globalThis.fetch;
|
|
722
722
|
const diagnostics = createRuntimeLogger({
|
|
723
723
|
level: options.logLevel,
|
|
@@ -764,7 +764,7 @@ function createResolvedMCPServer(root, options, runtime = {}) {
|
|
|
764
764
|
const baseContext = {
|
|
765
765
|
...services,
|
|
766
766
|
...requestServices,
|
|
767
|
-
|
|
767
|
+
humanInLoop,
|
|
768
768
|
root,
|
|
769
769
|
secrets,
|
|
770
770
|
fetch: runtimeFetch,
|
|
@@ -863,7 +863,7 @@ function createResolvedMCPServer(root, options, runtime = {}) {
|
|
|
863
863
|
const baseContext = {
|
|
864
864
|
...services,
|
|
865
865
|
...requestServices,
|
|
866
|
-
|
|
866
|
+
humanInLoop,
|
|
867
867
|
root,
|
|
868
868
|
secrets,
|
|
869
869
|
fetch: runtimeFetch,
|
|
@@ -879,10 +879,13 @@ function createResolvedMCPServer(root, options, runtime = {}) {
|
|
|
879
879
|
env: options.env
|
|
880
880
|
});
|
|
881
881
|
params = validateToolArguments(tool.command.params, argumentsValue, casing);
|
|
882
|
-
const
|
|
882
|
+
const handlerContext = {
|
|
883
883
|
...baseContext,
|
|
884
884
|
params
|
|
885
|
-
}
|
|
885
|
+
};
|
|
886
|
+
const result = humanInLoop === undefined
|
|
887
|
+
? await tool.command.handler(handlerContext)
|
|
888
|
+
: await humanInLoop.invoke(tool.command, handlerContext, tool.commandPath);
|
|
886
889
|
if (isHumanInLoopPending(result)) {
|
|
887
890
|
return renderPendingApproval(result);
|
|
888
891
|
}
|
|
@@ -960,7 +963,8 @@ function createDeferredMCPServer(root, options) {
|
|
|
960
963
|
}
|
|
961
964
|
export function createMCPServer(roots, options) {
|
|
962
965
|
const normalizedRoot = normalizeRoots(roots);
|
|
963
|
-
const root =
|
|
966
|
+
const root = mergeApprovalsRoot(normalizedRoot, options);
|
|
967
|
+
assertHumanInLoopWired(root, options.humanInLoop);
|
|
964
968
|
if (!hasMcpProxyGroups(root)) {
|
|
965
969
|
return createResolvedMCPServer(root, options);
|
|
966
970
|
}
|
|
@@ -969,14 +973,16 @@ export function createMCPServer(roots, options) {
|
|
|
969
973
|
/** @internal */
|
|
970
974
|
export async function createMCPServerForTransport(roots, options, runtime) {
|
|
971
975
|
const normalizedRoot = normalizeRoots(roots);
|
|
972
|
-
const root =
|
|
976
|
+
const root = mergeApprovalsRoot(normalizedRoot, options);
|
|
977
|
+
assertHumanInLoopWired(root, options.humanInLoop);
|
|
973
978
|
await resolveMcpProxies(root, { projectRoot: options.projectRoot });
|
|
974
979
|
createResolvedMCPServer(root, options, runtime);
|
|
975
980
|
}
|
|
976
981
|
export async function runMCP(roots, options) {
|
|
977
982
|
enableSourceMaps();
|
|
978
983
|
const normalizedRoot = normalizeRoots(roots);
|
|
979
|
-
const root =
|
|
984
|
+
const root = mergeApprovalsRoot(normalizedRoot, options);
|
|
985
|
+
assertHumanInLoopWired(root, options.humanInLoop);
|
|
980
986
|
await resolveMcpProxies(root, { projectRoot: options.projectRoot });
|
|
981
987
|
const server = createResolvedMCPServer(root, options);
|
|
982
988
|
await server.listen();
|
|
@@ -95,7 +95,10 @@ const ignoredOptions = {
|
|
|
95
95
|
services: {
|
|
96
96
|
logger: console,
|
|
97
97
|
},
|
|
98
|
-
humanInLoop: {
|
|
98
|
+
humanInLoop: {
|
|
99
|
+
invoke: async (node, ctx) => node.handler(ctx),
|
|
100
|
+
mergeApprovalsGroup: (root) => root,
|
|
101
|
+
},
|
|
99
102
|
};
|
|
100
103
|
const ignoredSdk = createSDK(ignoredRoot, ignoredOptions);
|
|
101
104
|
const ignoredResult = ignoredSdk.poeCode.generate.text({
|
package/dist/sdk.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import "./node-require-shim.js";
|
|
|
2
2
|
import type { ObjectSchema, Static } from "toolcraft-schema";
|
|
3
3
|
import type { Group, HandlerFs, LogLevel, RuntimeLoggerInput, Scope } from "./index.js";
|
|
4
4
|
import { type ErrorReportsOption } from "./error-report.js";
|
|
5
|
-
import type { HumanInLoopPending,
|
|
5
|
+
import type { HumanInLoopPending, HumanInLoopRuntime } from "./human-in-loop/types.js";
|
|
6
6
|
import { type ValidationError } from "./validation-errors.js";
|
|
7
7
|
import type { StreamConsumerOptions, ToolcraftStream } from "./stream.js";
|
|
8
8
|
type ScopeInput = readonly Scope[] | undefined;
|
|
@@ -70,7 +70,7 @@ export interface CreateSDKOptions<TServices extends object = Record<string, unkn
|
|
|
70
70
|
fs?: HandlerFs;
|
|
71
71
|
services?: TServices;
|
|
72
72
|
casing?: "camel";
|
|
73
|
-
humanInLoop?:
|
|
73
|
+
humanInLoop?: HumanInLoopRuntime;
|
|
74
74
|
apiVersion?: string;
|
|
75
75
|
projectRoot?: string;
|
|
76
76
|
errorReports?: ErrorReportsOption;
|
package/dist/sdk.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import "./node-require-shim.js";
|
|
2
2
|
import { ToolcraftBugError, UserError, assertCommandRequirements, resolveCommandSecrets } from "./index.js";
|
|
3
3
|
import { writeErrorReport } from "./error-report.js";
|
|
4
|
-
import {
|
|
5
|
-
import { invokeWithHumanInLoop } from "./human-in-loop/index.js";
|
|
4
|
+
import { assertHumanInLoopWired, mergeApprovalsRoot } from "./human-in-loop/wiring.js";
|
|
6
5
|
import { hasMcpProxyGroups, resolveMcpProxies } from "./mcp-proxy.js";
|
|
7
6
|
import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
|
|
8
7
|
import { filterSchemaForScope } from "./schema-scope.js";
|
|
@@ -310,7 +309,8 @@ function defineMember(target, key, value) {
|
|
|
310
309
|
}
|
|
311
310
|
export function createSDK(root, options = {}) {
|
|
312
311
|
enableSourceMaps();
|
|
313
|
-
const mergedRoot =
|
|
312
|
+
const mergedRoot = mergeApprovalsRoot(root, options);
|
|
313
|
+
assertHumanInLoopWired(mergedRoot, options.humanInLoop);
|
|
314
314
|
if (!hasMcpProxyGroups(mergedRoot)) {
|
|
315
315
|
return createResolvedSDK(mergedRoot, options);
|
|
316
316
|
}
|
|
@@ -318,7 +318,7 @@ export function createSDK(root, options = {}) {
|
|
|
318
318
|
}
|
|
319
319
|
function createResolvedSDK(root, options = {}) {
|
|
320
320
|
const services = options.services ?? {};
|
|
321
|
-
const
|
|
321
|
+
const humanInLoop = options.humanInLoop;
|
|
322
322
|
const runtimeFetch = options.fetch ?? globalThis.fetch;
|
|
323
323
|
const diagnostics = createRuntimeLogger({
|
|
324
324
|
level: options.logLevel,
|
|
@@ -344,7 +344,7 @@ function createResolvedSDK(root, options = {}) {
|
|
|
344
344
|
secrets = resolveCommandSecrets(node, options.env);
|
|
345
345
|
const baseContext = {
|
|
346
346
|
...services,
|
|
347
|
-
|
|
347
|
+
humanInLoop,
|
|
348
348
|
root,
|
|
349
349
|
secrets,
|
|
350
350
|
fetch: runtimeFetch,
|
|
@@ -395,7 +395,7 @@ function createResolvedSDK(root, options = {}) {
|
|
|
395
395
|
secrets = resolveCommandSecrets(node, options.env);
|
|
396
396
|
const baseContext = {
|
|
397
397
|
...services,
|
|
398
|
-
|
|
398
|
+
humanInLoop,
|
|
399
399
|
root,
|
|
400
400
|
secrets,
|
|
401
401
|
fetch: runtimeFetch,
|
|
@@ -415,10 +415,13 @@ function createResolvedSDK(root, options = {}) {
|
|
|
415
415
|
throw new ToolcraftBugError(`command "${node.name}" must define an object params schema for SDK.`);
|
|
416
416
|
}
|
|
417
417
|
validatedParams = validateSDKArguments(paramsSchema, params);
|
|
418
|
-
|
|
418
|
+
const handlerContext = {
|
|
419
419
|
...baseContext,
|
|
420
420
|
params: validatedParams
|
|
421
|
-
}
|
|
421
|
+
};
|
|
422
|
+
return humanInLoop === undefined
|
|
423
|
+
? await node.handler(handlerContext)
|
|
424
|
+
: await humanInLoop.invoke(node, handlerContext, commandPath);
|
|
422
425
|
}
|
|
423
426
|
catch (error) {
|
|
424
427
|
await writeErrorReport({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { parseDocument, stringify } from "yaml";
|
|
3
|
-
import
|
|
3
|
+
import { TASK_SCHEMA_ID } from "../schema/ids.js";
|
|
4
4
|
import { eventsFromState, findEvent } from "../state-machine.js";
|
|
5
5
|
import { resolveStateMachine } from "../state.js";
|
|
6
6
|
import { AnchorNotFoundError, InvalidTransitionError, MalformedTaskError, OrderMismatchError, TaskAlreadyExistsError, TaskNotFoundError } from "../types.js";
|
|
@@ -9,7 +9,6 @@ const ARCHIVE_DIRECTORY_NAME = "archive";
|
|
|
9
9
|
const MARKDOWN_EXTENSION = ".md";
|
|
10
10
|
const TASK_KIND = "task";
|
|
11
11
|
const TASK_VERSION = 1;
|
|
12
|
-
const TASK_SCHEMA_ID = taskSchema.$id;
|
|
13
12
|
const MIN_PREFIX_WIDTH = 2;
|
|
14
13
|
const RESERVED_FRONTMATTER_KEYS = new Set([
|
|
15
14
|
"$schema",
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { isMap, parseDocument } from "yaml";
|
|
3
|
-
import
|
|
4
|
-
import taskSchema from "../schema/task.schema.json" with { type: "json" };
|
|
3
|
+
import { STORE_SCHEMA_ID, TASK_SCHEMA_ID } from "../schema/ids.js";
|
|
5
4
|
import { eventsFromState, findEvent } from "../state-machine.js";
|
|
6
5
|
import { resolveStateMachine } from "../state.js";
|
|
7
6
|
import { AnchorNotFoundError, InvalidTransitionError, MalformedTaskError, OrderMismatchError, TaskAlreadyExistsError, TaskNotFoundError } from "../types.js";
|
|
8
7
|
import { applyOrder, isTrimmedPrintableIdentifier, isRecord, rejectSymbolicLinkComponents, sortStrings, statIfExists, validateTaskId, validateTaskName, withFileLock, writeAtomically } from "./utils.js";
|
|
9
8
|
const STORE_KIND = "task-store";
|
|
10
|
-
const STORE_SCHEMA_ID = storeSchema.$id;
|
|
11
9
|
const STORE_VERSION = 1;
|
|
12
10
|
const TASK_KIND = "task";
|
|
13
|
-
const TASK_SCHEMA_ID = taskSchema.$id;
|
|
14
11
|
const TASK_VERSION = 1;
|
|
15
12
|
const RESERVED_TASK_KEYS = new Set([
|
|
16
13
|
"$schema",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Kept in sync with the sibling *.schema.json files by ids.test.ts. Inlined as
|
|
2
|
+
// constants so every artifact surface (tsc dist, esbuild bundles) works without
|
|
3
|
+
// runtime file lookups, and without JSON import attributes — a syntax error
|
|
4
|
+
// before Node 18.20 despite the >=18.18 engines floor (#517).
|
|
5
|
+
export const STORE_SCHEMA_ID = "https://poe-platform.github.io/poe-code/schemas/task-list/store.schema.json";
|
|
6
|
+
export const TASK_SCHEMA_ID = "https://poe-platform.github.io/poe-code/schemas/task-list/task.schema.json";
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import { parseArgs } from "node:util";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
5
6
|
import { createHttpServer } from "./http-server.js";
|
|
6
7
|
import { loadOAuthVerifier } from "./load-oauth-verifier.js";
|
|
7
|
-
|
|
8
|
+
// createRequire instead of JSON import attributes: `with { type: "json" }` is a
|
|
9
|
+
// syntax error before Node 18.20, and engines declares >=18.18 (#517).
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const packageJson = require("../package.json");
|
|
8
12
|
function readPackageInfo() {
|
|
9
13
|
return {
|
|
10
14
|
name: packageJson.name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.123",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,10 +22,6 @@
|
|
|
22
22
|
"types": "./dist/agent-defs.d.ts",
|
|
23
23
|
"import": "./dist/agent-defs.js"
|
|
24
24
|
},
|
|
25
|
-
"./agent-human-in-loop": {
|
|
26
|
-
"types": "./dist/agent-human-in-loop.d.ts",
|
|
27
|
-
"import": "./dist/agent-human-in-loop.js"
|
|
28
|
-
},
|
|
29
25
|
"./agent-mcp-config": {
|
|
30
26
|
"types": "./dist/agent-mcp-config.d.ts",
|
|
31
27
|
"import": "./dist/agent-mcp-config.js"
|
|
@@ -94,10 +90,6 @@
|
|
|
94
90
|
"types": "./dist/testing/index.d.ts",
|
|
95
91
|
"import": "./dist/testing/index.js"
|
|
96
92
|
},
|
|
97
|
-
"./task-list": {
|
|
98
|
-
"types": "./dist/task-list.d.ts",
|
|
99
|
-
"import": "./dist/task-list.js"
|
|
100
|
-
},
|
|
101
93
|
"./tiny-mcp-client": {
|
|
102
94
|
"types": "./dist/tiny-mcp-client.d.ts",
|
|
103
95
|
"import": "./dist/tiny-mcp-client.js"
|
|
@@ -160,7 +152,7 @@
|
|
|
160
152
|
"yaml"
|
|
161
153
|
],
|
|
162
154
|
"optionalDependencies": {
|
|
163
|
-
"toolcraft-schema": "0.0.
|
|
155
|
+
"toolcraft-schema": "0.0.123",
|
|
164
156
|
"toolcraft-design": "*",
|
|
165
157
|
"@poe-code/frontmatter": "*",
|
|
166
158
|
"@poe-code/agent-mcp-config": "*",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "@poe-code/agent-human-in-loop";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "@poe-code/agent-human-in-loop";
|
package/dist/task-list.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "@poe-code/task-list";
|
package/dist/task-list.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "@poe-code/task-list";
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://poe-platform.github.io/poe-code/schemas/task-list/store.schema.json",
|
|
4
|
-
"title": "Task Store",
|
|
5
|
-
"description": "YAML multi-list task store.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"properties": {
|
|
8
|
-
"$schema": {
|
|
9
|
-
"type": "string",
|
|
10
|
-
"const": "https://poe-platform.github.io/poe-code/schemas/task-list/store.schema.json"
|
|
11
|
-
},
|
|
12
|
-
"kind": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"const": "task-store"
|
|
15
|
-
},
|
|
16
|
-
"version": {
|
|
17
|
-
"type": "integer",
|
|
18
|
-
"const": 1
|
|
19
|
-
},
|
|
20
|
-
"lists": {
|
|
21
|
-
"type": "object",
|
|
22
|
-
"additionalProperties": {
|
|
23
|
-
"type": "object",
|
|
24
|
-
"additionalProperties": {
|
|
25
|
-
"$ref": "./task.schema.json"
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
"required": ["$schema", "kind", "version", "lists"],
|
|
31
|
-
"additionalProperties": false
|
|
32
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://poe-platform.github.io/poe-code/schemas/task-list/task.schema.json",
|
|
4
|
-
"title": "Task",
|
|
5
|
-
"description": "Persisted task payload used by task-list backends.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"properties": {
|
|
8
|
-
"$schema": {
|
|
9
|
-
"type": "string",
|
|
10
|
-
"const": "https://poe-platform.github.io/poe-code/schemas/task-list/task.schema.json"
|
|
11
|
-
},
|
|
12
|
-
"kind": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
"const": "task"
|
|
15
|
-
},
|
|
16
|
-
"version": {
|
|
17
|
-
"type": "integer",
|
|
18
|
-
"const": 1
|
|
19
|
-
},
|
|
20
|
-
"name": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"minLength": 1
|
|
23
|
-
},
|
|
24
|
-
"state": {
|
|
25
|
-
"type": "string"
|
|
26
|
-
},
|
|
27
|
-
"description": {
|
|
28
|
-
"type": "string"
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"required": ["name", "state"],
|
|
32
|
-
"additionalProperties": true
|
|
33
|
-
}
|