@poncho-ai/harness 0.59.13 → 0.59.14
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/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +18 -0
- package/dist/index.js +76 -11
- package/package.json +1 -1
- package/src/ask-user-tool.ts +95 -0
- package/src/default-agent.ts +1 -1
- package/src/harness.ts +9 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.59.
|
|
2
|
+
> @poncho-ai/harness@0.59.14 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
|
|
3
3
|
> node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[embed-docs] Generated poncho-docs.ts with 4 topics
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mESM[39m Build start
|
|
11
11
|
[32mESM[39m [1mdist/isolate-F2PPSUL6.js [22m[32m53.82 KB[39m
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m564.58 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 217ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 8055ms
|
|
16
16
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m102.50 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @poncho-ai/harness
|
|
2
2
|
|
|
3
|
+
## 0.59.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#172](https://github.com/cesr/poncho-ai/pull/172) [`dc61836`](https://github.com/cesr/poncho-ai/commit/dc61836aa99e3cb6e1339dc6f82ffdab522918ba) Thanks [@cesr](https://github.com/cesr)! - Add the `ask_user` built-in tool: the agent can pause the run to ask the
|
|
8
|
+
user a structured, multiple-choice question (the in-app analog of Claude
|
|
9
|
+
Code's AskUserQuestion) instead of asking in plain prose. Each call
|
|
10
|
+
carries 1–4 questions, each with a short header, a `multiSelect` flag,
|
|
11
|
+
and pre-made options; a free-text "Other" escape is rendered by the
|
|
12
|
+
client.
|
|
13
|
+
|
|
14
|
+
The tool is forced to client (`device`) dispatch, so the harness pauses
|
|
15
|
+
the run on a checkpoint carrying the questions and the consumer resumes
|
|
16
|
+
by injecting the user's selections as the tool result — no server-side
|
|
17
|
+
execution (the handler is a defensive stub). The default agent prompt
|
|
18
|
+
now steers the model to reach for `ask_user` whenever it would otherwise
|
|
19
|
+
stop to ask the user to choose between options.
|
|
20
|
+
|
|
3
21
|
## 0.59.13
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -710,7 +710,7 @@ Environment: {{runtime.environment}}
|
|
|
710
710
|
|
|
711
711
|
- Use tools when needed
|
|
712
712
|
- Explain your reasoning clearly
|
|
713
|
-
-
|
|
713
|
+
- When requirements are ambiguous or you need the user to choose between options, use the \`ask_user\` tool to ask a structured multiple-choice question instead of writing the question as plain text. Reserve plain-text questions for genuinely open-ended asks that have no sensible pre-made options.
|
|
714
714
|
- Never claim a file/tool change unless the corresponding tool call actually succeeded
|
|
715
715
|
`;
|
|
716
716
|
};
|
|
@@ -2445,7 +2445,7 @@ var ponchoDocsTool = defineTool({
|
|
|
2445
2445
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
2446
2446
|
import { readFile as readFile9 } from "fs/promises";
|
|
2447
2447
|
import { resolve as resolve11 } from "path";
|
|
2448
|
-
import { defineTool as
|
|
2448
|
+
import { defineTool as defineTool13, getTextContent as getTextContent2, createLogger as createLogger7, formatError as fmtErr, url as urlColor } from "@poncho-ai/sdk";
|
|
2449
2449
|
|
|
2450
2450
|
// src/upload-store.ts
|
|
2451
2451
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -8591,10 +8591,71 @@ var createSearchTools = () => [
|
|
|
8591
8591
|
})
|
|
8592
8592
|
];
|
|
8593
8593
|
|
|
8594
|
-
// src/
|
|
8594
|
+
// src/ask-user-tool.ts
|
|
8595
8595
|
import { defineTool as defineTool11 } from "@poncho-ai/sdk";
|
|
8596
|
+
var createAskUserTool = () => defineTool11({
|
|
8597
|
+
name: "ask_user",
|
|
8598
|
+
description: "Ask the user one or more structured multiple-choice questions and wait for their answer. Use this INSTEAD of writing a question as plain text whenever you would otherwise pause to let the user choose between options \u2014 clarifying ambiguous requirements, picking an approach, confirming a direction. The user sees tappable option chips and answers with a tap. Prefer this over a prose question: it is faster for the user and gives you a clean answer. Guidelines: ask 1\u20134 questions at once, each with 2\u20134 concrete options; keep `header` very short (a few words); write each option `label` short and its `description` to one line. The user can always type a custom 'Other' answer, so you do not need to add one. Do NOT call any other tool in the same turn as ask_user, and call it at most once per turn. After the user answers you will receive their selections and may continue.",
|
|
8599
|
+
inputSchema: {
|
|
8600
|
+
type: "object",
|
|
8601
|
+
properties: {
|
|
8602
|
+
questions: {
|
|
8603
|
+
type: "array",
|
|
8604
|
+
description: "1\u20134 questions to ask the user at once.",
|
|
8605
|
+
items: {
|
|
8606
|
+
type: "object",
|
|
8607
|
+
properties: {
|
|
8608
|
+
question: {
|
|
8609
|
+
type: "string",
|
|
8610
|
+
description: "The full question text shown to the user."
|
|
8611
|
+
},
|
|
8612
|
+
header: {
|
|
8613
|
+
type: "string",
|
|
8614
|
+
description: "A very short label for this question (a few words, ~12 chars), shown as a chip."
|
|
8615
|
+
},
|
|
8616
|
+
multiSelect: {
|
|
8617
|
+
type: "boolean",
|
|
8618
|
+
description: "If true, the user may select multiple options. Defaults to false (single choice)."
|
|
8619
|
+
},
|
|
8620
|
+
options: {
|
|
8621
|
+
type: "array",
|
|
8622
|
+
description: "The pre-made options. A free-text 'Other' option is added automatically by the client.",
|
|
8623
|
+
items: {
|
|
8624
|
+
type: "object",
|
|
8625
|
+
properties: {
|
|
8626
|
+
label: {
|
|
8627
|
+
type: "string",
|
|
8628
|
+
description: "Short, selectable label for this option."
|
|
8629
|
+
},
|
|
8630
|
+
description: {
|
|
8631
|
+
type: "string",
|
|
8632
|
+
description: "A one-line explanation of what this option means."
|
|
8633
|
+
}
|
|
8634
|
+
},
|
|
8635
|
+
required: ["label"],
|
|
8636
|
+
additionalProperties: false
|
|
8637
|
+
}
|
|
8638
|
+
}
|
|
8639
|
+
},
|
|
8640
|
+
required: ["question", "header", "options"],
|
|
8641
|
+
additionalProperties: false
|
|
8642
|
+
}
|
|
8643
|
+
}
|
|
8644
|
+
},
|
|
8645
|
+
required: ["questions"],
|
|
8646
|
+
additionalProperties: false
|
|
8647
|
+
},
|
|
8648
|
+
handler: async () => {
|
|
8649
|
+
return {
|
|
8650
|
+
error: "ask_user must be answered by the user on the client; it cannot run server-side. This indicates a dispatch misconfiguration."
|
|
8651
|
+
};
|
|
8652
|
+
}
|
|
8653
|
+
});
|
|
8654
|
+
|
|
8655
|
+
// src/subagent-tools.ts
|
|
8656
|
+
import { defineTool as defineTool12 } from "@poncho-ai/sdk";
|
|
8596
8657
|
var createSubagentTools = (manager) => [
|
|
8597
|
-
|
|
8658
|
+
defineTool12({
|
|
8598
8659
|
name: "spawn_subagent",
|
|
8599
8660
|
description: "Spawn a subagent to work on a task in the background. Returns immediately with a subagent ID. The subagent runs independently and its result will be delivered to you as a message in the conversation when it completes.\n\nGuidelines:\n- Spawn all needed subagents in a SINGLE response (they run concurrently), then end your turn with a brief message to the user.\n- Do NOT spawn more subagents in follow-up steps. Wait for results to be delivered before deciding if more work is needed.\n- Prefer doing work yourself for simple or quick tasks. Spawn subagents for substantial, self-contained work.\n- The subagent has no memory of your conversation -- write thorough, self-contained instructions in the task.",
|
|
8600
8661
|
inputSchema: {
|
|
@@ -8629,7 +8690,7 @@ var createSubagentTools = (manager) => [
|
|
|
8629
8690
|
return { subagentId, status: "running" };
|
|
8630
8691
|
}
|
|
8631
8692
|
}),
|
|
8632
|
-
|
|
8693
|
+
defineTool12({
|
|
8633
8694
|
name: "message_subagent",
|
|
8634
8695
|
description: "Send a follow-up message to a completed or stopped subagent. The subagent restarts in the background and its result will be delivered to you as a message when it completes. Only works when the subagent is not currently running.",
|
|
8635
8696
|
inputSchema: {
|
|
@@ -8657,7 +8718,7 @@ var createSubagentTools = (manager) => [
|
|
|
8657
8718
|
return { subagentId: id, status: "running" };
|
|
8658
8719
|
}
|
|
8659
8720
|
}),
|
|
8660
|
-
|
|
8721
|
+
defineTool12({
|
|
8661
8722
|
name: "stop_subagent",
|
|
8662
8723
|
description: "Stop a running subagent. The subagent's conversation is preserved but it will stop processing. Use this to cancel work that is no longer needed.",
|
|
8663
8724
|
inputSchema: {
|
|
@@ -8680,7 +8741,7 @@ var createSubagentTools = (manager) => [
|
|
|
8680
8741
|
return { message: `Subagent "${subagentId}" has been stopped.` };
|
|
8681
8742
|
}
|
|
8682
8743
|
}),
|
|
8683
|
-
|
|
8744
|
+
defineTool12({
|
|
8684
8745
|
name: "list_subagents",
|
|
8685
8746
|
description: "List all subagents that have been spawned in this conversation. Returns each subagent's ID, original task, current status, and message count. Use this to look up subagent IDs before calling message_subagent or stop_subagent.",
|
|
8686
8747
|
inputSchema: {
|
|
@@ -8700,7 +8761,7 @@ var createSubagentTools = (manager) => [
|
|
|
8700
8761
|
return { subagents };
|
|
8701
8762
|
}
|
|
8702
8763
|
}),
|
|
8703
|
-
|
|
8764
|
+
defineTool12({
|
|
8704
8765
|
name: "read_subagent",
|
|
8705
8766
|
description: "Fetch the conversation transcript of a subagent you spawned. Use this to inspect a subagent's intermediate reasoning, tool calls, or full output -- instead of asking it to repeat its work via message_subagent.\n\nModes:\n- 'final' (default): just the last assistant message. Cheap.\n- 'assistant': all assistant messages, no tool calls/results.\n- 'full': every message including tool calls and results. Can be large.\n\nUse since_index / max_messages to page through long transcripts. Only works on subagents directly spawned by this conversation.",
|
|
8706
8767
|
inputSchema: {
|
|
@@ -9578,6 +9639,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9578
9639
|
}
|
|
9579
9640
|
/** Returns the normalized {access, dispatch} mode for the tool. */
|
|
9580
9641
|
resolveToolMode(toolName) {
|
|
9642
|
+
if (toolName === "ask_user") return { dispatch: "device" };
|
|
9581
9643
|
return normalizeToolAccess(this.resolveToolAccess(toolName));
|
|
9582
9644
|
}
|
|
9583
9645
|
isToolEnabled(name) {
|
|
@@ -9619,6 +9681,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
9619
9681
|
this.registerIfMissing(tool);
|
|
9620
9682
|
}
|
|
9621
9683
|
}
|
|
9684
|
+
if (this.isToolEnabled("ask_user")) {
|
|
9685
|
+
this.registerIfMissing(createAskUserTool());
|
|
9686
|
+
}
|
|
9622
9687
|
if (this.environment === "development" && this.isToolEnabled("poncho_docs")) {
|
|
9623
9688
|
this.registerIfMissing(ponchoDocsTool);
|
|
9624
9689
|
}
|
|
@@ -9627,7 +9692,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9627
9692
|
}
|
|
9628
9693
|
}
|
|
9629
9694
|
createGetToolResultByIdTool() {
|
|
9630
|
-
return
|
|
9695
|
+
return defineTool13({
|
|
9631
9696
|
name: "get_tool_result_by_id",
|
|
9632
9697
|
description: "Retrieve a previously archived full tool result by id for the current conversation. Use this when older tool outputs were truncated in prompt history.",
|
|
9633
9698
|
inputSchema: {
|
|
@@ -14654,7 +14719,7 @@ ${draft.toolTimeline.join("\n")}`);
|
|
|
14654
14719
|
};
|
|
14655
14720
|
|
|
14656
14721
|
// src/index.ts
|
|
14657
|
-
import { defineTool as
|
|
14722
|
+
import { defineTool as defineTool14 } from "@poncho-ai/sdk";
|
|
14658
14723
|
export {
|
|
14659
14724
|
AgentHarness,
|
|
14660
14725
|
AgentOrchestrator,
|
|
@@ -14728,7 +14793,7 @@ export {
|
|
|
14728
14793
|
createWriteTool,
|
|
14729
14794
|
decodeFileInputData,
|
|
14730
14795
|
defaultAgentDefinition,
|
|
14731
|
-
|
|
14796
|
+
defineTool14 as defineTool,
|
|
14732
14797
|
deleteOpenAICodexSession,
|
|
14733
14798
|
deriveUploadKey,
|
|
14734
14799
|
ensureAgentIdentity,
|
package/package.json
CHANGED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { defineTool, type ToolDefinition } from "@poncho-ai/sdk";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// ask_user — pause the run to ask the user a structured, multiple-choice
|
|
5
|
+
// question with pre-made options (the in-app analog of Claude Code's
|
|
6
|
+
// AskUserQuestion). The client renders tappable option chips so the user
|
|
7
|
+
// answers with a tap instead of typing prose.
|
|
8
|
+
//
|
|
9
|
+
// This tool is dispatched to the client ("device" dispatch, forced in
|
|
10
|
+
// AgentHarness.resolveToolMode): the harness pauses the run, emits a
|
|
11
|
+
// checkpoint carrying the questions payload, and the consumer (PonchOS)
|
|
12
|
+
// resumes the run by POSTing the user's selections back as this tool's
|
|
13
|
+
// result. The handler below is a defensive stub — device dispatch
|
|
14
|
+
// intercepts the call before any server-side execution, so it must never
|
|
15
|
+
// actually run.
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export const createAskUserTool = (): ToolDefinition =>
|
|
19
|
+
defineTool({
|
|
20
|
+
name: "ask_user",
|
|
21
|
+
description:
|
|
22
|
+
"Ask the user one or more structured multiple-choice questions and wait for their answer. " +
|
|
23
|
+
"Use this INSTEAD of writing a question as plain text whenever you would otherwise pause to " +
|
|
24
|
+
"let the user choose between options — clarifying ambiguous requirements, picking an approach, " +
|
|
25
|
+
"confirming a direction. The user sees tappable option chips and answers with a tap. " +
|
|
26
|
+
"Prefer this over a prose question: it is faster for the user and gives you a clean answer. " +
|
|
27
|
+
"Guidelines: ask 1–4 questions at once, each with 2–4 concrete options; keep `header` very short " +
|
|
28
|
+
"(a few words); write each option `label` short and its `description` to one line. The user can " +
|
|
29
|
+
"always type a custom 'Other' answer, so you do not need to add one. Do NOT call any other tool " +
|
|
30
|
+
"in the same turn as ask_user, and call it at most once per turn. After the user answers you will " +
|
|
31
|
+
"receive their selections and may continue.",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
questions: {
|
|
36
|
+
type: "array",
|
|
37
|
+
description: "1–4 questions to ask the user at once.",
|
|
38
|
+
items: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
question: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "The full question text shown to the user.",
|
|
44
|
+
},
|
|
45
|
+
header: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description:
|
|
48
|
+
"A very short label for this question (a few words, ~12 chars), shown as a chip.",
|
|
49
|
+
},
|
|
50
|
+
multiSelect: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
description:
|
|
53
|
+
"If true, the user may select multiple options. Defaults to false (single choice).",
|
|
54
|
+
},
|
|
55
|
+
options: {
|
|
56
|
+
type: "array",
|
|
57
|
+
description:
|
|
58
|
+
"The pre-made options. A free-text 'Other' option is added automatically by the client.",
|
|
59
|
+
items: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
label: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Short, selectable label for this option.",
|
|
65
|
+
},
|
|
66
|
+
description: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "A one-line explanation of what this option means.",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ["label"],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ["question", "header", "options"],
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ["questions"],
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
},
|
|
84
|
+
handler: async () => {
|
|
85
|
+
// Unreachable in normal operation: ask_user is forced to client/device
|
|
86
|
+
// dispatch, so the harness checkpoints before this handler is invoked.
|
|
87
|
+
// If it ever runs, the tool was misconfigured (dispatch not forced) —
|
|
88
|
+
// surface an error rather than silently resolving with no user input.
|
|
89
|
+
return {
|
|
90
|
+
error:
|
|
91
|
+
"ask_user must be answered by the user on the client; it cannot run server-side. " +
|
|
92
|
+
"This indicates a dispatch misconfiguration.",
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
});
|
package/src/default-agent.ts
CHANGED
|
@@ -100,7 +100,7 @@ Environment: {{runtime.environment}}
|
|
|
100
100
|
|
|
101
101
|
- Use tools when needed
|
|
102
102
|
- Explain your reasoning clearly
|
|
103
|
-
-
|
|
103
|
+
- When requirements are ambiguous or you need the user to choose between options, use the \`ask_user\` tool to ask a structured multiple-choice question instead of writing the question as plain text. Reserve plain-text questions for genuinely open-ended asks that have no sensible pre-made options.
|
|
104
104
|
- Never claim a file/tool change unless the corresponding tool call actually succeeded
|
|
105
105
|
`;
|
|
106
106
|
};
|
package/src/harness.ts
CHANGED
|
@@ -65,6 +65,7 @@ import { jsonSchemaToZod } from "./schema-converter.js";
|
|
|
65
65
|
import type { SkillMetadata } from "./skill-context.js";
|
|
66
66
|
import { createSkillTools, normalizeScriptPolicyPath } from "./skill-tools.js";
|
|
67
67
|
import { createSearchTools } from "./search-tools.js";
|
|
68
|
+
import { createAskUserTool } from "./ask-user-tool.js";
|
|
68
69
|
import { createSubagentTools } from "./subagent-tools.js";
|
|
69
70
|
import type { SubagentManager } from "./subagent-manager.js";
|
|
70
71
|
import { trace, context as otelContext, createContextKey, type Context as OtelContextType, SpanStatusCode, SpanKind, diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
|
|
@@ -965,6 +966,11 @@ export class AgentHarness {
|
|
|
965
966
|
|
|
966
967
|
/** Returns the normalized {access, dispatch} mode for the tool. */
|
|
967
968
|
private resolveToolMode(toolName: string): { access?: "approval"; dispatch?: "device" } {
|
|
969
|
+
// ask_user is always answered by the user on the client. Force device
|
|
970
|
+
// dispatch unconditionally so it pauses the run and checkpoints rather
|
|
971
|
+
// than running its (defensive, error-returning) server-side handler —
|
|
972
|
+
// even if no `poncho.config.js` entry exists for it.
|
|
973
|
+
if (toolName === "ask_user") return { dispatch: "device" };
|
|
968
974
|
return normalizeToolAccess(this.resolveToolAccess(toolName));
|
|
969
975
|
}
|
|
970
976
|
|
|
@@ -1014,6 +1020,9 @@ export class AgentHarness {
|
|
|
1014
1020
|
this.registerIfMissing(tool);
|
|
1015
1021
|
}
|
|
1016
1022
|
}
|
|
1023
|
+
if (this.isToolEnabled("ask_user")) {
|
|
1024
|
+
this.registerIfMissing(createAskUserTool());
|
|
1025
|
+
}
|
|
1017
1026
|
if (this.environment === "development" && this.isToolEnabled("poncho_docs")) {
|
|
1018
1027
|
this.registerIfMissing(ponchoDocsTool);
|
|
1019
1028
|
}
|