@bojackduy/opencode-loopd 1.8.2 → 1.8.3
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/server.js +89 -115
- package/dist/tui.js +7 -7
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1,34 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
2
|
// src/domain/runtime.ts
|
|
18
|
-
var exports_runtime = {};
|
|
19
|
-
__export(exports_runtime, {
|
|
20
|
-
acquireLease: () => acquireLease,
|
|
21
|
-
addToolCall: () => addToolCall,
|
|
22
|
-
createRuntimeState: () => createRuntimeState,
|
|
23
|
-
hasActiveToolCalls: () => hasActiveToolCalls,
|
|
24
|
-
leaseIsValid: () => leaseIsValid,
|
|
25
|
-
markParentNotified: () => markParentNotified,
|
|
26
|
-
markProgress: () => markProgress,
|
|
27
|
-
recordActivity: () => recordActivity,
|
|
28
|
-
releaseLease: () => releaseLease,
|
|
29
|
-
removeToolCall: () => removeToolCall,
|
|
30
|
-
shouldNotifyParent: () => shouldNotifyParent
|
|
31
|
-
});
|
|
32
3
|
function createRuntimeState(goalID) {
|
|
33
4
|
const now = new Date().toISOString();
|
|
34
5
|
return {
|
|
@@ -135,9 +106,6 @@ function removeToolCall(rt, callID) {
|
|
|
135
106
|
updatedAt: new Date().toISOString()
|
|
136
107
|
};
|
|
137
108
|
}
|
|
138
|
-
function hasActiveToolCalls(rt) {
|
|
139
|
-
return (rt.activeToolCallIDs?.length ?? 0) > 0;
|
|
140
|
-
}
|
|
141
109
|
var PARENT_NOTIFY_DEDUPE_MS = 60000;
|
|
142
110
|
|
|
143
111
|
// src/application/control-worker.ts
|
|
@@ -176,8 +144,8 @@ async function acquireLock(directory, key, operation) {
|
|
|
176
144
|
try {
|
|
177
145
|
try {
|
|
178
146
|
const raw = await fs.readFile(lockPath, "utf8");
|
|
179
|
-
const
|
|
180
|
-
const age = Date.now() - Date.parse(
|
|
147
|
+
const meta = JSON.parse(raw);
|
|
148
|
+
const age = Date.now() - Date.parse(meta.acquiredAt);
|
|
181
149
|
if (age > LOCK_STALE_MS) {
|
|
182
150
|
await fs.rm(lockPath, { force: true });
|
|
183
151
|
}
|
|
@@ -712,54 +680,54 @@ function createControlWorker(options) {
|
|
|
712
680
|
ownerSessionID: args.ownerSessionID,
|
|
713
681
|
config: resolution.config
|
|
714
682
|
});
|
|
715
|
-
const
|
|
683
|
+
const state = await readState(directory);
|
|
716
684
|
response = {
|
|
717
685
|
...base,
|
|
718
686
|
message: `goal "${args.name}" created (${goal.id.slice(0, 8)}...)`,
|
|
719
|
-
stateRevision:
|
|
687
|
+
stateRevision: state.revision
|
|
720
688
|
};
|
|
721
689
|
break;
|
|
722
690
|
}
|
|
723
691
|
case "pause": {
|
|
724
692
|
await goalSvc.pause(directory, request.goalID);
|
|
725
|
-
const
|
|
726
|
-
const goal =
|
|
693
|
+
const state = await readState(directory);
|
|
694
|
+
const goal = state.goals.find((g) => g.id === request.goalID);
|
|
727
695
|
response = {
|
|
728
696
|
...base,
|
|
729
697
|
message: `goal "${goal?.name || request.goalID}" paused`,
|
|
730
|
-
stateRevision:
|
|
698
|
+
stateRevision: state.revision
|
|
731
699
|
};
|
|
732
700
|
break;
|
|
733
701
|
}
|
|
734
702
|
case "resume": {
|
|
735
703
|
await goalSvc.resume(directory, request.goalID);
|
|
736
|
-
const
|
|
737
|
-
const goal =
|
|
704
|
+
const state = await readState(directory);
|
|
705
|
+
const goal = state.goals.find((g) => g.id === request.goalID);
|
|
738
706
|
response = {
|
|
739
707
|
...base,
|
|
740
708
|
message: `goal "${goal?.name || request.goalID}" resumed`,
|
|
741
|
-
stateRevision:
|
|
709
|
+
stateRevision: state.revision
|
|
742
710
|
};
|
|
743
711
|
break;
|
|
744
712
|
}
|
|
745
713
|
case "retry": {
|
|
746
714
|
await goalSvc.retry(directory, request.goalID);
|
|
747
|
-
const
|
|
748
|
-
const goal =
|
|
715
|
+
const state = await readState(directory);
|
|
716
|
+
const goal = state.goals.find((g) => g.id === request.goalID);
|
|
749
717
|
response = {
|
|
750
718
|
...base,
|
|
751
719
|
message: `goal "${goal?.name || request.goalID}" retried`,
|
|
752
|
-
stateRevision:
|
|
720
|
+
stateRevision: state.revision
|
|
753
721
|
};
|
|
754
722
|
break;
|
|
755
723
|
}
|
|
756
724
|
case "clear": {
|
|
757
725
|
await goalSvc.clear(directory, request.goalID);
|
|
758
|
-
const
|
|
726
|
+
const state = await readState(directory);
|
|
759
727
|
response = {
|
|
760
728
|
...base,
|
|
761
729
|
message: `goal cleared`,
|
|
762
|
-
stateRevision:
|
|
730
|
+
stateRevision: state.revision
|
|
763
731
|
};
|
|
764
732
|
break;
|
|
765
733
|
}
|
|
@@ -775,25 +743,25 @@ function createControlWorker(options) {
|
|
|
775
743
|
break;
|
|
776
744
|
}
|
|
777
745
|
await appendGoalInbox(directory, request.goalID, "user", text);
|
|
778
|
-
const
|
|
779
|
-
const goal =
|
|
746
|
+
const state = await readState(directory);
|
|
747
|
+
const goal = state.goals.find((g) => g.id === request.goalID);
|
|
780
748
|
response = {
|
|
781
749
|
...base,
|
|
782
750
|
message: `sent to "${goal?.name || request.goalID}"`,
|
|
783
|
-
stateRevision:
|
|
751
|
+
stateRevision: state.revision
|
|
784
752
|
};
|
|
785
753
|
break;
|
|
786
754
|
}
|
|
787
755
|
case "force_complete": {
|
|
788
756
|
const args = request.args;
|
|
789
|
-
const
|
|
790
|
-
const goal =
|
|
757
|
+
const state = await readState(directory);
|
|
758
|
+
const goal = state.goals.find((g) => g.id === request.goalID);
|
|
791
759
|
if (!goal) {
|
|
792
760
|
response = { ...base, ok: false, message: "goal not found", errorCode: "not_found" };
|
|
793
761
|
break;
|
|
794
762
|
}
|
|
795
763
|
if (goal.status === "complete") {
|
|
796
|
-
response = { ...base, message: `goal "${goal.name}" already complete`, stateRevision:
|
|
764
|
+
response = { ...base, message: `goal "${goal.name}" already complete`, stateRevision: state.revision };
|
|
797
765
|
break;
|
|
798
766
|
}
|
|
799
767
|
goal.status = "complete";
|
|
@@ -803,14 +771,14 @@ function createControlWorker(options) {
|
|
|
803
771
|
evidence: String(args.evidence || "Manual override \u2014 no verification checks run."),
|
|
804
772
|
at: new Date().toISOString()
|
|
805
773
|
};
|
|
806
|
-
const runtime =
|
|
774
|
+
const runtime = state.runtimes.find((r) => r.goalID === goal.id);
|
|
807
775
|
if (runtime) {
|
|
808
776
|
Object.assign(runtime, releaseLease(runtime));
|
|
809
777
|
runtime.activeRunID = undefined;
|
|
810
778
|
runtime.lastError = undefined;
|
|
811
779
|
runtime.updatedAt = new Date().toISOString();
|
|
812
780
|
}
|
|
813
|
-
await writeState(directory,
|
|
781
|
+
await writeState(directory, state);
|
|
814
782
|
await appendEvent(directory, {
|
|
815
783
|
version: 1,
|
|
816
784
|
eventID: randomUUID(),
|
|
@@ -819,22 +787,22 @@ function createControlWorker(options) {
|
|
|
819
787
|
summary: goal.completionEvidence.summary,
|
|
820
788
|
evidence: goal.completionEvidence.evidence,
|
|
821
789
|
timestamp: new Date().toISOString(),
|
|
822
|
-
revision:
|
|
790
|
+
revision: state.revision
|
|
823
791
|
});
|
|
824
|
-
response = { ...base, message: `goal "${goal.name}" force-completed`, stateRevision:
|
|
792
|
+
response = { ...base, message: `goal "${goal.name}" force-completed`, stateRevision: state.revision };
|
|
825
793
|
break;
|
|
826
794
|
}
|
|
827
795
|
case "force_block":
|
|
828
796
|
case "block": {
|
|
829
797
|
const args = request.args;
|
|
830
|
-
const
|
|
831
|
-
const goal =
|
|
798
|
+
const state = await readState(directory);
|
|
799
|
+
const goal = state.goals.find((g) => g.id === request.goalID);
|
|
832
800
|
if (!goal) {
|
|
833
801
|
response = { ...base, ok: false, message: "goal not found", errorCode: "not_found" };
|
|
834
802
|
break;
|
|
835
803
|
}
|
|
836
804
|
if (goal.status === "blocked") {
|
|
837
|
-
response = { ...base, message: `goal "${goal.name}" already blocked`, stateRevision:
|
|
805
|
+
response = { ...base, message: `goal "${goal.name}" already blocked`, stateRevision: state.revision };
|
|
838
806
|
break;
|
|
839
807
|
}
|
|
840
808
|
goal.status = "blocked";
|
|
@@ -844,14 +812,14 @@ function createControlWorker(options) {
|
|
|
844
812
|
needed: String(args.needed || "User intervention required."),
|
|
845
813
|
at: new Date().toISOString()
|
|
846
814
|
};
|
|
847
|
-
const runtime =
|
|
815
|
+
const runtime = state.runtimes.find((r) => r.goalID === goal.id);
|
|
848
816
|
if (runtime) {
|
|
849
817
|
Object.assign(runtime, releaseLease(runtime));
|
|
850
818
|
runtime.activeRunID = undefined;
|
|
851
819
|
runtime.lastError = undefined;
|
|
852
820
|
runtime.updatedAt = new Date().toISOString();
|
|
853
821
|
}
|
|
854
|
-
await writeState(directory,
|
|
822
|
+
await writeState(directory, state);
|
|
855
823
|
await appendEvent(directory, {
|
|
856
824
|
version: 1,
|
|
857
825
|
eventID: randomUUID(),
|
|
@@ -860,9 +828,9 @@ function createControlWorker(options) {
|
|
|
860
828
|
reason: goal.blocker.reason,
|
|
861
829
|
needed: goal.blocker.needed,
|
|
862
830
|
timestamp: new Date().toISOString(),
|
|
863
|
-
revision:
|
|
831
|
+
revision: state.revision
|
|
864
832
|
});
|
|
865
|
-
response = { ...base, message: `goal "${goal.name}" blocked`, stateRevision:
|
|
833
|
+
response = { ...base, message: `goal "${goal.name}" blocked`, stateRevision: state.revision };
|
|
866
834
|
break;
|
|
867
835
|
}
|
|
868
836
|
default: {
|
|
@@ -878,8 +846,8 @@ function createControlWorker(options) {
|
|
|
878
846
|
await recordInLedger(directory, request);
|
|
879
847
|
return response;
|
|
880
848
|
}
|
|
881
|
-
async function recordInLedger(
|
|
882
|
-
const state = await readState(
|
|
849
|
+
async function recordInLedger(directory, request) {
|
|
850
|
+
const state = await readState(directory);
|
|
883
851
|
if (!state.commandLedger)
|
|
884
852
|
state.commandLedger = [];
|
|
885
853
|
state.commandLedger.push({
|
|
@@ -892,7 +860,7 @@ function createControlWorker(options) {
|
|
|
892
860
|
if (state.commandLedger.length > MAX_LEDGER_SIZE) {
|
|
893
861
|
state.commandLedger = state.commandLedger.slice(-MAX_LEDGER_SIZE);
|
|
894
862
|
}
|
|
895
|
-
await writeState(
|
|
863
|
+
await writeState(directory, state);
|
|
896
864
|
}
|
|
897
865
|
return { start, stop: async () => {
|
|
898
866
|
await stop();
|
|
@@ -1879,9 +1847,9 @@ function createGoalService(host) {
|
|
|
1879
1847
|
rt.lastScheduleAt = undefined;
|
|
1880
1848
|
}
|
|
1881
1849
|
state.runtimes.push(rt);
|
|
1882
|
-
const
|
|
1883
|
-
if (
|
|
1884
|
-
|
|
1850
|
+
const runtime = state.runtimes.find((r) => r.goalID === id);
|
|
1851
|
+
if (runtime)
|
|
1852
|
+
runtime.phase = "queued";
|
|
1885
1853
|
return state;
|
|
1886
1854
|
});
|
|
1887
1855
|
let runtime = state1.runtimes.find((r) => r.goalID === id);
|
|
@@ -2122,17 +2090,17 @@ function createGoalService(host) {
|
|
|
2122
2090
|
}
|
|
2123
2091
|
async function resumeUnlocked(directory, goalID) {
|
|
2124
2092
|
let resumed = false;
|
|
2125
|
-
const state = await mutateState(directory, `goal.resume:${goalID}`, async (
|
|
2126
|
-
const
|
|
2127
|
-
if (!
|
|
2128
|
-
return
|
|
2129
|
-
if (!canTransition(
|
|
2130
|
-
return
|
|
2131
|
-
assertWorkspaceWriteAvailable(
|
|
2132
|
-
|
|
2133
|
-
|
|
2093
|
+
const state = await mutateState(directory, `goal.resume:${goalID}`, async (state) => {
|
|
2094
|
+
const goal = state.goals.find((g) => g.id === goalID);
|
|
2095
|
+
if (!goal)
|
|
2096
|
+
return state;
|
|
2097
|
+
if (!canTransition(goal.status, "active", "user"))
|
|
2098
|
+
return state;
|
|
2099
|
+
assertWorkspaceWriteAvailable(state, goal);
|
|
2100
|
+
goal.status = "active";
|
|
2101
|
+
goal.updatedAt = new Date().toISOString();
|
|
2134
2102
|
resumed = true;
|
|
2135
|
-
return
|
|
2103
|
+
return state;
|
|
2136
2104
|
});
|
|
2137
2105
|
const goal = state.goals.find((g) => g.id === goalID);
|
|
2138
2106
|
if (!goal || !resumed)
|
|
@@ -2162,15 +2130,15 @@ function createGoalService(host) {
|
|
|
2162
2130
|
}
|
|
2163
2131
|
async function retryUnlocked(directory, goalID) {
|
|
2164
2132
|
let retried = false;
|
|
2165
|
-
const state = await mutateState(directory, `goal.retry:${goalID}`, async (
|
|
2166
|
-
const
|
|
2167
|
-
if (!
|
|
2168
|
-
return
|
|
2169
|
-
assertWorkspaceWriteAvailable(
|
|
2170
|
-
|
|
2171
|
-
|
|
2133
|
+
const state = await mutateState(directory, `goal.retry:${goalID}`, async (state) => {
|
|
2134
|
+
const goal = state.goals.find((g) => g.id === goalID);
|
|
2135
|
+
if (!goal || goal.status !== "blocked")
|
|
2136
|
+
return state;
|
|
2137
|
+
assertWorkspaceWriteAvailable(state, goal);
|
|
2138
|
+
goal.status = "active";
|
|
2139
|
+
goal.updatedAt = new Date().toISOString();
|
|
2172
2140
|
retried = true;
|
|
2173
|
-
const runtime =
|
|
2141
|
+
const runtime = state.runtimes.find((r) => r.goalID === goalID);
|
|
2174
2142
|
if (runtime) {
|
|
2175
2143
|
runtime.consecutiveFailures = 0;
|
|
2176
2144
|
runtime.lastError = undefined;
|
|
@@ -2180,7 +2148,7 @@ function createGoalService(host) {
|
|
|
2180
2148
|
runtime.phase = "idle";
|
|
2181
2149
|
runtime.updatedAt = new Date().toISOString();
|
|
2182
2150
|
}
|
|
2183
|
-
return
|
|
2151
|
+
return state;
|
|
2184
2152
|
});
|
|
2185
2153
|
const goal = state.goals.find((g) => g.id === goalID);
|
|
2186
2154
|
if (!goal || !retried)
|
|
@@ -2541,16 +2509,22 @@ function createRealHost(client, directory) {
|
|
|
2541
2509
|
async sessionStatus(sessionID) {
|
|
2542
2510
|
try {
|
|
2543
2511
|
const result = await client.session.status({});
|
|
2512
|
+
if (result?.error)
|
|
2513
|
+
return "unknown";
|
|
2544
2514
|
const data = result?.data;
|
|
2545
|
-
if (!data || typeof data !== "object")
|
|
2515
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
2546
2516
|
return "unknown";
|
|
2547
2517
|
const status = data[sessionID];
|
|
2548
|
-
if (
|
|
2518
|
+
if (status === undefined || status === null)
|
|
2519
|
+
return "idle";
|
|
2520
|
+
if (typeof status !== "object" || Array.isArray(status))
|
|
2549
2521
|
return "unknown";
|
|
2550
2522
|
const type = status.type;
|
|
2551
2523
|
if (type === "busy" || type === "retry")
|
|
2552
2524
|
return type;
|
|
2553
|
-
|
|
2525
|
+
if (type === "idle")
|
|
2526
|
+
return "idle";
|
|
2527
|
+
return "unknown";
|
|
2554
2528
|
} catch {
|
|
2555
2529
|
return "unknown";
|
|
2556
2530
|
}
|
|
@@ -2861,9 +2835,9 @@ function goalTools(dir, goalService, hostSessionID, defaults = {}) {
|
|
|
2861
2835
|
const cwd = goal.config.checkCwd || goal.config.artifactDir || dir;
|
|
2862
2836
|
const checkResults = await runCompletionChecks(goal.config.checks, cwd);
|
|
2863
2837
|
if (!checkResults.passed) {
|
|
2864
|
-
const
|
|
2865
|
-
if (
|
|
2866
|
-
|
|
2838
|
+
const runtime = state.runtimes.find((r) => r.goalID === goal.id);
|
|
2839
|
+
if (runtime) {
|
|
2840
|
+
runtime.evaluatorRejectionCount = (runtime.evaluatorRejectionCount || 0) + 1;
|
|
2867
2841
|
const failureDetails = checkResults.failures.map((f) => {
|
|
2868
2842
|
const stdoutSnippet = f.stdout ? `
|
|
2869
2843
|
Stdout: ${f.stdout.slice(0, 500)}` : "";
|
|
@@ -2874,7 +2848,7 @@ Exit code: ${f.exitCode}${stdoutSnippet}${stderrSnippet}`;
|
|
|
2874
2848
|
}).join(`
|
|
2875
2849
|
|
|
2876
2850
|
`);
|
|
2877
|
-
|
|
2851
|
+
runtime.lastRejectionDetails = `Rejection #${runtime.evaluatorRejectionCount} at ${new Date().toISOString()}
|
|
2878
2852
|
|
|
2879
2853
|
Working directory: ${cwd}
|
|
2880
2854
|
|
|
@@ -2882,8 +2856,8 @@ ${failureDetails}`;
|
|
|
2882
2856
|
const attemptID = randomUUID5();
|
|
2883
2857
|
const verificationAttempt = {
|
|
2884
2858
|
id: attemptID,
|
|
2885
|
-
sequence:
|
|
2886
|
-
runGeneration:
|
|
2859
|
+
sequence: runtime.evaluatorRejectionCount,
|
|
2860
|
+
runGeneration: runtime.runGeneration,
|
|
2887
2861
|
claimedSummary: args.summary,
|
|
2888
2862
|
claimedEvidence: args.evidence,
|
|
2889
2863
|
startedAt: new Date().toISOString(),
|
|
@@ -2897,15 +2871,15 @@ ${failureDetails}`;
|
|
|
2897
2871
|
stdout: f.stdout
|
|
2898
2872
|
}))
|
|
2899
2873
|
};
|
|
2900
|
-
|
|
2901
|
-
|
|
2874
|
+
runtime.lastVerificationAttempt = verificationAttempt;
|
|
2875
|
+
runtime.recentVerificationAttempts = appendVerificationAttempt(runtime.recentVerificationAttempts || [], verificationAttempt);
|
|
2902
2876
|
const rejectEvent = {
|
|
2903
2877
|
version: 1,
|
|
2904
2878
|
eventID: randomUUID5(),
|
|
2905
2879
|
goalID: goal.id,
|
|
2906
2880
|
type: "goal.completion_rejected",
|
|
2907
2881
|
attemptID,
|
|
2908
|
-
rejectionCount:
|
|
2882
|
+
rejectionCount: runtime.evaluatorRejectionCount,
|
|
2909
2883
|
failedCheckCount: checkResults.failures.length,
|
|
2910
2884
|
failureSummary: failureDetails.slice(0, 500),
|
|
2911
2885
|
timestamp: new Date().toISOString(),
|
|
@@ -2913,16 +2887,16 @@ ${failureDetails}`;
|
|
|
2913
2887
|
};
|
|
2914
2888
|
await appendEvent(dir, rejectEvent);
|
|
2915
2889
|
const maxRejections = goal.config.maxEvaluatorRejections || 3;
|
|
2916
|
-
if (
|
|
2890
|
+
if (runtime.evaluatorRejectionCount >= maxRejections) {
|
|
2917
2891
|
goal.status = "blocked";
|
|
2918
2892
|
goal.updatedAt = new Date().toISOString();
|
|
2919
2893
|
goal.blocker = {
|
|
2920
|
-
reason: `Evaluator rejected ${
|
|
2894
|
+
reason: `Evaluator rejected ${runtime.evaluatorRejectionCount} time(s). Last failure:
|
|
2921
2895
|
${failureDetails.slice(0, 500)}`,
|
|
2922
2896
|
needed: "Fix the failing checks and retry the goal.",
|
|
2923
2897
|
at: new Date().toISOString()
|
|
2924
2898
|
};
|
|
2925
|
-
|
|
2899
|
+
runtime.forceFinishRequested = undefined;
|
|
2926
2900
|
await appendEvent(dir, {
|
|
2927
2901
|
version: 1,
|
|
2928
2902
|
eventID: randomUUID5(),
|
|
@@ -2934,10 +2908,10 @@ ${failureDetails.slice(0, 500)}`,
|
|
|
2934
2908
|
revision: state.revision
|
|
2935
2909
|
});
|
|
2936
2910
|
} else {
|
|
2937
|
-
|
|
2938
|
-
|
|
2911
|
+
runtime.forceFinishRequested = false;
|
|
2912
|
+
runtime.freeRetryPending = true;
|
|
2939
2913
|
}
|
|
2940
|
-
|
|
2914
|
+
runtime.updatedAt = new Date().toISOString();
|
|
2941
2915
|
await writeState(dir, state);
|
|
2942
2916
|
}
|
|
2943
2917
|
return {
|
|
@@ -2946,7 +2920,7 @@ ${failureDetails.slice(0, 500)}`,
|
|
|
2946
2920
|
passed: false,
|
|
2947
2921
|
failedChecks: checkResults.failures,
|
|
2948
2922
|
message: "Evaluator rejected completion. Fix the issues above and try again.",
|
|
2949
|
-
rejectionCount:
|
|
2923
|
+
rejectionCount: runtime?.evaluatorRejectionCount || 0,
|
|
2950
2924
|
status: goal.status
|
|
2951
2925
|
})
|
|
2952
2926
|
};
|
|
@@ -3637,8 +3611,8 @@ var server = async ({ client, directory }, pluginOptions) => {
|
|
|
3637
3611
|
"tool.execute.before": async (input, _output) => {
|
|
3638
3612
|
const activeWorkers = goalService.getActiveWorkers();
|
|
3639
3613
|
let matchedGoalID;
|
|
3640
|
-
for (const [goalID,
|
|
3641
|
-
if (
|
|
3614
|
+
for (const [goalID, worker] of activeWorkers) {
|
|
3615
|
+
if (worker.workerSessionID === input.sessionID) {
|
|
3642
3616
|
matchedGoalID = goalID;
|
|
3643
3617
|
break;
|
|
3644
3618
|
}
|
|
@@ -3661,8 +3635,8 @@ var server = async ({ client, directory }, pluginOptions) => {
|
|
|
3661
3635
|
}
|
|
3662
3636
|
const activeWorkers = goalService.getActiveWorkers();
|
|
3663
3637
|
let matchedGoalID;
|
|
3664
|
-
for (const [goalID,
|
|
3665
|
-
if (
|
|
3638
|
+
for (const [goalID, worker] of activeWorkers) {
|
|
3639
|
+
if (worker.workerSessionID === input.sessionID) {
|
|
3666
3640
|
matchedGoalID = goalID;
|
|
3667
3641
|
break;
|
|
3668
3642
|
}
|
|
@@ -3688,20 +3662,20 @@ var server = async ({ client, directory }, pluginOptions) => {
|
|
|
3688
3662
|
const goalID = parsed.goalID;
|
|
3689
3663
|
if (!goalID)
|
|
3690
3664
|
return;
|
|
3691
|
-
|
|
3665
|
+
await Promise.resolve();
|
|
3692
3666
|
const state = await readState(directory);
|
|
3693
3667
|
const goal = state.goals.find((g) => g.id === goalID);
|
|
3694
3668
|
if (!goal)
|
|
3695
3669
|
return;
|
|
3696
3670
|
const runtime = state.runtimes.find((r) => r.goalID === goalID);
|
|
3697
3671
|
const notifyType = parsed.status === "complete" ? "complete" : "blocked";
|
|
3698
|
-
if (runtime && !
|
|
3672
|
+
if (runtime && !shouldNotifyParent(runtime, notifyType))
|
|
3699
3673
|
return;
|
|
3700
3674
|
if (runtime) {
|
|
3701
3675
|
await mutateState(directory, `notify-parent:${goalID}`, async (s) => {
|
|
3702
3676
|
const rt = s.runtimes.find((r) => r.goalID === goalID);
|
|
3703
3677
|
if (rt)
|
|
3704
|
-
|
|
3678
|
+
markParentNotified(rt, notifyType);
|
|
3705
3679
|
return s;
|
|
3706
3680
|
});
|
|
3707
3681
|
}
|
package/dist/tui.js
CHANGED
|
@@ -574,10 +574,10 @@ function LoopDashboard(props) {
|
|
|
574
574
|
try {
|
|
575
575
|
const s = await client.getState();
|
|
576
576
|
setState(s);
|
|
577
|
-
const
|
|
578
|
-
if (
|
|
579
|
-
setSelected(
|
|
580
|
-
setSelectedGoal(
|
|
577
|
+
const goals = s.goals.filter((g) => showCompleted() || g.status !== "complete");
|
|
578
|
+
if (goals.length > 0 && selected() >= goals.length)
|
|
579
|
+
setSelected(goals.length - 1);
|
|
580
|
+
setSelectedGoal(goals[selected()] || null);
|
|
581
581
|
setEvents(await client.getEvents(20));
|
|
582
582
|
} catch (e) {
|
|
583
583
|
setStatusText(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -1077,7 +1077,7 @@ function LoopDashboard(props) {
|
|
|
1077
1077
|
children: (seg, idx) => {
|
|
1078
1078
|
const hasArrow = seg.includes("\u2192");
|
|
1079
1079
|
if (hasArrow) {
|
|
1080
|
-
const [
|
|
1080
|
+
const [k, d] = seg.split("\u2192").map((s) => s.trim());
|
|
1081
1081
|
return [_$memo(() => _$memo(() => idx() > 0)() && (() => {
|
|
1082
1082
|
var _el$54 = _$createElement("span");
|
|
1083
1083
|
_$insertNode(_el$54, _$createTextNode(` | `));
|
|
@@ -1087,7 +1087,7 @@ function LoopDashboard(props) {
|
|
|
1087
1087
|
return _el$54;
|
|
1088
1088
|
})()), (() => {
|
|
1089
1089
|
var _el$50 = _$createElement("span");
|
|
1090
|
-
_$insert(_el$50,
|
|
1090
|
+
_$insert(_el$50, k);
|
|
1091
1091
|
_$effect((_$p) => _$setProp(_el$50, "style", {
|
|
1092
1092
|
fg: theme().warning,
|
|
1093
1093
|
bold: true
|
|
@@ -1102,7 +1102,7 @@ function LoopDashboard(props) {
|
|
|
1102
1102
|
return _el$51;
|
|
1103
1103
|
})(), (() => {
|
|
1104
1104
|
var _el$53 = _$createElement("span");
|
|
1105
|
-
_$insert(_el$53,
|
|
1105
|
+
_$insert(_el$53, d);
|
|
1106
1106
|
_$effect((_$p) => _$setProp(_el$53, "style", {
|
|
1107
1107
|
fg: theme().text
|
|
1108
1108
|
}, _$p));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@bojackduy/opencode-loopd",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.3",
|
|
5
5
|
"description": "Codex-inspired background goal engine for OpenCode — autonomous subagents, engine-driven loop, child worker sessions and modal TUI dashboard. Like Claude Code loop for OpenCode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "AGPL-3.0-or-later",
|