oh-my-opencode 3.12.0 → 3.12.2
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/cli/index.js +203 -97
- package/dist/config/schema/background-task.d.ts +2 -2
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/config/schema/oh-my-opencode-config.d.ts +2 -2
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/background-agent/constants.d.ts +2 -2
- package/dist/features/background-agent/loop-detector.d.ts +4 -5
- package/dist/features/background-agent/manager.d.ts +1 -0
- package/dist/features/background-agent/session-status-classifier.d.ts +2 -0
- package/dist/features/background-agent/types.d.ts +4 -4
- package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/todo-description-override/description.d.ts +1 -0
- package/dist/hooks/todo-description-override/hook.d.ts +8 -0
- package/dist/hooks/todo-description-override/index.d.ts +1 -0
- package/dist/index.js +520 -323
- package/dist/oh-my-opencode.schema.json +4 -6
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/connected-providers-cache.d.ts +26 -29
- package/package.json +12 -12
package/dist/cli/index.js
CHANGED
|
@@ -4920,17 +4920,40 @@ var init_file_reference_resolver = () => {};
|
|
|
4920
4920
|
import * as fs from "fs";
|
|
4921
4921
|
import * as os from "os";
|
|
4922
4922
|
import * as path from "path";
|
|
4923
|
+
function flush() {
|
|
4924
|
+
if (buffer.length === 0)
|
|
4925
|
+
return;
|
|
4926
|
+
const data = buffer.join("");
|
|
4927
|
+
buffer = [];
|
|
4928
|
+
try {
|
|
4929
|
+
fs.appendFileSync(logFile, data);
|
|
4930
|
+
} catch {}
|
|
4931
|
+
}
|
|
4932
|
+
function scheduleFlush() {
|
|
4933
|
+
if (flushTimer)
|
|
4934
|
+
return;
|
|
4935
|
+
flushTimer = setTimeout(() => {
|
|
4936
|
+
flushTimer = null;
|
|
4937
|
+
flush();
|
|
4938
|
+
}, FLUSH_INTERVAL_MS);
|
|
4939
|
+
}
|
|
4923
4940
|
function log(message, data) {
|
|
4924
4941
|
try {
|
|
4925
4942
|
const timestamp2 = new Date().toISOString();
|
|
4926
4943
|
const logEntry = `[${timestamp2}] ${message} ${data ? JSON.stringify(data) : ""}
|
|
4927
4944
|
`;
|
|
4928
|
-
|
|
4945
|
+
buffer.push(logEntry);
|
|
4946
|
+
if (buffer.length >= BUFFER_SIZE_LIMIT) {
|
|
4947
|
+
flush();
|
|
4948
|
+
} else {
|
|
4949
|
+
scheduleFlush();
|
|
4950
|
+
}
|
|
4929
4951
|
} catch {}
|
|
4930
4952
|
}
|
|
4931
|
-
var logFile;
|
|
4953
|
+
var logFile, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50;
|
|
4932
4954
|
var init_logger = __esm(() => {
|
|
4933
4955
|
logFile = path.join(os.tmpdir(), "oh-my-opencode.log");
|
|
4956
|
+
buffer = [];
|
|
4934
4957
|
});
|
|
4935
4958
|
|
|
4936
4959
|
// src/shared/deep-merge.ts
|
|
@@ -4974,6 +4997,12 @@ var init_snake_case = __esm(() => {
|
|
|
4974
4997
|
|
|
4975
4998
|
// src/shared/tool-name.ts
|
|
4976
4999
|
var init_tool_name = () => {};
|
|
5000
|
+
|
|
5001
|
+
// src/shared/pattern-matcher.ts
|
|
5002
|
+
var regexCache;
|
|
5003
|
+
var init_pattern_matcher = __esm(() => {
|
|
5004
|
+
regexCache = new Map;
|
|
5005
|
+
});
|
|
4977
5006
|
// src/shared/file-utils.ts
|
|
4978
5007
|
var init_file_utils = () => {};
|
|
4979
5008
|
|
|
@@ -6619,85 +6648,160 @@ var init_agent_tool_restrictions = () => {};
|
|
|
6619
6648
|
// src/shared/connected-providers-cache.ts
|
|
6620
6649
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
6621
6650
|
import { join as join4 } from "path";
|
|
6622
|
-
function
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6651
|
+
function createConnectedProvidersCacheStore(getCacheDir2 = getOmoOpenCodeCacheDir) {
|
|
6652
|
+
function getCacheFilePath(filename) {
|
|
6653
|
+
return join4(getCacheDir2(), filename);
|
|
6654
|
+
}
|
|
6655
|
+
let memConnected;
|
|
6656
|
+
let memProviderModels;
|
|
6657
|
+
function ensureCacheDir() {
|
|
6658
|
+
const cacheDir = getCacheDir2();
|
|
6659
|
+
if (!existsSync3(cacheDir)) {
|
|
6660
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
function readConnectedProvidersCache() {
|
|
6664
|
+
if (memConnected !== undefined)
|
|
6665
|
+
return memConnected;
|
|
6666
|
+
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
6667
|
+
if (!existsSync3(cacheFile)) {
|
|
6668
|
+
log("[connected-providers-cache] Cache file not found", { cacheFile });
|
|
6669
|
+
memConnected = null;
|
|
6670
|
+
return null;
|
|
6671
|
+
}
|
|
6672
|
+
try {
|
|
6673
|
+
const content = readFileSync2(cacheFile, "utf-8");
|
|
6674
|
+
const data = JSON.parse(content);
|
|
6675
|
+
log("[connected-providers-cache] Read cache", { count: data.connected.length, updatedAt: data.updatedAt });
|
|
6676
|
+
memConnected = data.connected;
|
|
6677
|
+
return data.connected;
|
|
6678
|
+
} catch (err) {
|
|
6679
|
+
log("[connected-providers-cache] Error reading cache", { error: String(err) });
|
|
6680
|
+
memConnected = null;
|
|
6681
|
+
return null;
|
|
6682
|
+
}
|
|
6629
6683
|
}
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
6634
|
-
const data = {
|
|
6635
|
-
connected,
|
|
6636
|
-
updatedAt: new Date().toISOString()
|
|
6637
|
-
};
|
|
6638
|
-
try {
|
|
6639
|
-
writeFileSync2(cacheFile, JSON.stringify(data, null, 2));
|
|
6640
|
-
log("[connected-providers-cache] Cache written", { count: connected.length });
|
|
6641
|
-
} catch (err) {
|
|
6642
|
-
log("[connected-providers-cache] Error writing cache", { error: String(err) });
|
|
6684
|
+
function hasConnectedProvidersCache() {
|
|
6685
|
+
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
6686
|
+
return existsSync3(cacheFile);
|
|
6643
6687
|
}
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
log("[connected-providers-cache] Provider-models cache written", {
|
|
6659
|
-
providerCount: Object.keys(data.models).length
|
|
6660
|
-
});
|
|
6661
|
-
} catch (err) {
|
|
6662
|
-
log("[connected-providers-cache] Error writing provider-models cache", { error: String(err) });
|
|
6688
|
+
function writeConnectedProvidersCache(connected) {
|
|
6689
|
+
ensureCacheDir();
|
|
6690
|
+
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
6691
|
+
const data = {
|
|
6692
|
+
connected,
|
|
6693
|
+
updatedAt: new Date().toISOString()
|
|
6694
|
+
};
|
|
6695
|
+
try {
|
|
6696
|
+
writeFileSync2(cacheFile, JSON.stringify(data, null, 2));
|
|
6697
|
+
memConnected = connected;
|
|
6698
|
+
log("[connected-providers-cache] Cache written", { count: connected.length });
|
|
6699
|
+
} catch (err) {
|
|
6700
|
+
log("[connected-providers-cache] Error writing cache", { error: String(err) });
|
|
6701
|
+
}
|
|
6663
6702
|
}
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6703
|
+
function readProviderModelsCache() {
|
|
6704
|
+
if (memProviderModels !== undefined)
|
|
6705
|
+
return memProviderModels;
|
|
6706
|
+
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE);
|
|
6707
|
+
if (!existsSync3(cacheFile)) {
|
|
6708
|
+
log("[connected-providers-cache] Provider-models cache file not found", { cacheFile });
|
|
6709
|
+
memProviderModels = null;
|
|
6710
|
+
return null;
|
|
6711
|
+
}
|
|
6712
|
+
try {
|
|
6713
|
+
const content = readFileSync2(cacheFile, "utf-8");
|
|
6714
|
+
const data = JSON.parse(content);
|
|
6715
|
+
log("[connected-providers-cache] Read provider-models cache", {
|
|
6716
|
+
providerCount: Object.keys(data.models).length,
|
|
6717
|
+
updatedAt: data.updatedAt
|
|
6718
|
+
});
|
|
6719
|
+
memProviderModels = data;
|
|
6720
|
+
return data;
|
|
6721
|
+
} catch (err) {
|
|
6722
|
+
log("[connected-providers-cache] Error reading provider-models cache", { error: String(err) });
|
|
6723
|
+
memProviderModels = null;
|
|
6724
|
+
return null;
|
|
6725
|
+
}
|
|
6669
6726
|
}
|
|
6670
|
-
|
|
6671
|
-
const
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
const
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
models: modelsByProvider,
|
|
6691
|
-
connected
|
|
6692
|
-
});
|
|
6693
|
-
} catch (err) {
|
|
6694
|
-
log("[connected-providers-cache] Error updating cache", { error: String(err) });
|
|
6727
|
+
function hasProviderModelsCache() {
|
|
6728
|
+
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE);
|
|
6729
|
+
return existsSync3(cacheFile);
|
|
6730
|
+
}
|
|
6731
|
+
function writeProviderModelsCache(data) {
|
|
6732
|
+
ensureCacheDir();
|
|
6733
|
+
const cacheFile = getCacheFilePath(PROVIDER_MODELS_CACHE_FILE);
|
|
6734
|
+
const cacheData = {
|
|
6735
|
+
...data,
|
|
6736
|
+
updatedAt: new Date().toISOString()
|
|
6737
|
+
};
|
|
6738
|
+
try {
|
|
6739
|
+
writeFileSync2(cacheFile, JSON.stringify(cacheData, null, 2));
|
|
6740
|
+
memProviderModels = cacheData;
|
|
6741
|
+
log("[connected-providers-cache] Provider-models cache written", {
|
|
6742
|
+
providerCount: Object.keys(data.models).length
|
|
6743
|
+
});
|
|
6744
|
+
} catch (err) {
|
|
6745
|
+
log("[connected-providers-cache] Error writing provider-models cache", { error: String(err) });
|
|
6746
|
+
}
|
|
6695
6747
|
}
|
|
6748
|
+
async function updateConnectedProvidersCache(client) {
|
|
6749
|
+
if (!client?.provider?.list) {
|
|
6750
|
+
log("[connected-providers-cache] client.provider.list not available");
|
|
6751
|
+
return;
|
|
6752
|
+
}
|
|
6753
|
+
try {
|
|
6754
|
+
const result = await client.provider.list();
|
|
6755
|
+
const connected = result.data?.connected ?? [];
|
|
6756
|
+
log("[connected-providers-cache] Fetched connected providers", {
|
|
6757
|
+
count: connected.length,
|
|
6758
|
+
providers: connected
|
|
6759
|
+
});
|
|
6760
|
+
writeConnectedProvidersCache(connected);
|
|
6761
|
+
const modelsByProvider = {};
|
|
6762
|
+
const allProviders = result.data?.all ?? [];
|
|
6763
|
+
for (const provider of allProviders) {
|
|
6764
|
+
if (provider.models) {
|
|
6765
|
+
const modelIds = Object.keys(provider.models);
|
|
6766
|
+
if (modelIds.length > 0) {
|
|
6767
|
+
modelsByProvider[provider.id] = modelIds;
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6771
|
+
log("[connected-providers-cache] Extracted models from provider list", {
|
|
6772
|
+
providerCount: Object.keys(modelsByProvider).length,
|
|
6773
|
+
totalModels: Object.values(modelsByProvider).reduce((sum, ids) => sum + ids.length, 0)
|
|
6774
|
+
});
|
|
6775
|
+
writeProviderModelsCache({
|
|
6776
|
+
models: modelsByProvider,
|
|
6777
|
+
connected
|
|
6778
|
+
});
|
|
6779
|
+
} catch (err) {
|
|
6780
|
+
log("[connected-providers-cache] Error updating cache", { error: String(err) });
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
return {
|
|
6784
|
+
readConnectedProvidersCache,
|
|
6785
|
+
hasConnectedProvidersCache,
|
|
6786
|
+
readProviderModelsCache,
|
|
6787
|
+
hasProviderModelsCache,
|
|
6788
|
+
writeProviderModelsCache,
|
|
6789
|
+
updateConnectedProvidersCache
|
|
6790
|
+
};
|
|
6696
6791
|
}
|
|
6697
|
-
var CONNECTED_PROVIDERS_CACHE_FILE = "connected-providers.json", PROVIDER_MODELS_CACHE_FILE = "provider-models.json";
|
|
6792
|
+
var CONNECTED_PROVIDERS_CACHE_FILE = "connected-providers.json", PROVIDER_MODELS_CACHE_FILE = "provider-models.json", defaultConnectedProvidersCacheStore, readConnectedProvidersCache, hasConnectedProvidersCache, readProviderModelsCache, hasProviderModelsCache, writeProviderModelsCache, updateConnectedProvidersCache;
|
|
6698
6793
|
var init_connected_providers_cache = __esm(() => {
|
|
6699
6794
|
init_logger();
|
|
6700
6795
|
init_data_path();
|
|
6796
|
+
defaultConnectedProvidersCacheStore = createConnectedProvidersCacheStore(() => getOmoOpenCodeCacheDir());
|
|
6797
|
+
({
|
|
6798
|
+
readConnectedProvidersCache,
|
|
6799
|
+
hasConnectedProvidersCache,
|
|
6800
|
+
readProviderModelsCache,
|
|
6801
|
+
hasProviderModelsCache,
|
|
6802
|
+
writeProviderModelsCache,
|
|
6803
|
+
updateConnectedProvidersCache
|
|
6804
|
+
} = defaultConnectedProvidersCacheStore);
|
|
6701
6805
|
});
|
|
6702
6806
|
|
|
6703
6807
|
// src/shared/model-availability.ts
|
|
@@ -7066,6 +7170,7 @@ var init_shared = __esm(() => {
|
|
|
7066
7170
|
init_logger();
|
|
7067
7171
|
init_snake_case();
|
|
7068
7172
|
init_tool_name();
|
|
7173
|
+
init_pattern_matcher();
|
|
7069
7174
|
init_deep_merge();
|
|
7070
7175
|
init_file_utils();
|
|
7071
7176
|
init_dynamic_truncator();
|
|
@@ -8836,7 +8941,7 @@ var {
|
|
|
8836
8941
|
// package.json
|
|
8837
8942
|
var package_default = {
|
|
8838
8943
|
name: "oh-my-opencode",
|
|
8839
|
-
version: "3.12.
|
|
8944
|
+
version: "3.12.2",
|
|
8840
8945
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
8841
8946
|
main: "dist/index.js",
|
|
8842
8947
|
types: "dist/index.d.ts",
|
|
@@ -8912,17 +9017,17 @@ var package_default = {
|
|
|
8912
9017
|
typescript: "^5.7.3"
|
|
8913
9018
|
},
|
|
8914
9019
|
optionalDependencies: {
|
|
8915
|
-
"oh-my-opencode-darwin-arm64": "3.12.
|
|
8916
|
-
"oh-my-opencode-darwin-x64": "3.12.
|
|
8917
|
-
"oh-my-opencode-darwin-x64-baseline": "3.12.
|
|
8918
|
-
"oh-my-opencode-linux-arm64": "3.12.
|
|
8919
|
-
"oh-my-opencode-linux-arm64-musl": "3.12.
|
|
8920
|
-
"oh-my-opencode-linux-x64": "3.12.
|
|
8921
|
-
"oh-my-opencode-linux-x64-baseline": "3.12.
|
|
8922
|
-
"oh-my-opencode-linux-x64-musl": "3.12.
|
|
8923
|
-
"oh-my-opencode-linux-x64-musl-baseline": "3.12.
|
|
8924
|
-
"oh-my-opencode-windows-x64": "3.12.
|
|
8925
|
-
"oh-my-opencode-windows-x64-baseline": "3.12.
|
|
9020
|
+
"oh-my-opencode-darwin-arm64": "3.12.2",
|
|
9021
|
+
"oh-my-opencode-darwin-x64": "3.12.2",
|
|
9022
|
+
"oh-my-opencode-darwin-x64-baseline": "3.12.2",
|
|
9023
|
+
"oh-my-opencode-linux-arm64": "3.12.2",
|
|
9024
|
+
"oh-my-opencode-linux-arm64-musl": "3.12.2",
|
|
9025
|
+
"oh-my-opencode-linux-x64": "3.12.2",
|
|
9026
|
+
"oh-my-opencode-linux-x64-baseline": "3.12.2",
|
|
9027
|
+
"oh-my-opencode-linux-x64-musl": "3.12.2",
|
|
9028
|
+
"oh-my-opencode-linux-x64-musl-baseline": "3.12.2",
|
|
9029
|
+
"oh-my-opencode-windows-x64": "3.12.2",
|
|
9030
|
+
"oh-my-opencode-windows-x64-baseline": "3.12.2"
|
|
8926
9031
|
},
|
|
8927
9032
|
overrides: {
|
|
8928
9033
|
"@opencode-ai/sdk": "^1.2.24"
|
|
@@ -10291,24 +10396,24 @@ function writePaddedText(text, atLineStart) {
|
|
|
10291
10396
|
return { output: text, atLineStart: text.endsWith(`
|
|
10292
10397
|
`) };
|
|
10293
10398
|
}
|
|
10294
|
-
|
|
10399
|
+
const parts = [];
|
|
10295
10400
|
let lineStart = atLineStart;
|
|
10296
10401
|
for (let i2 = 0;i2 < text.length; i2++) {
|
|
10297
10402
|
const ch = text[i2];
|
|
10298
10403
|
if (lineStart) {
|
|
10299
|
-
|
|
10404
|
+
parts.push(" ");
|
|
10300
10405
|
lineStart = false;
|
|
10301
10406
|
}
|
|
10302
10407
|
if (ch === `
|
|
10303
10408
|
`) {
|
|
10304
|
-
|
|
10305
|
-
|
|
10409
|
+
parts.push(`
|
|
10410
|
+
`);
|
|
10306
10411
|
lineStart = true;
|
|
10307
10412
|
continue;
|
|
10308
10413
|
}
|
|
10309
|
-
|
|
10414
|
+
parts.push(ch);
|
|
10310
10415
|
}
|
|
10311
|
-
return { output, atLineStart: lineStart };
|
|
10416
|
+
return { output: parts.join(""), atLineStart: lineStart };
|
|
10312
10417
|
}
|
|
10313
10418
|
function colorizeWithProfileColor(text, hexColor) {
|
|
10314
10419
|
if (!hexColor)
|
|
@@ -24301,9 +24406,9 @@ var BabysittingConfigSchema = exports_external.object({
|
|
|
24301
24406
|
});
|
|
24302
24407
|
// src/config/schema/background-task.ts
|
|
24303
24408
|
var CircuitBreakerConfigSchema = exports_external.object({
|
|
24409
|
+
enabled: exports_external.boolean().optional(),
|
|
24304
24410
|
maxToolCalls: exports_external.number().int().min(10).optional(),
|
|
24305
|
-
|
|
24306
|
-
repetitionThresholdPercent: exports_external.number().gt(0).max(100).optional()
|
|
24411
|
+
consecutiveThreshold: exports_external.number().int().min(5).optional()
|
|
24307
24412
|
});
|
|
24308
24413
|
var BackgroundTaskConfigSchema = exports_external.object({
|
|
24309
24414
|
defaultConcurrency: exports_external.number().min(1).optional(),
|
|
@@ -24493,7 +24598,8 @@ var HookNameSchema = exports_external.enum([
|
|
|
24493
24598
|
"write-existing-file-guard",
|
|
24494
24599
|
"anthropic-effort",
|
|
24495
24600
|
"hashline-read-enhancer",
|
|
24496
|
-
"read-image-resizer"
|
|
24601
|
+
"read-image-resizer",
|
|
24602
|
+
"todo-description-override"
|
|
24497
24603
|
]);
|
|
24498
24604
|
// src/config/schema/notification.ts
|
|
24499
24605
|
var NotificationConfigSchema = exports_external.object({
|
|
@@ -24800,7 +24906,7 @@ var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseVa
|
|
|
24800
24906
|
if (!response.body)
|
|
24801
24907
|
throw new Error("No body in SSE response");
|
|
24802
24908
|
const reader = response.body.pipeThrough(new TextDecoderStream).getReader();
|
|
24803
|
-
let
|
|
24909
|
+
let buffer2 = "";
|
|
24804
24910
|
const abortHandler = () => {
|
|
24805
24911
|
try {
|
|
24806
24912
|
reader.cancel();
|
|
@@ -24812,11 +24918,11 @@ var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseVa
|
|
|
24812
24918
|
const { done, value } = await reader.read();
|
|
24813
24919
|
if (done)
|
|
24814
24920
|
break;
|
|
24815
|
-
|
|
24816
|
-
const chunks =
|
|
24921
|
+
buffer2 += value;
|
|
24922
|
+
const chunks = buffer2.split(`
|
|
24817
24923
|
|
|
24818
24924
|
`);
|
|
24819
|
-
|
|
24925
|
+
buffer2 = chunks.pop() ?? "";
|
|
24820
24926
|
for (const chunk of chunks) {
|
|
24821
24927
|
const lines = chunk.split(`
|
|
24822
24928
|
`);
|
|
@@ -10,9 +10,9 @@ export declare const BackgroundTaskConfigSchema: z.ZodObject<{
|
|
|
10
10
|
syncPollTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
11
11
|
maxToolCalls: z.ZodOptional<z.ZodNumber>;
|
|
12
12
|
circuitBreaker: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
13
14
|
maxToolCalls: z.ZodOptional<z.ZodNumber>;
|
|
14
|
-
|
|
15
|
-
repetitionThresholdPercent: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
consecutiveThreshold: z.ZodOptional<z.ZodNumber>;
|
|
16
16
|
}, z.core.$strip>>;
|
|
17
17
|
}, z.core.$strip>;
|
|
18
18
|
export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>;
|
|
@@ -48,5 +48,6 @@ export declare const HookNameSchema: z.ZodEnum<{
|
|
|
48
48
|
"anthropic-effort": "anthropic-effort";
|
|
49
49
|
"hashline-read-enhancer": "hashline-read-enhancer";
|
|
50
50
|
"read-image-resizer": "read-image-resizer";
|
|
51
|
+
"todo-description-override": "todo-description-override";
|
|
51
52
|
}>;
|
|
52
53
|
export type HookName = z.infer<typeof HookNameSchema>;
|
|
@@ -1343,9 +1343,9 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
|
|
|
1343
1343
|
syncPollTimeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
1344
1344
|
maxToolCalls: z.ZodOptional<z.ZodNumber>;
|
|
1345
1345
|
circuitBreaker: z.ZodOptional<z.ZodObject<{
|
|
1346
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
1346
1347
|
maxToolCalls: z.ZodOptional<z.ZodNumber>;
|
|
1347
|
-
|
|
1348
|
-
repetitionThresholdPercent: z.ZodOptional<z.ZodNumber>;
|
|
1348
|
+
consecutiveThreshold: z.ZodOptional<z.ZodNumber>;
|
|
1349
1349
|
}, z.core.$strip>>;
|
|
1350
1350
|
}, z.core.$strip>>;
|
|
1351
1351
|
notification: z.ZodOptional<z.ZodObject<{
|
package/dist/create-hooks.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export declare function createHooks(args: {
|
|
|
50
50
|
hashlineReadEnhancer: ReturnType<typeof import("./hooks").createHashlineReadEnhancerHook> | null;
|
|
51
51
|
jsonErrorRecovery: ReturnType<typeof import("./hooks").createJsonErrorRecoveryHook> | null;
|
|
52
52
|
readImageResizer: ReturnType<typeof import("./hooks").createReadImageResizerHook> | null;
|
|
53
|
+
todoDescriptionOverride: ReturnType<typeof import("./hooks").createTodoDescriptionOverrideHook> | null;
|
|
53
54
|
contextWindowMonitor: ReturnType<typeof import("./hooks").createContextWindowMonitorHook> | null;
|
|
54
55
|
preemptiveCompaction: ReturnType<typeof import("./hooks").createPreemptiveCompactionHook> | null;
|
|
55
56
|
sessionRecovery: ReturnType<typeof import("./hooks").createSessionRecoveryHook> | null;
|
|
@@ -6,8 +6,8 @@ export declare const MIN_STABILITY_TIME_MS: number;
|
|
|
6
6
|
export declare const DEFAULT_STALE_TIMEOUT_MS = 1200000;
|
|
7
7
|
export declare const DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 1800000;
|
|
8
8
|
export declare const DEFAULT_MAX_TOOL_CALLS = 200;
|
|
9
|
-
export declare const
|
|
10
|
-
export declare const
|
|
9
|
+
export declare const DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
|
|
10
|
+
export declare const DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
|
|
11
11
|
export declare const MIN_RUNTIME_BEFORE_STALE_MS = 30000;
|
|
12
12
|
export declare const MIN_IDLE_TIME_MS = 5000;
|
|
13
13
|
export declare const POLLING_INTERVAL_MS = 3000;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { BackgroundTaskConfig } from "../../config/schema";
|
|
2
2
|
import type { ToolCallWindow } from "./types";
|
|
3
3
|
export interface CircuitBreakerSettings {
|
|
4
|
+
enabled: boolean;
|
|
4
5
|
maxToolCalls: number;
|
|
5
|
-
|
|
6
|
-
repetitionThresholdPercent: number;
|
|
6
|
+
consecutiveThreshold: number;
|
|
7
7
|
}
|
|
8
8
|
export interface ToolLoopDetectionResult {
|
|
9
9
|
triggered: boolean;
|
|
10
10
|
toolName?: string;
|
|
11
11
|
repeatedCount?: number;
|
|
12
|
-
sampleSize?: number;
|
|
13
|
-
thresholdPercent?: number;
|
|
14
12
|
}
|
|
15
13
|
export declare function resolveCircuitBreakerSettings(config?: BackgroundTaskConfig): CircuitBreakerSettings;
|
|
16
|
-
export declare function recordToolCall(window: ToolCallWindow | undefined, toolName: string, settings: CircuitBreakerSettings): ToolCallWindow;
|
|
14
|
+
export declare function recordToolCall(window: ToolCallWindow | undefined, toolName: string, settings: CircuitBreakerSettings, toolInput?: Record<string, unknown> | null): ToolCallWindow;
|
|
15
|
+
export declare function createToolCallSignature(toolName: string, toolInput?: Record<string, unknown> | null): string;
|
|
17
16
|
export declare function detectRepetitiveToolUse(window: ToolCallWindow | undefined): ToolLoopDetectionResult;
|
|
@@ -45,6 +45,7 @@ export declare class BackgroundManager {
|
|
|
45
45
|
private preStartDescendantReservations;
|
|
46
46
|
private enableParentSessionNotifications;
|
|
47
47
|
readonly taskHistory: TaskHistory;
|
|
48
|
+
private cachedCircuitBreakerSettings?;
|
|
48
49
|
constructor(ctx: PluginInput, config?: BackgroundTaskConfig, options?: {
|
|
49
50
|
tmuxConfig?: TmuxConfig;
|
|
50
51
|
onSubagentSessionCreated?: OnSubagentSessionCreated;
|
|
@@ -2,15 +2,15 @@ import type { FallbackEntry } from "../../shared/model-requirements";
|
|
|
2
2
|
import type { SessionPermissionRule } from "../../shared/question-denied-session-permission";
|
|
3
3
|
export type BackgroundTaskStatus = "pending" | "running" | "completed" | "error" | "cancelled" | "interrupt";
|
|
4
4
|
export interface ToolCallWindow {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
lastSignature: string;
|
|
6
|
+
consecutiveCount: number;
|
|
7
|
+
threshold: number;
|
|
8
8
|
}
|
|
9
9
|
export interface TaskProgress {
|
|
10
10
|
toolCalls: number;
|
|
11
11
|
lastTool?: string;
|
|
12
12
|
toolCallWindow?: ToolCallWindow;
|
|
13
|
-
countedToolPartIDs?: string
|
|
13
|
+
countedToolPartIDs?: Set<string>;
|
|
14
14
|
lastUpdate: Date;
|
|
15
15
|
lastMessage?: string;
|
|
16
16
|
lastMessageAt?: Date;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const START_WORK_TEMPLATE = "You are starting a Sisyphus work session.\n\n## ARGUMENTS\n\n- `/start-work [plan-name] [--worktree <path>]`\n - `plan-name` (optional): name or partial match of the plan to start\n - `--worktree <path>` (optional): absolute path to an existing git worktree to work in\n - If specified and valid: hook pre-sets worktree_path in boulder.json\n - If specified but invalid: you must run `git worktree add <path> <branch>` first\n - If omitted:
|
|
1
|
+
export declare const START_WORK_TEMPLATE = "You are starting a Sisyphus work session.\n\n## ARGUMENTS\n\n- `/start-work [plan-name] [--worktree <path>]`\n - `plan-name` (optional): name or partial match of the plan to start\n - `--worktree <path>` (optional): absolute path to an existing git worktree to work in\n - If specified and valid: hook pre-sets worktree_path in boulder.json\n - If specified but invalid: you must run `git worktree add <path> <branch>` first\n - If omitted: work directly in the current project directory (no worktree)\n\n## WHAT TO DO\n\n1. **Find available plans**: Search for Prometheus-generated plan files at `.sisyphus/plans/`\n\n2. **Check for active boulder state**: Read `.sisyphus/boulder.json` if it exists\n\n3. **Decision logic**:\n - If `.sisyphus/boulder.json` exists AND plan is NOT complete (has unchecked boxes):\n - **APPEND** current session to session_ids\n - Continue work on existing plan\n - If no active plan OR plan is complete:\n - List available plan files\n - If ONE plan: auto-select it\n - If MULTIPLE plans: show list with timestamps, ask user to select\n\n4. **Worktree Setup** (ONLY when `--worktree` was explicitly specified and `worktree_path` not already set in boulder.json):\n 1. `git worktree list --porcelain` \u2014 see available worktrees\n 2. Create: `git worktree add <absolute-path> <branch-or-HEAD>`\n 3. Update boulder.json to add `\"worktree_path\": \"<absolute-path>\"`\n 4. All work happens inside that worktree directory\n\n5. **Create/Update boulder.json**:\n ```json\n {\n \"active_plan\": \"/absolute/path/to/plan.md\",\n \"started_at\": \"ISO_TIMESTAMP\",\n \"session_ids\": [\"session_id_1\", \"session_id_2\"],\n \"plan_name\": \"plan-name\",\n \"worktree_path\": \"/absolute/path/to/git/worktree\"\n }\n ```\n\n6. **Read the plan file** and start executing tasks according to atlas workflow\n\n## OUTPUT FORMAT\n\nWhen listing plans for selection:\n```\nAvailable Work Plans\n\nCurrent Time: {ISO timestamp}\nSession ID: {current session id}\n\n1. [plan-name-1.md] - Modified: {date} - Progress: 3/10 tasks\n2. [plan-name-2.md] - Modified: {date} - Progress: 0/5 tasks\n\nWhich plan would you like to work on? (Enter number or plan name)\n```\n\nWhen resuming existing work:\n```\nResuming Work Session\n\nActive Plan: {plan-name}\nProgress: {completed}/{total} tasks\nSessions: {count} (appending current session)\nWorktree: {worktree_path}\n\nReading plan and continuing from last incomplete task...\n```\n\nWhen auto-selecting single plan:\n```\nStarting Work Session\n\nPlan: {plan-name}\nSession ID: {session_id}\nStarted: {timestamp}\nWorktree: {worktree_path}\n\nReading plan and beginning execution...\n```\n\n## CRITICAL\n\n- The session_id is injected by the hook - use it directly\n- Always update boulder.json BEFORE starting work\n- If worktree_path is set in boulder.json, all work happens inside that worktree directory\n- Read the FULL plan file before delegating any tasks\n- Follow atlas delegation protocols (7-section format)\n\n## TASK BREAKDOWN (MANDATORY)\n\nAfter reading the plan file, you MUST decompose every plan task into granular, implementation-level sub-steps and register ALL of them as task/todo items BEFORE starting any work.\n\n**How to break down**:\n- Each plan checkbox item (e.g., `- [ ] Add user authentication`) must be split into concrete, actionable sub-tasks\n- Sub-tasks should be specific enough that each one touches a clear set of files/functions\n- Include: file to modify, what to change, expected behavior, and how to verify\n- Do NOT leave any task vague \u2014 \"implement feature X\" is NOT acceptable; \"add validateToken() to src/auth/middleware.ts that checks JWT expiry and returns 401\" IS acceptable\n\n**Example breakdown**:\nPlan task: `- [ ] Add rate limiting to API`\n\u2192 Todo items:\n 1. Create `src/middleware/rate-limiter.ts` with sliding window algorithm (max 100 req/min per IP)\n 2. Add RateLimiter middleware to `src/app.ts` router chain, before auth middleware\n 3. Add rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining) to response in `rate-limiter.ts`\n 4. Add test: verify 429 response after exceeding limit in `src/middleware/rate-limiter.test.ts`\n 5. Add test: verify headers are present on normal responses\n\nRegister these as task/todo items so progress is tracked and visible throughout the session.\n\n## WORKTREE COMPLETION\n\nWhen working in a worktree (`worktree_path` is set in boulder.json) and ALL plan tasks are complete:\n1. Commit all remaining changes in the worktree\n2. Switch to the main working directory (the original repo, NOT the worktree)\n3. Merge the worktree branch into the current branch: `git merge <worktree-branch>`\n4. If merge succeeds, clean up: `git worktree remove <worktree-path>`\n5. Remove the boulder.json state\n\nThis is the DEFAULT behavior when `--worktree` was used. Skip merge only if the user explicitly instructs otherwise (e.g., asks to create a PR instead).";
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -48,3 +48,4 @@ export { createWriteExistingFileGuardHook } from "./write-existing-file-guard";
|
|
|
48
48
|
export { createHashlineReadEnhancerHook } from "./hashline-read-enhancer";
|
|
49
49
|
export { createJsonErrorRecoveryHook, JSON_ERROR_TOOL_EXCLUDE_LIST, JSON_ERROR_PATTERNS, JSON_ERROR_REMINDER } from "./json-error-recovery";
|
|
50
50
|
export { createReadImageResizerHook } from "./read-image-resizer";
|
|
51
|
+
export { createTodoDescriptionOverrideHook } from "./todo-description-override";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TODOWRITE_DESCRIPTION = "Use this tool to create and manage a structured task list for tracking progress on multi-step work.\n\n## Todo Format (MANDATORY)\n\nEach todo title MUST encode four elements: WHERE, WHY, HOW, and EXPECTED RESULT.\n\nFormat: \"[WHERE] [HOW] to [WHY] \u2014 expect [RESULT]\"\n\nGOOD:\n- \"src/utils/validation.ts: Add validateEmail() for input sanitization \u2014 returns boolean\"\n- \"UserService.create(): Call validateEmail() before DB insert \u2014 rejects invalid emails with 400\"\n- \"validation.test.ts: Add test for missing @ sign \u2014 expect validateEmail('foo') to return false\"\n\nBAD:\n- \"Implement email validation\" (where? how? what result?)\n- \"Add dark mode\" (this is a feature, not a todo)\n- \"Fix auth\" (what file? what changes? what's expected?)\n\n## Granularity Rules\n\nEach todo MUST be a single atomic action completable in 1-3 tool calls. If it needs more, split it.\n\n**Size test**: Can you complete this todo by editing one file or running one command? If not, it's too big.\n\n## Task Management\n- One in_progress at a time. Complete it before starting the next.\n- Mark completed immediately after finishing each item.\n- Skip this tool for single trivial tasks (one-step, obvious action).";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createTodoDescriptionOverrideHook } from "./hook";
|