@poncho-ai/harness 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/dist/index.js +96 -2
- package/package.json +2 -2
- package/src/harness.ts +68 -4
- package/src/tool-dispatcher.ts +35 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.
|
|
2
|
+
> @poncho-ai/harness@0.9.0 build /Users/cesar/Dev/latitude/poncho-ai/packages/harness
|
|
3
3
|
> tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2022
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
11
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mESM[39m [1mdist/index.js [22m[32m133.06 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 29ms
|
|
12
12
|
[34mDTS[39m Build start
|
|
13
|
-
[32mDTS[39m ⚡️ Build success in
|
|
13
|
+
[32mDTS[39m ⚡️ Build success in 3021ms
|
|
14
14
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m17.17 KB[39m
|
package/dist/index.js
CHANGED
|
@@ -2032,6 +2032,13 @@ var ToolDispatcher = class {
|
|
|
2032
2032
|
return this.tools.get(name);
|
|
2033
2033
|
}
|
|
2034
2034
|
async execute(call, context) {
|
|
2035
|
+
if (context.abortSignal?.aborted) {
|
|
2036
|
+
return {
|
|
2037
|
+
callId: call.id,
|
|
2038
|
+
tool: call.name,
|
|
2039
|
+
error: "Tool execution cancelled"
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2035
2042
|
const definition = this.tools.get(call.name);
|
|
2036
2043
|
if (!definition) {
|
|
2037
2044
|
return {
|
|
@@ -2042,12 +2049,26 @@ var ToolDispatcher = class {
|
|
|
2042
2049
|
}
|
|
2043
2050
|
try {
|
|
2044
2051
|
const output = await definition.handler(call.input, context);
|
|
2052
|
+
if (context.abortSignal?.aborted) {
|
|
2053
|
+
return {
|
|
2054
|
+
callId: call.id,
|
|
2055
|
+
tool: call.name,
|
|
2056
|
+
error: "Tool execution cancelled"
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2045
2059
|
return {
|
|
2046
2060
|
callId: call.id,
|
|
2047
2061
|
tool: call.name,
|
|
2048
2062
|
output
|
|
2049
2063
|
};
|
|
2050
2064
|
} catch (error) {
|
|
2065
|
+
if (context.abortSignal?.aborted) {
|
|
2066
|
+
return {
|
|
2067
|
+
callId: call.id,
|
|
2068
|
+
tool: call.name,
|
|
2069
|
+
error: "Tool execution cancelled"
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2051
2072
|
return {
|
|
2052
2073
|
callId: call.id,
|
|
2053
2074
|
tool: call.name,
|
|
@@ -2056,7 +2077,19 @@ var ToolDispatcher = class {
|
|
|
2056
2077
|
}
|
|
2057
2078
|
}
|
|
2058
2079
|
async executeBatch(calls, context) {
|
|
2059
|
-
|
|
2080
|
+
const results = [];
|
|
2081
|
+
for (const call of calls) {
|
|
2082
|
+
if (context.abortSignal?.aborted) {
|
|
2083
|
+
results.push({
|
|
2084
|
+
callId: call.id,
|
|
2085
|
+
tool: call.name,
|
|
2086
|
+
error: "Tool execution cancelled"
|
|
2087
|
+
});
|
|
2088
|
+
continue;
|
|
2089
|
+
}
|
|
2090
|
+
results.push(await this.execute(call, context));
|
|
2091
|
+
}
|
|
2092
|
+
return results;
|
|
2060
2093
|
}
|
|
2061
2094
|
};
|
|
2062
2095
|
|
|
@@ -2072,6 +2105,17 @@ var SKILL_TOOL_NAMES = [
|
|
|
2072
2105
|
"run_skill_script"
|
|
2073
2106
|
];
|
|
2074
2107
|
var trimMessageWindow = (messages) => messages.length <= MAX_CONTEXT_MESSAGES ? messages : messages.slice(messages.length - MAX_CONTEXT_MESSAGES);
|
|
2108
|
+
var isAbortError = (error) => {
|
|
2109
|
+
if (!error || typeof error !== "object") {
|
|
2110
|
+
return false;
|
|
2111
|
+
}
|
|
2112
|
+
const maybeName = "name" in error ? String(error.name ?? "") : "";
|
|
2113
|
+
if (maybeName === "AbortError") {
|
|
2114
|
+
return true;
|
|
2115
|
+
}
|
|
2116
|
+
const maybeMessage = "message" in error ? String(error.message ?? "") : "";
|
|
2117
|
+
return maybeMessage.toLowerCase().includes("abort");
|
|
2118
|
+
};
|
|
2075
2119
|
var MODEL_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
2076
2120
|
var toProviderSafeToolName = (originalName, index, used) => {
|
|
2077
2121
|
if (MODEL_TOOL_NAME_PATTERN.test(originalName) && !used.has(originalName)) {
|
|
@@ -2582,6 +2626,12 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2582
2626
|
events.push(event);
|
|
2583
2627
|
return event;
|
|
2584
2628
|
};
|
|
2629
|
+
const isCancelled = () => input.abortSignal?.aborted === true;
|
|
2630
|
+
let cancellationEmitted = false;
|
|
2631
|
+
const emitCancellation = () => {
|
|
2632
|
+
cancellationEmitted = true;
|
|
2633
|
+
return pushEvent({ type: "run:cancelled", runId });
|
|
2634
|
+
};
|
|
2585
2635
|
yield pushEvent({
|
|
2586
2636
|
type: "run:started",
|
|
2587
2637
|
runId,
|
|
@@ -2597,6 +2647,10 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2597
2647
|
let totalOutputTokens = 0;
|
|
2598
2648
|
for (let step = 1; step <= maxSteps; step += 1) {
|
|
2599
2649
|
try {
|
|
2650
|
+
if (isCancelled()) {
|
|
2651
|
+
yield emitCancellation();
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2600
2654
|
if (now() - start > timeoutMs) {
|
|
2601
2655
|
yield pushEvent({
|
|
2602
2656
|
type: "run:error",
|
|
@@ -2690,6 +2744,7 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2690
2744
|
messages: coreMessages,
|
|
2691
2745
|
tools,
|
|
2692
2746
|
temperature,
|
|
2747
|
+
abortSignal: input.abortSignal,
|
|
2693
2748
|
...typeof maxTokens === "number" ? { maxTokens } : {},
|
|
2694
2749
|
experimental_telemetry: {
|
|
2695
2750
|
isEnabled: telemetryEnabled && !!latitudeApiKey
|
|
@@ -2698,10 +2753,18 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2698
2753
|
let streamedAnyChunk = false;
|
|
2699
2754
|
let fullText = "";
|
|
2700
2755
|
for await (const chunk of result.textStream) {
|
|
2756
|
+
if (isCancelled()) {
|
|
2757
|
+
yield emitCancellation();
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2701
2760
|
streamedAnyChunk = true;
|
|
2702
2761
|
fullText += chunk;
|
|
2703
2762
|
yield pushEvent({ type: "model:chunk", content: chunk });
|
|
2704
2763
|
}
|
|
2764
|
+
if (isCancelled()) {
|
|
2765
|
+
yield emitCancellation();
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2705
2768
|
const fullResult = await result.response;
|
|
2706
2769
|
const usage = await result.usage;
|
|
2707
2770
|
const toolCallsResult = await result.toolCalls;
|
|
@@ -2746,11 +2809,16 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2746
2809
|
agentId: agent.frontmatter.id ?? agent.frontmatter.name,
|
|
2747
2810
|
step,
|
|
2748
2811
|
workingDir: this.workingDir,
|
|
2749
|
-
parameters: input.parameters ?? {}
|
|
2812
|
+
parameters: input.parameters ?? {},
|
|
2813
|
+
abortSignal: input.abortSignal
|
|
2750
2814
|
};
|
|
2751
2815
|
const toolResultsForModel = [];
|
|
2752
2816
|
const approvedCalls = [];
|
|
2753
2817
|
for (const call of toolCalls) {
|
|
2818
|
+
if (isCancelled()) {
|
|
2819
|
+
yield emitCancellation();
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2754
2822
|
const runtimeToolName = exposedToolNames.get(call.name) ?? call.name;
|
|
2755
2823
|
yield pushEvent({ type: "tool:started", tool: runtimeToolName, input: call.input });
|
|
2756
2824
|
const requiresApproval = this.requiresApprovalForToolCall(
|
|
@@ -2772,6 +2840,10 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2772
2840
|
step,
|
|
2773
2841
|
approvalId
|
|
2774
2842
|
}) : false;
|
|
2843
|
+
if (isCancelled()) {
|
|
2844
|
+
yield emitCancellation();
|
|
2845
|
+
return;
|
|
2846
|
+
}
|
|
2775
2847
|
if (!approved) {
|
|
2776
2848
|
yield pushEvent({
|
|
2777
2849
|
type: "tool:approval:denied",
|
|
@@ -2801,7 +2873,15 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2801
2873
|
});
|
|
2802
2874
|
}
|
|
2803
2875
|
const batchStart = now();
|
|
2876
|
+
if (isCancelled()) {
|
|
2877
|
+
yield emitCancellation();
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
2804
2880
|
const batchResults = approvedCalls.length > 0 ? await this.dispatcher.executeBatch(approvedCalls, toolContext) : [];
|
|
2881
|
+
if (isCancelled()) {
|
|
2882
|
+
yield emitCancellation();
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2805
2885
|
for (const result2 of batchResults) {
|
|
2806
2886
|
if (result2.error) {
|
|
2807
2887
|
yield pushEvent({
|
|
@@ -2855,6 +2935,12 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2855
2935
|
duration: now() - stepStart
|
|
2856
2936
|
});
|
|
2857
2937
|
} catch (error) {
|
|
2938
|
+
if (isCancelled() || isAbortError(error)) {
|
|
2939
|
+
if (!cancellationEmitted) {
|
|
2940
|
+
yield emitCancellation();
|
|
2941
|
+
}
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2858
2944
|
yield pushEvent({
|
|
2859
2945
|
type: "run:error",
|
|
2860
2946
|
runId,
|
|
@@ -2903,6 +2989,14 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
2903
2989
|
duration: 0
|
|
2904
2990
|
};
|
|
2905
2991
|
}
|
|
2992
|
+
if (event.type === "run:cancelled") {
|
|
2993
|
+
finalResult = {
|
|
2994
|
+
status: "cancelled",
|
|
2995
|
+
steps: 0,
|
|
2996
|
+
tokens: { input: 0, output: 0, cached: 0 },
|
|
2997
|
+
duration: 0
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
2906
3000
|
}
|
|
2907
3001
|
return {
|
|
2908
3002
|
runId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"redis": "^5.10.0",
|
|
31
31
|
"yaml": "^2.4.0",
|
|
32
32
|
"zod": "^3.22.0",
|
|
33
|
-
"@poncho-ai/sdk": "0.
|
|
33
|
+
"@poncho-ai/sdk": "0.4.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/mustache": "^4.2.6",
|
package/src/harness.ts
CHANGED
|
@@ -69,6 +69,18 @@ const trimMessageWindow = (messages: Message[]): Message[] =>
|
|
|
69
69
|
? messages
|
|
70
70
|
: messages.slice(messages.length - MAX_CONTEXT_MESSAGES);
|
|
71
71
|
|
|
72
|
+
const isAbortError = (error: unknown): boolean => {
|
|
73
|
+
if (!error || typeof error !== "object") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const maybeName = "name" in error ? String(error.name ?? "") : "";
|
|
77
|
+
if (maybeName === "AbortError") {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const maybeMessage = "message" in error ? String(error.message ?? "") : "";
|
|
81
|
+
return maybeMessage.toLowerCase().includes("abort");
|
|
82
|
+
};
|
|
83
|
+
|
|
72
84
|
const MODEL_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
73
85
|
|
|
74
86
|
const toProviderSafeToolName = (
|
|
@@ -658,6 +670,12 @@ ${boundedMainMemory.trim()}`
|
|
|
658
670
|
events.push(event);
|
|
659
671
|
return event;
|
|
660
672
|
};
|
|
673
|
+
const isCancelled = (): boolean => input.abortSignal?.aborted === true;
|
|
674
|
+
let cancellationEmitted = false;
|
|
675
|
+
const emitCancellation = (): AgentEvent => {
|
|
676
|
+
cancellationEmitted = true;
|
|
677
|
+
return pushEvent({ type: "run:cancelled", runId });
|
|
678
|
+
};
|
|
661
679
|
|
|
662
680
|
yield pushEvent({
|
|
663
681
|
type: "run:started",
|
|
@@ -677,6 +695,10 @@ ${boundedMainMemory.trim()}`
|
|
|
677
695
|
|
|
678
696
|
for (let step = 1; step <= maxSteps; step += 1) {
|
|
679
697
|
try {
|
|
698
|
+
if (isCancelled()) {
|
|
699
|
+
yield emitCancellation();
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
680
702
|
if (now() - start > timeoutMs) {
|
|
681
703
|
yield pushEvent({
|
|
682
704
|
type: "run:error",
|
|
@@ -793,6 +815,7 @@ ${boundedMainMemory.trim()}`
|
|
|
793
815
|
messages: coreMessages,
|
|
794
816
|
tools,
|
|
795
817
|
temperature,
|
|
818
|
+
abortSignal: input.abortSignal,
|
|
796
819
|
...(typeof maxTokens === "number" ? { maxTokens } : {}),
|
|
797
820
|
experimental_telemetry: {
|
|
798
821
|
isEnabled: telemetryEnabled && !!latitudeApiKey,
|
|
@@ -803,10 +826,19 @@ ${boundedMainMemory.trim()}`
|
|
|
803
826
|
let streamedAnyChunk = false;
|
|
804
827
|
let fullText = "";
|
|
805
828
|
for await (const chunk of result.textStream) {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
829
|
+
if (isCancelled()) {
|
|
830
|
+
yield emitCancellation();
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
streamedAnyChunk = true;
|
|
834
|
+
fullText += chunk;
|
|
835
|
+
yield pushEvent({ type: "model:chunk", content: chunk });
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (isCancelled()) {
|
|
839
|
+
yield emitCancellation();
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
810
842
|
|
|
811
843
|
// Get full response with usage and tool calls
|
|
812
844
|
const fullResult = await result.response;
|
|
@@ -861,6 +893,7 @@ ${boundedMainMemory.trim()}`
|
|
|
861
893
|
step,
|
|
862
894
|
workingDir: this.workingDir,
|
|
863
895
|
parameters: input.parameters ?? {},
|
|
896
|
+
abortSignal: input.abortSignal,
|
|
864
897
|
};
|
|
865
898
|
|
|
866
899
|
const toolResultsForModel: Array<{
|
|
@@ -877,6 +910,10 @@ ${boundedMainMemory.trim()}`
|
|
|
877
910
|
}> = [];
|
|
878
911
|
|
|
879
912
|
for (const call of toolCalls) {
|
|
913
|
+
if (isCancelled()) {
|
|
914
|
+
yield emitCancellation();
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
880
917
|
const runtimeToolName = exposedToolNames.get(call.name) ?? call.name;
|
|
881
918
|
yield pushEvent({ type: "tool:started", tool: runtimeToolName, input: call.input });
|
|
882
919
|
const requiresApproval = this.requiresApprovalForToolCall(
|
|
@@ -900,6 +937,10 @@ ${boundedMainMemory.trim()}`
|
|
|
900
937
|
approvalId,
|
|
901
938
|
})
|
|
902
939
|
: false;
|
|
940
|
+
if (isCancelled()) {
|
|
941
|
+
yield emitCancellation();
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
903
944
|
if (!approved) {
|
|
904
945
|
yield pushEvent({
|
|
905
946
|
type: "tool:approval:denied",
|
|
@@ -929,11 +970,20 @@ ${boundedMainMemory.trim()}`
|
|
|
929
970
|
});
|
|
930
971
|
}
|
|
931
972
|
const batchStart = now();
|
|
973
|
+
if (isCancelled()) {
|
|
974
|
+
yield emitCancellation();
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
932
977
|
const batchResults =
|
|
933
978
|
approvedCalls.length > 0
|
|
934
979
|
? await this.dispatcher.executeBatch(approvedCalls, toolContext)
|
|
935
980
|
: [];
|
|
936
981
|
|
|
982
|
+
if (isCancelled()) {
|
|
983
|
+
yield emitCancellation();
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
|
|
937
987
|
for (const result of batchResults) {
|
|
938
988
|
if (result.error) {
|
|
939
989
|
yield pushEvent({
|
|
@@ -993,6 +1043,12 @@ ${boundedMainMemory.trim()}`
|
|
|
993
1043
|
duration: now() - stepStart,
|
|
994
1044
|
});
|
|
995
1045
|
} catch (error) {
|
|
1046
|
+
if (isCancelled() || isAbortError(error)) {
|
|
1047
|
+
if (!cancellationEmitted) {
|
|
1048
|
+
yield emitCancellation();
|
|
1049
|
+
}
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
996
1052
|
yield pushEvent({
|
|
997
1053
|
type: "run:error",
|
|
998
1054
|
runId,
|
|
@@ -1044,6 +1100,14 @@ ${boundedMainMemory.trim()}`
|
|
|
1044
1100
|
duration: 0,
|
|
1045
1101
|
};
|
|
1046
1102
|
}
|
|
1103
|
+
if (event.type === "run:cancelled") {
|
|
1104
|
+
finalResult = {
|
|
1105
|
+
status: "cancelled",
|
|
1106
|
+
steps: 0,
|
|
1107
|
+
tokens: { input: 0, output: 0, cached: 0 },
|
|
1108
|
+
duration: 0,
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1047
1111
|
}
|
|
1048
1112
|
|
|
1049
1113
|
return {
|
package/src/tool-dispatcher.ts
CHANGED
|
@@ -45,6 +45,13 @@ export class ToolDispatcher {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async execute(call: ToolCall, context: ToolContext): Promise<ToolExecutionResult> {
|
|
48
|
+
if (context.abortSignal?.aborted) {
|
|
49
|
+
return {
|
|
50
|
+
callId: call.id,
|
|
51
|
+
tool: call.name,
|
|
52
|
+
error: "Tool execution cancelled",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
48
55
|
const definition = this.tools.get(call.name);
|
|
49
56
|
if (!definition) {
|
|
50
57
|
return {
|
|
@@ -56,12 +63,26 @@ export class ToolDispatcher {
|
|
|
56
63
|
|
|
57
64
|
try {
|
|
58
65
|
const output = await definition.handler(call.input, context);
|
|
66
|
+
if (context.abortSignal?.aborted) {
|
|
67
|
+
return {
|
|
68
|
+
callId: call.id,
|
|
69
|
+
tool: call.name,
|
|
70
|
+
error: "Tool execution cancelled",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
59
73
|
return {
|
|
60
74
|
callId: call.id,
|
|
61
75
|
tool: call.name,
|
|
62
76
|
output,
|
|
63
77
|
};
|
|
64
78
|
} catch (error) {
|
|
79
|
+
if (context.abortSignal?.aborted) {
|
|
80
|
+
return {
|
|
81
|
+
callId: call.id,
|
|
82
|
+
tool: call.name,
|
|
83
|
+
error: "Tool execution cancelled",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
65
86
|
return {
|
|
66
87
|
callId: call.id,
|
|
67
88
|
tool: call.name,
|
|
@@ -74,6 +95,19 @@ export class ToolDispatcher {
|
|
|
74
95
|
calls: ToolCall[],
|
|
75
96
|
context: ToolContext,
|
|
76
97
|
): Promise<ToolExecutionResult[]> {
|
|
77
|
-
|
|
98
|
+
const results: ToolExecutionResult[] = [];
|
|
99
|
+
for (const call of calls) {
|
|
100
|
+
if (context.abortSignal?.aborted) {
|
|
101
|
+
results.push({
|
|
102
|
+
callId: call.id,
|
|
103
|
+
tool: call.name,
|
|
104
|
+
error: "Tool execution cancelled",
|
|
105
|
+
});
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
// eslint-disable-next-line no-await-in-loop
|
|
109
|
+
results.push(await this.execute(call, context));
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
78
112
|
}
|
|
79
113
|
}
|