@agentteams/runner 0.0.69-dev.141 → 0.0.69-dev.144
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/dist/api-client.d.ts +0 -1
- package/dist/api-client.js +0 -11
- package/dist/api-client.js.map +1 -1
- package/dist/handlers/trigger-handler.js +13 -96
- package/dist/handlers/trigger-handler.js.map +1 -1
- package/dist/handlers/trigger-handler.test.js +139 -176
- package/dist/handlers/trigger-handler.test.js.map +1 -1
- package/dist/harness/config-loader.d.ts +3 -19
- package/dist/harness/config-loader.js +4 -89
- package/dist/harness/config-loader.js.map +1 -1
- package/dist/harness/config-loader.test.js +46 -112
- package/dist/harness/config-loader.test.js.map +1 -1
- package/dist/harness/types.d.ts +3 -3
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
- package/dist/harness/yaml-parser.d.ts +0 -12
- package/dist/harness/yaml-parser.js +0 -160
- package/dist/harness/yaml-parser.js.map +0 -1
|
@@ -36,6 +36,7 @@ const runtime = {
|
|
|
36
36
|
apiKey: "api-key",
|
|
37
37
|
teamId: "team-1",
|
|
38
38
|
projectId: "project-1",
|
|
39
|
+
runnerPrompt: "API runner prompt\n- History path: {{AGENTRUNNER_CURRENT_HISTORY_PATH}}\n- Previous history path: {{AGENTRUNNER_PARENT_HISTORY_PATH}}",
|
|
39
40
|
parentHistoryMarkdown: null,
|
|
40
41
|
useWorktree: false,
|
|
41
42
|
baseBranch: null,
|
|
@@ -108,10 +109,7 @@ test("createTriggerHandler runs the runner, reports history, and marks success",
|
|
|
108
109
|
await handler(trigger);
|
|
109
110
|
assert.deepEqual(discoveredAuthPaths, ["/auth/path"]);
|
|
110
111
|
assert.equal(runnerInputs.length, 1);
|
|
111
|
-
assert.
|
|
112
|
-
assert.match(runnerInputs[0]?.prompt ?? "", /Previous history path: \/auth\/path\/\.agentteams\/runner\/history\/parent-1\.md/);
|
|
113
|
-
assert.match(runnerInputs[0]?.prompt ?? "", /## Requests/);
|
|
114
|
-
assert.match(runnerInputs[0]?.prompt ?? "", /## History \(latest\)/);
|
|
112
|
+
assert.equal(runnerInputs[0]?.prompt, "API runner prompt\n- History path: /auth/path/.agentteams/runner/history/trigger-1.md\n- Previous history path: /auth/path/.agentteams/runner/history/parent-1.md");
|
|
115
113
|
assert.equal(logEntries.some((entry) => entry.level === "INFO" && entry.message.includes("stdout")), true);
|
|
116
114
|
assert.equal(logEntries.some((entry) => entry.level === "WARN" && entry.message.includes("stderr")), true);
|
|
117
115
|
assert.deepEqual(clientCalls.map((entry) => entry.method), [
|
|
@@ -563,25 +561,22 @@ const withTempDir = async (run) => {
|
|
|
563
561
|
await rm(dir, { recursive: true, force: true });
|
|
564
562
|
}
|
|
565
563
|
};
|
|
564
|
+
const withHarnessRuntime = (authPath, overrides = {}) => ({
|
|
565
|
+
...runtime,
|
|
566
|
+
authPath,
|
|
567
|
+
harnessConfigId: "harness-1",
|
|
568
|
+
...overrides,
|
|
569
|
+
});
|
|
570
|
+
const fetchServerHarness = (config) => async () => ({ config });
|
|
566
571
|
test("createTriggerHandler blocks trigger when pre-hook fails with onFailure=fail", async () => {
|
|
567
572
|
await withTempDir(async (dir) => {
|
|
568
|
-
// Write harness.yml with a failing pre-hook
|
|
569
|
-
const harnessDir = join(dir, ".agentteams");
|
|
570
|
-
await mkdir(harnessDir, { recursive: true });
|
|
571
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
572
|
-
"preHooks:",
|
|
573
|
-
" - name: lint-check",
|
|
574
|
-
" command: exit 1",
|
|
575
|
-
" onFailure: fail",
|
|
576
|
-
].join("\n"));
|
|
577
573
|
const clientCalls = [];
|
|
578
574
|
let runnerCalled = false;
|
|
579
575
|
const client = {
|
|
580
|
-
fetchTriggerRuntime: async () => (
|
|
581
|
-
|
|
582
|
-
|
|
576
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir),
|
|
577
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
578
|
+
preHooks: [{ name: "lint-check", command: "exit 1", onFailure: "fail" }],
|
|
583
579
|
}),
|
|
584
|
-
fetchHarnessConfig: async () => null,
|
|
585
580
|
isTriggerCancelRequested: async () => false,
|
|
586
581
|
updateTriggerHistory: async (...args) => {
|
|
587
582
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -628,22 +623,13 @@ test("createTriggerHandler blocks trigger when pre-hook fails with onFailure=fai
|
|
|
628
623
|
});
|
|
629
624
|
test("createTriggerHandler continues when pre-hook fails with onFailure=warn", async () => {
|
|
630
625
|
await withTempDir(async (dir) => {
|
|
631
|
-
const harnessDir = join(dir, ".agentteams");
|
|
632
|
-
await mkdir(harnessDir, { recursive: true });
|
|
633
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
634
|
-
"preHooks:",
|
|
635
|
-
" - name: optional-check",
|
|
636
|
-
" command: exit 1",
|
|
637
|
-
" onFailure: warn",
|
|
638
|
-
].join("\n"));
|
|
639
626
|
let runnerCalled = false;
|
|
640
627
|
const clientCalls = [];
|
|
641
628
|
const client = {
|
|
642
|
-
fetchTriggerRuntime: async () => (
|
|
643
|
-
|
|
644
|
-
|
|
629
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir),
|
|
630
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
631
|
+
preHooks: [{ name: "optional-check", command: "exit 1", onFailure: "warn" }],
|
|
645
632
|
}),
|
|
646
|
-
fetchHarnessConfig: async () => null,
|
|
647
633
|
isTriggerCancelRequested: async () => false,
|
|
648
634
|
updateTriggerHistory: async (...args) => {
|
|
649
635
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -689,15 +675,10 @@ test("createTriggerHandler continues when pre-hook fails with onFailure=warn", a
|
|
|
689
675
|
});
|
|
690
676
|
test("createTriggerHandler runs normally when no pre-hooks are defined", async () => {
|
|
691
677
|
await withTempDir(async (dir) => {
|
|
692
|
-
// No harness.yml — should behave exactly as before
|
|
693
678
|
let runnerCalled = false;
|
|
694
679
|
const clientCalls = [];
|
|
695
680
|
const client = {
|
|
696
|
-
fetchTriggerRuntime: async () => ({
|
|
697
|
-
...runtime,
|
|
698
|
-
authPath: dir,
|
|
699
|
-
}),
|
|
700
|
-
fetchHarnessConfig: async () => null,
|
|
681
|
+
fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir }),
|
|
701
682
|
isTriggerCancelRequested: async () => false,
|
|
702
683
|
updateTriggerHistory: async (...args) => {
|
|
703
684
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -741,26 +722,76 @@ test("createTriggerHandler runs normally when no pre-hooks are defined", async (
|
|
|
741
722
|
assert.equal(statusCall.args[1], "DONE");
|
|
742
723
|
});
|
|
743
724
|
});
|
|
744
|
-
test("createTriggerHandler
|
|
725
|
+
test("createTriggerHandler ignores local harness file when no server harness is selected", async () => {
|
|
745
726
|
await withTempDir(async (dir) => {
|
|
746
727
|
const harnessDir = join(dir, ".agentteams");
|
|
747
728
|
await mkdir(harnessDir, { recursive: true });
|
|
748
729
|
await writeFile(join(harnessDir, "harness.yml"), [
|
|
749
730
|
"preHooks:",
|
|
750
|
-
" - name:
|
|
751
|
-
" command: sh -c 'echo
|
|
731
|
+
" - name: local-should-not-run",
|
|
732
|
+
" command: sh -c 'echo local > local.txt && exit 1'",
|
|
752
733
|
" onFailure: fail",
|
|
753
734
|
].join("\n"));
|
|
754
735
|
let runnerCalled = false;
|
|
755
736
|
const clientCalls = [];
|
|
756
737
|
const client = {
|
|
757
|
-
fetchTriggerRuntime: async () => ({
|
|
758
|
-
|
|
759
|
-
|
|
738
|
+
fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir, harnessConfigId: null }),
|
|
739
|
+
isTriggerCancelRequested: async () => false,
|
|
740
|
+
updateTriggerHistory: async (...args) => {
|
|
741
|
+
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
742
|
+
},
|
|
743
|
+
updateTriggerStatus: async (...args) => {
|
|
744
|
+
clientCalls.push({ method: "updateTriggerStatus", args });
|
|
745
|
+
},
|
|
746
|
+
};
|
|
747
|
+
const handler = createTriggerHandler({
|
|
748
|
+
config: {
|
|
749
|
+
daemonToken: "daemon-token",
|
|
750
|
+
apiUrl: "https://api.example",
|
|
751
|
+
pollingIntervalMs: 5000,
|
|
752
|
+
timeoutMs: 1500,
|
|
753
|
+
idleTimeoutMs: 500,
|
|
754
|
+
runnerCmd: "opencode",
|
|
755
|
+
},
|
|
756
|
+
client: client,
|
|
757
|
+
}, {
|
|
758
|
+
createRunnerFactory: () => () => ({
|
|
759
|
+
run: async () => {
|
|
760
|
+
runnerCalled = true;
|
|
761
|
+
return { exitCode: 0 };
|
|
762
|
+
},
|
|
763
|
+
}),
|
|
764
|
+
createLogReporter: () => ({
|
|
765
|
+
start: () => undefined,
|
|
766
|
+
append: () => undefined,
|
|
767
|
+
stop: async () => undefined,
|
|
768
|
+
}),
|
|
769
|
+
readHistoryFile: async () => "### Summary\n- done\n",
|
|
770
|
+
resolveRunnerHistoryPaths: () => ({
|
|
771
|
+
currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
|
|
772
|
+
parentHistoryPath: null,
|
|
773
|
+
}),
|
|
774
|
+
});
|
|
775
|
+
await handler({ ...trigger, parentTriggerId: null });
|
|
776
|
+
assert.equal(runnerCalled, true);
|
|
777
|
+
await assert.rejects(access(join(dir, "local.txt")));
|
|
778
|
+
const statusCall = clientCalls.find((c) => c.method === "updateTriggerStatus");
|
|
779
|
+
assert.ok(statusCall);
|
|
780
|
+
assert.equal(statusCall.args[1], "DONE");
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
test("createTriggerHandler always runs pre-hooks without a convention trigger", async () => {
|
|
784
|
+
await withTempDir(async (dir) => {
|
|
785
|
+
let runnerCalled = false;
|
|
786
|
+
const clientCalls = [];
|
|
787
|
+
const client = {
|
|
788
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
|
|
760
789
|
conventions: [],
|
|
761
790
|
planType: "BUG_FIX",
|
|
762
791
|
}),
|
|
763
|
-
|
|
792
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
793
|
+
preHooks: [{ name: "always-run", command: "sh -c 'echo always > always.txt && exit 1'", onFailure: "fail" }],
|
|
794
|
+
}),
|
|
764
795
|
isTriggerCancelRequested: async () => false,
|
|
765
796
|
updateTriggerHistory: async (...args) => {
|
|
766
797
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -808,15 +839,6 @@ test("createTriggerHandler always runs pre-hooks without a convention trigger",
|
|
|
808
839
|
});
|
|
809
840
|
test("createTriggerHandler runs convention-linked pre-hooks when the convention matches", async () => {
|
|
810
841
|
await withTempDir(async (dir) => {
|
|
811
|
-
const harnessDir = join(dir, ".agentteams");
|
|
812
|
-
await mkdir(harnessDir, { recursive: true });
|
|
813
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
814
|
-
"preHooks:",
|
|
815
|
-
" - name: only-on-bugfix",
|
|
816
|
-
" command: sh -c 'echo bugfix > bugfix.txt && exit 1'",
|
|
817
|
-
" onFailure: fail",
|
|
818
|
-
" conventionTrigger: task:BUG_FIX",
|
|
819
|
-
].join("\n"));
|
|
820
842
|
let runnerCalled = false;
|
|
821
843
|
const clientCalls = [];
|
|
822
844
|
const conventions = [{
|
|
@@ -827,13 +849,18 @@ test("createTriggerHandler runs convention-linked pre-hooks when the convention
|
|
|
827
849
|
description: "Rules for bug fix tasks",
|
|
828
850
|
}];
|
|
829
851
|
const client = {
|
|
830
|
-
fetchTriggerRuntime: async () => ({
|
|
831
|
-
...runtime,
|
|
832
|
-
authPath: dir,
|
|
852
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
|
|
833
853
|
conventions,
|
|
834
854
|
planType: "BUG_FIX",
|
|
835
855
|
}),
|
|
836
|
-
|
|
856
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
857
|
+
preHooks: [{
|
|
858
|
+
name: "only-on-bugfix",
|
|
859
|
+
command: "sh -c 'echo bugfix > bugfix.txt && exit 1'",
|
|
860
|
+
onFailure: "fail",
|
|
861
|
+
conventionTrigger: "task:BUG_FIX",
|
|
862
|
+
}],
|
|
863
|
+
}),
|
|
837
864
|
isTriggerCancelRequested: async () => false,
|
|
838
865
|
updateTriggerHistory: async (...args) => {
|
|
839
866
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -881,15 +908,6 @@ test("createTriggerHandler runs convention-linked pre-hooks when the convention
|
|
|
881
908
|
});
|
|
882
909
|
test("createTriggerHandler skips convention-linked pre-hooks when the convention does not match", async () => {
|
|
883
910
|
await withTempDir(async (dir) => {
|
|
884
|
-
const harnessDir = join(dir, ".agentteams");
|
|
885
|
-
await mkdir(harnessDir, { recursive: true });
|
|
886
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
887
|
-
"preHooks:",
|
|
888
|
-
" - name: only-on-feature",
|
|
889
|
-
" command: sh -c 'echo feature > feature.txt && exit 1'",
|
|
890
|
-
" onFailure: fail",
|
|
891
|
-
" conventionTrigger: task:FEATURE",
|
|
892
|
-
].join("\n"));
|
|
893
911
|
let runnerCalled = false;
|
|
894
912
|
const clientCalls = [];
|
|
895
913
|
const conventions = [{
|
|
@@ -900,13 +918,18 @@ test("createTriggerHandler skips convention-linked pre-hooks when the convention
|
|
|
900
918
|
description: "Rules for bug fix tasks",
|
|
901
919
|
}];
|
|
902
920
|
const client = {
|
|
903
|
-
fetchTriggerRuntime: async () => ({
|
|
904
|
-
...runtime,
|
|
905
|
-
authPath: dir,
|
|
921
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
|
|
906
922
|
conventions,
|
|
907
923
|
planType: "BUG_FIX",
|
|
908
924
|
}),
|
|
909
|
-
|
|
925
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
926
|
+
preHooks: [{
|
|
927
|
+
name: "only-on-feature",
|
|
928
|
+
command: "sh -c 'echo feature > feature.txt && exit 1'",
|
|
929
|
+
onFailure: "fail",
|
|
930
|
+
conventionTrigger: "task:FEATURE",
|
|
931
|
+
}],
|
|
932
|
+
}),
|
|
910
933
|
isTriggerCancelRequested: async () => false,
|
|
911
934
|
updateTriggerHistory: async (...args) => {
|
|
912
935
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -953,18 +976,6 @@ test("createTriggerHandler skips convention-linked pre-hooks when the convention
|
|
|
953
976
|
});
|
|
954
977
|
test("createTriggerHandler runs unconditional hooks while skipping non-matching conditional hooks", async () => {
|
|
955
978
|
await withTempDir(async (dir) => {
|
|
956
|
-
const harnessDir = join(dir, ".agentteams");
|
|
957
|
-
await mkdir(harnessDir, { recursive: true });
|
|
958
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
959
|
-
"preHooks:",
|
|
960
|
-
" - name: always-run",
|
|
961
|
-
" command: sh -c 'echo always > always.txt'",
|
|
962
|
-
" onFailure: warn",
|
|
963
|
-
" - name: only-on-feature",
|
|
964
|
-
" command: sh -c 'echo feature > feature.txt && exit 1'",
|
|
965
|
-
" onFailure: fail",
|
|
966
|
-
" conventionTrigger: task:FEATURE",
|
|
967
|
-
].join("\n"));
|
|
968
979
|
let runnerCalled = false;
|
|
969
980
|
const clientCalls = [];
|
|
970
981
|
const conventions = [{
|
|
@@ -975,13 +986,21 @@ test("createTriggerHandler runs unconditional hooks while skipping non-matching
|
|
|
975
986
|
description: "Rules for bug fix tasks",
|
|
976
987
|
}];
|
|
977
988
|
const client = {
|
|
978
|
-
fetchTriggerRuntime: async () => ({
|
|
979
|
-
...runtime,
|
|
980
|
-
authPath: dir,
|
|
989
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
|
|
981
990
|
conventions,
|
|
982
991
|
planType: "BUG_FIX",
|
|
983
992
|
}),
|
|
984
|
-
|
|
993
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
994
|
+
preHooks: [
|
|
995
|
+
{ name: "always-run", command: "sh -c 'echo always > always.txt'", onFailure: "warn" },
|
|
996
|
+
{
|
|
997
|
+
name: "only-on-feature",
|
|
998
|
+
command: "sh -c 'echo feature > feature.txt && exit 1'",
|
|
999
|
+
onFailure: "fail",
|
|
1000
|
+
conventionTrigger: "task:FEATURE",
|
|
1001
|
+
},
|
|
1002
|
+
],
|
|
1003
|
+
}),
|
|
985
1004
|
isTriggerCancelRequested: async () => false,
|
|
986
1005
|
updateTriggerHistory: async (...args) => {
|
|
987
1006
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -1027,24 +1046,15 @@ test("createTriggerHandler runs unconditional hooks while skipping non-matching
|
|
|
1027
1046
|
assert.equal(statusCall.args[1], "DONE");
|
|
1028
1047
|
});
|
|
1029
1048
|
});
|
|
1030
|
-
test("createTriggerHandler
|
|
1049
|
+
test("createTriggerHandler passes the API-provided runner prompt unchanged", async () => {
|
|
1031
1050
|
await withTempDir(async (dir) => {
|
|
1032
1051
|
const runnerInputs = [];
|
|
1033
|
-
const conventions = [{
|
|
1034
|
-
id: "conv-bugfix",
|
|
1035
|
-
filePath: ".agentteams/rules/bugfix.md",
|
|
1036
|
-
trigger: "task:BUG_FIX",
|
|
1037
|
-
title: "Bug Fix Convention",
|
|
1038
|
-
description: "Rules for bug fix tasks",
|
|
1039
|
-
}];
|
|
1040
1052
|
const client = {
|
|
1041
1053
|
fetchTriggerRuntime: async () => ({
|
|
1042
1054
|
...runtime,
|
|
1043
1055
|
authPath: dir,
|
|
1044
|
-
|
|
1045
|
-
planType: "BUG_FIX",
|
|
1056
|
+
runnerPrompt: "Prompt generated by API\n\nDo exactly this.",
|
|
1046
1057
|
}),
|
|
1047
|
-
fetchHarnessConfig: async () => null,
|
|
1048
1058
|
isTriggerCancelRequested: async () => false,
|
|
1049
1059
|
updateTriggerHistory: async () => undefined,
|
|
1050
1060
|
updateTriggerStatus: async () => undefined,
|
|
@@ -1079,12 +1089,10 @@ test("createTriggerHandler injects context-matched conventions into the runner p
|
|
|
1079
1089
|
});
|
|
1080
1090
|
await handler({ ...trigger, parentTriggerId: null });
|
|
1081
1091
|
assert.equal(runnerInputs.length, 1);
|
|
1082
|
-
assert.
|
|
1083
|
-
assert.match(runnerInputs[0]?.prompt ?? "", /\[Context-Matched Conventions \(AUTO-LOADED\)\]/);
|
|
1084
|
-
assert.match(runnerInputs[0]?.prompt ?? "", /`\.agentteams\/rules\/bugfix\.md` — Rules for bug fix tasks/);
|
|
1092
|
+
assert.equal(runnerInputs[0]?.prompt, "Prompt generated by API\n\nDo exactly this.");
|
|
1085
1093
|
});
|
|
1086
1094
|
});
|
|
1087
|
-
test("createTriggerHandler
|
|
1095
|
+
test("createTriggerHandler does not append history or convention text to the API prompt", async () => {
|
|
1088
1096
|
await withTempDir(async (dir) => {
|
|
1089
1097
|
const runnerInputs = [];
|
|
1090
1098
|
const conventions = [{
|
|
@@ -1100,8 +1108,8 @@ test("createTriggerHandler omits auto-loaded convention prompt section when no c
|
|
|
1100
1108
|
authPath: dir,
|
|
1101
1109
|
conventions,
|
|
1102
1110
|
planType: "BUG_FIX",
|
|
1111
|
+
runnerPrompt: "Only the API prompt",
|
|
1103
1112
|
}),
|
|
1104
|
-
fetchHarnessConfig: async () => null,
|
|
1105
1113
|
isTriggerCancelRequested: async () => false,
|
|
1106
1114
|
updateTriggerHistory: async () => undefined,
|
|
1107
1115
|
updateTriggerStatus: async () => undefined,
|
|
@@ -1136,7 +1144,7 @@ test("createTriggerHandler omits auto-loaded convention prompt section when no c
|
|
|
1136
1144
|
});
|
|
1137
1145
|
await handler({ ...trigger, parentTriggerId: null });
|
|
1138
1146
|
assert.equal(runnerInputs.length, 1);
|
|
1139
|
-
assert.
|
|
1147
|
+
assert.equal(runnerInputs[0]?.prompt, "Only the API prompt");
|
|
1140
1148
|
assert.doesNotMatch(runnerInputs[0]?.prompt ?? "", /Context-Matched Conventions \(AUTO-LOADED\)/);
|
|
1141
1149
|
});
|
|
1142
1150
|
});
|
|
@@ -1145,18 +1153,12 @@ test("createTriggerHandler omits auto-loaded convention prompt section when no c
|
|
|
1145
1153
|
// ---------------------------------------------------------------------------
|
|
1146
1154
|
test("createTriggerHandler marks DONE when post-hook succeeds after runner success", async () => {
|
|
1147
1155
|
await withTempDir(async (dir) => {
|
|
1148
|
-
const harnessDir = join(dir, ".agentteams");
|
|
1149
|
-
await mkdir(harnessDir, { recursive: true });
|
|
1150
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
1151
|
-
"postHooks:",
|
|
1152
|
-
" - name: quality-gate",
|
|
1153
|
-
" command: echo ok",
|
|
1154
|
-
" onFailure: fail",
|
|
1155
|
-
].join("\n"));
|
|
1156
1156
|
const clientCalls = [];
|
|
1157
1157
|
const client = {
|
|
1158
|
-
fetchTriggerRuntime: async () => (
|
|
1159
|
-
|
|
1158
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir),
|
|
1159
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
1160
|
+
postHooks: [{ name: "quality-gate", command: "echo ok", onFailure: "fail" }],
|
|
1161
|
+
}),
|
|
1160
1162
|
isTriggerCancelRequested: async () => false,
|
|
1161
1163
|
updateTriggerHistory: async (...args) => {
|
|
1162
1164
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -1182,18 +1184,12 @@ test("createTriggerHandler marks DONE when post-hook succeeds after runner succe
|
|
|
1182
1184
|
});
|
|
1183
1185
|
test("createTriggerHandler marks FAILED when post-hook fails with onFailure=fail", async () => {
|
|
1184
1186
|
await withTempDir(async (dir) => {
|
|
1185
|
-
const harnessDir = join(dir, ".agentteams");
|
|
1186
|
-
await mkdir(harnessDir, { recursive: true });
|
|
1187
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
1188
|
-
"postHooks:",
|
|
1189
|
-
" - name: quality-gate",
|
|
1190
|
-
" command: exit 1",
|
|
1191
|
-
" onFailure: fail",
|
|
1192
|
-
].join("\n"));
|
|
1193
1187
|
const clientCalls = [];
|
|
1194
1188
|
const client = {
|
|
1195
|
-
fetchTriggerRuntime: async () => (
|
|
1196
|
-
|
|
1189
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir),
|
|
1190
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
1191
|
+
postHooks: [{ name: "quality-gate", command: "exit 1", onFailure: "fail" }],
|
|
1192
|
+
}),
|
|
1197
1193
|
isTriggerCancelRequested: async () => false,
|
|
1198
1194
|
updateTriggerHistory: async (...args) => {
|
|
1199
1195
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -1220,18 +1216,12 @@ test("createTriggerHandler marks FAILED when post-hook fails with onFailure=fail
|
|
|
1220
1216
|
});
|
|
1221
1217
|
test("createTriggerHandler marks NEEDS_REVIEW when post-hook fails with onFailure=needs_review", async () => {
|
|
1222
1218
|
await withTempDir(async (dir) => {
|
|
1223
|
-
const harnessDir = join(dir, ".agentteams");
|
|
1224
|
-
await mkdir(harnessDir, { recursive: true });
|
|
1225
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
1226
|
-
"postHooks:",
|
|
1227
|
-
" - name: review-gate",
|
|
1228
|
-
" command: exit 1",
|
|
1229
|
-
" onFailure: needs_review",
|
|
1230
|
-
].join("\n"));
|
|
1231
1219
|
const clientCalls = [];
|
|
1232
1220
|
const client = {
|
|
1233
|
-
fetchTriggerRuntime: async () => (
|
|
1234
|
-
|
|
1221
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir),
|
|
1222
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
1223
|
+
postHooks: [{ name: "review-gate", command: "exit 1", onFailure: "needs_review" }],
|
|
1224
|
+
}),
|
|
1235
1225
|
isTriggerCancelRequested: async () => false,
|
|
1236
1226
|
updateTriggerHistory: async (...args) => {
|
|
1237
1227
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -1257,19 +1247,13 @@ test("createTriggerHandler marks NEEDS_REVIEW when post-hook fails with onFailur
|
|
|
1257
1247
|
});
|
|
1258
1248
|
test("createTriggerHandler skips post-hooks when runner fails", async () => {
|
|
1259
1249
|
await withTempDir(async (dir) => {
|
|
1260
|
-
const harnessDir = join(dir, ".agentteams");
|
|
1261
|
-
await mkdir(harnessDir, { recursive: true });
|
|
1262
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
1263
|
-
"postHooks:",
|
|
1264
|
-
" - name: should-not-run",
|
|
1265
|
-
" command: echo unreachable",
|
|
1266
|
-
" onFailure: fail",
|
|
1267
|
-
].join("\n"));
|
|
1268
1250
|
const clientCalls = [];
|
|
1269
1251
|
const logEntries = [];
|
|
1270
1252
|
const client = {
|
|
1271
|
-
fetchTriggerRuntime: async () => (
|
|
1272
|
-
|
|
1253
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir),
|
|
1254
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
1255
|
+
postHooks: [{ name: "should-not-run", command: "echo unreachable", onFailure: "fail" }],
|
|
1256
|
+
}),
|
|
1273
1257
|
isTriggerCancelRequested: async () => false,
|
|
1274
1258
|
updateTriggerHistory: async (...args) => {
|
|
1275
1259
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -1298,14 +1282,8 @@ test("createTriggerHandler skips post-hooks when runner fails", async () => {
|
|
|
1298
1282
|
assert.equal(logEntries.some((l) => l.includes("post-execution")), false, "Post-hooks should not run when runner fails");
|
|
1299
1283
|
});
|
|
1300
1284
|
});
|
|
1301
|
-
test("createTriggerHandler
|
|
1285
|
+
test("createTriggerHandler leaves prompt convention audit recording to the API", async () => {
|
|
1302
1286
|
await withTempDir(async (dir) => {
|
|
1303
|
-
const harnessDir = join(dir, ".agentteams");
|
|
1304
|
-
await mkdir(harnessDir, { recursive: true });
|
|
1305
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
1306
|
-
"conventionIds:",
|
|
1307
|
-
" - conv-pinned",
|
|
1308
|
-
].join("\n"));
|
|
1309
1287
|
const conventions = [
|
|
1310
1288
|
{
|
|
1311
1289
|
id: "conv-auto",
|
|
@@ -1329,20 +1307,20 @@ test("createTriggerHandler records injected conventions with auto-match and harn
|
|
|
1329
1307
|
description: null,
|
|
1330
1308
|
},
|
|
1331
1309
|
];
|
|
1332
|
-
|
|
1310
|
+
let recordCalled = false;
|
|
1333
1311
|
const client = {
|
|
1334
|
-
fetchTriggerRuntime: async () => ({
|
|
1335
|
-
...runtime,
|
|
1336
|
-
authPath: dir,
|
|
1312
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
|
|
1337
1313
|
conventions,
|
|
1338
1314
|
planType: "BUG_FIX",
|
|
1339
1315
|
}),
|
|
1340
|
-
|
|
1316
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
1317
|
+
conventionIds: ["conv-pinned"],
|
|
1318
|
+
}),
|
|
1341
1319
|
isTriggerCancelRequested: async () => false,
|
|
1342
1320
|
updateTriggerHistory: async () => undefined,
|
|
1343
1321
|
updateTriggerStatus: async () => undefined,
|
|
1344
|
-
recordInjectedConventions: async (
|
|
1345
|
-
|
|
1322
|
+
recordInjectedConventions: async () => {
|
|
1323
|
+
recordCalled = true;
|
|
1346
1324
|
},
|
|
1347
1325
|
};
|
|
1348
1326
|
const handler = createTriggerHandler({
|
|
@@ -1371,23 +1349,11 @@ test("createTriggerHandler records injected conventions with auto-match and harn
|
|
|
1371
1349
|
}),
|
|
1372
1350
|
});
|
|
1373
1351
|
await handler({ ...trigger, parentTriggerId: null });
|
|
1374
|
-
assert.equal(
|
|
1375
|
-
assert.equal(recordedBatches[0]?.triggerId, "trigger-1");
|
|
1376
|
-
const items = recordedBatches[0]?.items ?? [];
|
|
1377
|
-
const byId = new Map(items.map((i) => [i.conventionId, i.source]));
|
|
1378
|
-
assert.equal(byId.get("conv-auto"), "AUTO_MATCH");
|
|
1379
|
-
assert.equal(byId.get("conv-pinned"), "HARNESS_PINNED");
|
|
1380
|
-
assert.equal(byId.has("conv-unrelated"), false);
|
|
1352
|
+
assert.equal(recordCalled, false);
|
|
1381
1353
|
});
|
|
1382
1354
|
});
|
|
1383
|
-
test("createTriggerHandler
|
|
1355
|
+
test("createTriggerHandler ignores unavailable prompt convention recording client method", async () => {
|
|
1384
1356
|
await withTempDir(async (dir) => {
|
|
1385
|
-
const harnessDir = join(dir, ".agentteams");
|
|
1386
|
-
await mkdir(harnessDir, { recursive: true });
|
|
1387
|
-
await writeFile(join(harnessDir, "harness.yml"), [
|
|
1388
|
-
"conventionIds:",
|
|
1389
|
-
" - conv-pinned",
|
|
1390
|
-
].join("\n"));
|
|
1391
1357
|
const conventions = [
|
|
1392
1358
|
{
|
|
1393
1359
|
id: "conv-pinned",
|
|
@@ -1399,13 +1365,13 @@ test("createTriggerHandler swallows recordInjectedConventions failures without f
|
|
|
1399
1365
|
];
|
|
1400
1366
|
const clientCalls = [];
|
|
1401
1367
|
const client = {
|
|
1402
|
-
fetchTriggerRuntime: async () => ({
|
|
1403
|
-
...runtime,
|
|
1404
|
-
authPath: dir,
|
|
1368
|
+
fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
|
|
1405
1369
|
conventions,
|
|
1406
1370
|
planType: "BUG_FIX",
|
|
1407
1371
|
}),
|
|
1408
|
-
|
|
1372
|
+
fetchHarnessConfigById: fetchServerHarness({
|
|
1373
|
+
conventionIds: ["conv-pinned"],
|
|
1374
|
+
}),
|
|
1409
1375
|
isTriggerCancelRequested: async () => false,
|
|
1410
1376
|
updateTriggerHistory: async (...args) => {
|
|
1411
1377
|
clientCalls.push({ method: "updateTriggerHistory", args });
|
|
@@ -1413,9 +1379,6 @@ test("createTriggerHandler swallows recordInjectedConventions failures without f
|
|
|
1413
1379
|
updateTriggerStatus: async (...args) => {
|
|
1414
1380
|
clientCalls.push({ method: "updateTriggerStatus", args });
|
|
1415
1381
|
},
|
|
1416
|
-
recordInjectedConventions: async () => {
|
|
1417
|
-
throw new Error("server unavailable");
|
|
1418
|
-
},
|
|
1419
1382
|
};
|
|
1420
1383
|
const handler = createTriggerHandler({
|
|
1421
1384
|
config: {
|