@mestreyoda/fabrica 0.2.21 → 0.2.23
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/ARCHITECTURE.md +137 -0
- package/README.md +1 -0
- package/defaults/fabrica/prompts/developer.md +22 -6
- package/defaults/fabrica/prompts/reviewer.md +15 -0
- package/defaults/fabrica/prompts/tester.md +9 -0
- package/dist/index.js +1614 -677
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -26948,14 +26948,14 @@ var require_tls_helpers = __commonJS({
|
|
|
26948
26948
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
26949
26949
|
exports2.CIPHER_SUITES = void 0;
|
|
26950
26950
|
exports2.getDefaultRootsData = getDefaultRootsData;
|
|
26951
|
-
var
|
|
26951
|
+
var fs46 = __require("fs");
|
|
26952
26952
|
exports2.CIPHER_SUITES = process.env.GRPC_SSL_CIPHER_SUITES;
|
|
26953
26953
|
var DEFAULT_ROOTS_FILE_PATH = process.env.GRPC_DEFAULT_SSL_ROOTS_FILE_PATH;
|
|
26954
26954
|
var defaultRootsData = null;
|
|
26955
26955
|
function getDefaultRootsData() {
|
|
26956
26956
|
if (DEFAULT_ROOTS_FILE_PATH) {
|
|
26957
26957
|
if (defaultRootsData === null) {
|
|
26958
|
-
defaultRootsData =
|
|
26958
|
+
defaultRootsData = fs46.readFileSync(DEFAULT_ROOTS_FILE_PATH);
|
|
26959
26959
|
}
|
|
26960
26960
|
return defaultRootsData;
|
|
26961
26961
|
}
|
|
@@ -31086,7 +31086,7 @@ var require_fetch = __commonJS({
|
|
|
31086
31086
|
module2.exports = fetch3;
|
|
31087
31087
|
var asPromise = require_aspromise();
|
|
31088
31088
|
var inquire2 = require_inquire();
|
|
31089
|
-
var
|
|
31089
|
+
var fs46 = inquire2("fs");
|
|
31090
31090
|
function fetch3(filename, options, callback) {
|
|
31091
31091
|
if (typeof options === "function") {
|
|
31092
31092
|
callback = options;
|
|
@@ -31095,8 +31095,8 @@ var require_fetch = __commonJS({
|
|
|
31095
31095
|
options = {};
|
|
31096
31096
|
if (!callback)
|
|
31097
31097
|
return asPromise(fetch3, this, filename, options);
|
|
31098
|
-
if (!options.xhr &&
|
|
31099
|
-
return
|
|
31098
|
+
if (!options.xhr && fs46 && fs46.readFile)
|
|
31099
|
+
return fs46.readFile(filename, function fetchReadFileCallback(err, contents) {
|
|
31100
31100
|
return err && typeof XMLHttpRequest !== "undefined" ? fetch3.xhr(filename, options, callback) : err ? callback(err) : callback(null, options.binary ? contents : contents.toString("utf8"));
|
|
31101
31101
|
});
|
|
31102
31102
|
return fetch3.xhr(filename, options, callback);
|
|
@@ -37370,7 +37370,7 @@ var require_util3 = __commonJS({
|
|
|
37370
37370
|
"use strict";
|
|
37371
37371
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
37372
37372
|
exports2.addCommonProtos = exports2.loadProtosWithOptionsSync = exports2.loadProtosWithOptions = void 0;
|
|
37373
|
-
var
|
|
37373
|
+
var fs46 = __require("fs");
|
|
37374
37374
|
var path47 = __require("path");
|
|
37375
37375
|
var Protobuf = require_protobufjs();
|
|
37376
37376
|
function addIncludePathResolver(root, includePaths) {
|
|
@@ -37382,7 +37382,7 @@ var require_util3 = __commonJS({
|
|
|
37382
37382
|
for (const directory of includePaths) {
|
|
37383
37383
|
const fullPath = path47.join(directory, target);
|
|
37384
37384
|
try {
|
|
37385
|
-
|
|
37385
|
+
fs46.accessSync(fullPath, fs46.constants.R_OK);
|
|
37386
37386
|
return fullPath;
|
|
37387
37387
|
} catch (err) {
|
|
37388
37388
|
continue;
|
|
@@ -47204,7 +47204,7 @@ var require_certificate_provider = __commonJS({
|
|
|
47204
47204
|
"use strict";
|
|
47205
47205
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
47206
47206
|
exports2.FileWatcherCertificateProvider = void 0;
|
|
47207
|
-
var
|
|
47207
|
+
var fs46 = __require("fs");
|
|
47208
47208
|
var logging = require_logging();
|
|
47209
47209
|
var constants_1 = require_constants2();
|
|
47210
47210
|
var util_1 = __require("util");
|
|
@@ -47212,7 +47212,7 @@ var require_certificate_provider = __commonJS({
|
|
|
47212
47212
|
function trace2(text) {
|
|
47213
47213
|
logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME2, text);
|
|
47214
47214
|
}
|
|
47215
|
-
var readFilePromise = (0, util_1.promisify)(
|
|
47215
|
+
var readFilePromise = (0, util_1.promisify)(fs46.readFile);
|
|
47216
47216
|
var FileWatcherCertificateProvider = class {
|
|
47217
47217
|
constructor(config2) {
|
|
47218
47218
|
this.config = config2;
|
|
@@ -49124,7 +49124,7 @@ var require_otlp_grpc_env_configuration = __commonJS({
|
|
|
49124
49124
|
var core_1 = require_src();
|
|
49125
49125
|
var grpc_exporter_transport_1 = require_grpc_exporter_transport();
|
|
49126
49126
|
var node_http_1 = (init_index_node_http(), __toCommonJS(index_node_http_exports));
|
|
49127
|
-
var
|
|
49127
|
+
var fs46 = __require("fs");
|
|
49128
49128
|
var path47 = __require("path");
|
|
49129
49129
|
var api_1 = (init_esm(), __toCommonJS(esm_exports));
|
|
49130
49130
|
function fallbackIfNullishOrBlank(signalSpecific, nonSignalSpecific) {
|
|
@@ -49174,7 +49174,7 @@ var require_otlp_grpc_env_configuration = __commonJS({
|
|
|
49174
49174
|
const filePath = fallbackIfNullishOrBlank(signalSpecificPath, nonSignalSpecificPath);
|
|
49175
49175
|
if (filePath != null) {
|
|
49176
49176
|
try {
|
|
49177
|
-
return
|
|
49177
|
+
return fs46.readFileSync(path47.resolve(process.cwd(), filePath));
|
|
49178
49178
|
} catch {
|
|
49179
49179
|
api_1.diag.warn(warningMessage);
|
|
49180
49180
|
return void 0;
|
|
@@ -59234,14 +59234,14 @@ var require_parser = __commonJS({
|
|
|
59234
59234
|
case "scalar":
|
|
59235
59235
|
case "single-quoted-scalar":
|
|
59236
59236
|
case "double-quoted-scalar": {
|
|
59237
|
-
const
|
|
59237
|
+
const fs46 = this.flowScalar(this.type);
|
|
59238
59238
|
if (atNextItem || it.value) {
|
|
59239
|
-
map2.items.push({ start, key:
|
|
59239
|
+
map2.items.push({ start, key: fs46, sep: [] });
|
|
59240
59240
|
this.onKeyLine = true;
|
|
59241
59241
|
} else if (it.sep) {
|
|
59242
|
-
this.stack.push(
|
|
59242
|
+
this.stack.push(fs46);
|
|
59243
59243
|
} else {
|
|
59244
|
-
Object.assign(it, { key:
|
|
59244
|
+
Object.assign(it, { key: fs46, sep: [] });
|
|
59245
59245
|
this.onKeyLine = true;
|
|
59246
59246
|
}
|
|
59247
59247
|
return;
|
|
@@ -59369,13 +59369,13 @@ var require_parser = __commonJS({
|
|
|
59369
59369
|
case "scalar":
|
|
59370
59370
|
case "single-quoted-scalar":
|
|
59371
59371
|
case "double-quoted-scalar": {
|
|
59372
|
-
const
|
|
59372
|
+
const fs46 = this.flowScalar(this.type);
|
|
59373
59373
|
if (!it || it.value)
|
|
59374
|
-
fc.items.push({ start: [], key:
|
|
59374
|
+
fc.items.push({ start: [], key: fs46, sep: [] });
|
|
59375
59375
|
else if (it.sep)
|
|
59376
|
-
this.stack.push(
|
|
59376
|
+
this.stack.push(fs46);
|
|
59377
59377
|
else
|
|
59378
|
-
Object.assign(it, { key:
|
|
59378
|
+
Object.assign(it, { key: fs46, sep: [] });
|
|
59379
59379
|
return;
|
|
59380
59380
|
}
|
|
59381
59381
|
case "flow-map-end":
|
|
@@ -59691,7 +59691,7 @@ var require_FileConfigFactory = __commonJS({
|
|
|
59691
59691
|
exports2.setLoggerProvider = exports2.getSeverity = exports2.setMeterProvider = exports2.getTemporalityPreference = exports2.setTracerProvider = exports2.setPropagator = exports2.setAttributeLimits = exports2.setResourceAttributes = exports2.parseConfigFile = exports2.hasValidConfigFile = exports2.FileConfigFactory = void 0;
|
|
59692
59692
|
var core_1 = require_src();
|
|
59693
59693
|
var configModel_1 = require_configModel();
|
|
59694
|
-
var
|
|
59694
|
+
var fs46 = __require("fs");
|
|
59695
59695
|
var yaml = require_dist();
|
|
59696
59696
|
var utils_1 = require_utils10();
|
|
59697
59697
|
var commonModel_1 = require_commonModel();
|
|
@@ -59713,7 +59713,7 @@ var require_FileConfigFactory = __commonJS({
|
|
|
59713
59713
|
function hasValidConfigFile() {
|
|
59714
59714
|
const configFile = (0, core_1.getStringFromEnv)("OTEL_EXPERIMENTAL_CONFIG_FILE");
|
|
59715
59715
|
if (configFile) {
|
|
59716
|
-
if (!(configFile.endsWith(".yaml") || configFile.endsWith(".yml")) || !
|
|
59716
|
+
if (!(configFile.endsWith(".yaml") || configFile.endsWith(".yml")) || !fs46.existsSync(configFile)) {
|
|
59717
59717
|
api_1.diag.warn(`Config file ${configFile} set on OTEL_EXPERIMENTAL_CONFIG_FILE is not valid`);
|
|
59718
59718
|
return false;
|
|
59719
59719
|
}
|
|
@@ -59725,7 +59725,7 @@ var require_FileConfigFactory = __commonJS({
|
|
|
59725
59725
|
function parseConfigFile(config2) {
|
|
59726
59726
|
const supportedFileVersions = ["1.0-rc.3"];
|
|
59727
59727
|
const configFile = (0, core_1.getStringFromEnv)("OTEL_EXPERIMENTAL_CONFIG_FILE") || "";
|
|
59728
|
-
const file2 =
|
|
59728
|
+
const file2 = fs46.readFileSync(configFile, "utf8");
|
|
59729
59729
|
const parsedContent = yaml.parse(file2);
|
|
59730
59730
|
if (parsedContent["file_format"] && supportedFileVersions.includes(parsedContent["file_format"])) {
|
|
59731
59731
|
const disabled = (0, utils_1.getBooleanFromConfigFile)(parsedContent["disabled"]);
|
|
@@ -61436,7 +61436,7 @@ var require_instrumentation3 = __commonJS({
|
|
|
61436
61436
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
61437
61437
|
exports2.AwsLambdaInstrumentation = exports2.AWS_HANDLER_STREAMING_RESPONSE = exports2.AWS_HANDLER_STREAMING_SYMBOL = exports2.lambdaMaxInitInMilliseconds = void 0;
|
|
61438
61438
|
var path47 = __require("path");
|
|
61439
|
-
var
|
|
61439
|
+
var fs46 = __require("fs");
|
|
61440
61440
|
var instrumentation_1 = require_src10();
|
|
61441
61441
|
var api_1 = (init_esm(), __toCommonJS(esm_exports));
|
|
61442
61442
|
var semantic_conventions_1 = (init_esm2(), __toCommonJS(esm_exports2));
|
|
@@ -61482,15 +61482,15 @@ var require_instrumentation3 = __commonJS({
|
|
|
61482
61482
|
let filename = path47.resolve(taskRoot, moduleRoot, module3);
|
|
61483
61483
|
if (!filename.endsWith(".js")) {
|
|
61484
61484
|
try {
|
|
61485
|
-
|
|
61485
|
+
fs46.statSync(`${filename}.js`);
|
|
61486
61486
|
filename += ".js";
|
|
61487
61487
|
} catch (e2) {
|
|
61488
61488
|
try {
|
|
61489
|
-
|
|
61489
|
+
fs46.statSync(`${filename}.mjs`);
|
|
61490
61490
|
filename += ".mjs";
|
|
61491
61491
|
} catch (e22) {
|
|
61492
61492
|
try {
|
|
61493
|
-
|
|
61493
|
+
fs46.statSync(`${filename}.cjs`);
|
|
61494
61494
|
filename += ".cjs";
|
|
61495
61495
|
} catch (e3) {
|
|
61496
61496
|
this._diag.warn("No handler file was able to resolved with one of the known extensions for the file", filename);
|
|
@@ -66162,19 +66162,19 @@ var require_utils17 = __commonJS({
|
|
|
66162
66162
|
}
|
|
66163
66163
|
}
|
|
66164
66164
|
exports2.splitTwoLevels = splitTwoLevels;
|
|
66165
|
-
function indexFs(
|
|
66165
|
+
function indexFs(fs46, member) {
|
|
66166
66166
|
if (!member)
|
|
66167
66167
|
throw new Error(JSON.stringify({ member }));
|
|
66168
66168
|
const splitResult = splitTwoLevels(member);
|
|
66169
66169
|
const [functionName1, functionName2] = splitResult;
|
|
66170
66170
|
if (functionName2) {
|
|
66171
66171
|
return {
|
|
66172
|
-
objectToPatch:
|
|
66172
|
+
objectToPatch: fs46[functionName1],
|
|
66173
66173
|
functionNameToPatch: functionName2
|
|
66174
66174
|
};
|
|
66175
66175
|
} else {
|
|
66176
66176
|
return {
|
|
66177
|
-
objectToPatch:
|
|
66177
|
+
objectToPatch: fs46,
|
|
66178
66178
|
functionNameToPatch: functionName1
|
|
66179
66179
|
};
|
|
66180
66180
|
}
|
|
@@ -66205,16 +66205,16 @@ var require_instrumentation12 = __commonJS({
|
|
|
66205
66205
|
}
|
|
66206
66206
|
init() {
|
|
66207
66207
|
return [
|
|
66208
|
-
new instrumentation_1.InstrumentationNodeModuleDefinition("fs", ["*"], (
|
|
66208
|
+
new instrumentation_1.InstrumentationNodeModuleDefinition("fs", ["*"], (fs46) => {
|
|
66209
66209
|
for (const fName of constants_1.SYNC_FUNCTIONS) {
|
|
66210
|
-
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(
|
|
66210
|
+
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(fs46, fName);
|
|
66211
66211
|
if ((0, instrumentation_1.isWrapped)(objectToPatch[functionNameToPatch])) {
|
|
66212
66212
|
this._unwrap(objectToPatch, functionNameToPatch);
|
|
66213
66213
|
}
|
|
66214
66214
|
this._wrap(objectToPatch, functionNameToPatch, this._patchSyncFunction.bind(this, fName));
|
|
66215
66215
|
}
|
|
66216
66216
|
for (const fName of constants_1.CALLBACK_FUNCTIONS) {
|
|
66217
|
-
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(
|
|
66217
|
+
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(fs46, fName);
|
|
66218
66218
|
if ((0, instrumentation_1.isWrapped)(objectToPatch[functionNameToPatch])) {
|
|
66219
66219
|
this._unwrap(objectToPatch, functionNameToPatch);
|
|
66220
66220
|
}
|
|
@@ -66225,30 +66225,30 @@ var require_instrumentation12 = __commonJS({
|
|
|
66225
66225
|
this._wrap(objectToPatch, functionNameToPatch, this._patchCallbackFunction.bind(this, fName));
|
|
66226
66226
|
}
|
|
66227
66227
|
for (const fName of constants_1.PROMISE_FUNCTIONS) {
|
|
66228
|
-
if ((0, instrumentation_1.isWrapped)(
|
|
66229
|
-
this._unwrap(
|
|
66228
|
+
if ((0, instrumentation_1.isWrapped)(fs46.promises[fName])) {
|
|
66229
|
+
this._unwrap(fs46.promises, fName);
|
|
66230
66230
|
}
|
|
66231
|
-
this._wrap(
|
|
66231
|
+
this._wrap(fs46.promises, fName, this._patchPromiseFunction.bind(this, fName));
|
|
66232
66232
|
}
|
|
66233
|
-
return
|
|
66234
|
-
}, (
|
|
66235
|
-
if (
|
|
66233
|
+
return fs46;
|
|
66234
|
+
}, (fs46) => {
|
|
66235
|
+
if (fs46 === void 0)
|
|
66236
66236
|
return;
|
|
66237
66237
|
for (const fName of constants_1.SYNC_FUNCTIONS) {
|
|
66238
|
-
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(
|
|
66238
|
+
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(fs46, fName);
|
|
66239
66239
|
if ((0, instrumentation_1.isWrapped)(objectToPatch[functionNameToPatch])) {
|
|
66240
66240
|
this._unwrap(objectToPatch, functionNameToPatch);
|
|
66241
66241
|
}
|
|
66242
66242
|
}
|
|
66243
66243
|
for (const fName of constants_1.CALLBACK_FUNCTIONS) {
|
|
66244
|
-
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(
|
|
66244
|
+
const { objectToPatch, functionNameToPatch } = (0, utils_1.indexFs)(fs46, fName);
|
|
66245
66245
|
if ((0, instrumentation_1.isWrapped)(objectToPatch[functionNameToPatch])) {
|
|
66246
66246
|
this._unwrap(objectToPatch, functionNameToPatch);
|
|
66247
66247
|
}
|
|
66248
66248
|
}
|
|
66249
66249
|
for (const fName of constants_1.PROMISE_FUNCTIONS) {
|
|
66250
|
-
if ((0, instrumentation_1.isWrapped)(
|
|
66251
|
-
this._unwrap(
|
|
66250
|
+
if ((0, instrumentation_1.isWrapped)(fs46.promises[fName])) {
|
|
66251
|
+
this._unwrap(fs46.promises, fName);
|
|
66252
66252
|
}
|
|
66253
66253
|
}
|
|
66254
66254
|
}),
|
|
@@ -79247,14 +79247,14 @@ var require_AwsBeanstalkDetector = __commonJS({
|
|
|
79247
79247
|
var core_1 = require_src();
|
|
79248
79248
|
var semantic_conventions_1 = (init_esm2(), __toCommonJS(esm_exports2));
|
|
79249
79249
|
var semconv_1 = require_semconv30();
|
|
79250
|
-
var
|
|
79250
|
+
var fs46 = __require("fs");
|
|
79251
79251
|
var util = __require("util");
|
|
79252
79252
|
var DEFAULT_BEANSTALK_CONF_PATH = "/var/elasticbeanstalk/xray/environment.conf";
|
|
79253
79253
|
var WIN_OS_BEANSTALK_CONF_PATH = "C:\\Program Files\\Amazon\\XRay\\environment.conf";
|
|
79254
79254
|
var AwsBeanstalkDetector = class _AwsBeanstalkDetector {
|
|
79255
79255
|
BEANSTALK_CONF_PATH;
|
|
79256
|
-
static readFileAsync = util.promisify(
|
|
79257
|
-
static fileAccessAsync = util.promisify(
|
|
79256
|
+
static readFileAsync = util.promisify(fs46.readFile);
|
|
79257
|
+
static fileAccessAsync = util.promisify(fs46.access);
|
|
79258
79258
|
constructor() {
|
|
79259
79259
|
if (process.platform === "win32") {
|
|
79260
79260
|
this.BEANSTALK_CONF_PATH = WIN_OS_BEANSTALK_CONF_PATH;
|
|
@@ -79283,7 +79283,7 @@ var require_AwsBeanstalkDetector = __commonJS({
|
|
|
79283
79283
|
*/
|
|
79284
79284
|
async _gatherData() {
|
|
79285
79285
|
try {
|
|
79286
|
-
await _AwsBeanstalkDetector.fileAccessAsync(this.BEANSTALK_CONF_PATH,
|
|
79286
|
+
await _AwsBeanstalkDetector.fileAccessAsync(this.BEANSTALK_CONF_PATH, fs46.constants.R_OK);
|
|
79287
79287
|
const rawData = await _AwsBeanstalkDetector.readFileAsync(this.BEANSTALK_CONF_PATH, "utf8");
|
|
79288
79288
|
const parsedData = JSON.parse(rawData);
|
|
79289
79289
|
return {
|
|
@@ -79458,14 +79458,14 @@ var require_AwsEcsDetector = __commonJS({
|
|
|
79458
79458
|
var semconv_1 = require_semconv30();
|
|
79459
79459
|
var http3 = __require("http");
|
|
79460
79460
|
var util = __require("util");
|
|
79461
|
-
var
|
|
79461
|
+
var fs46 = __require("fs");
|
|
79462
79462
|
var os5 = __require("os");
|
|
79463
79463
|
var HTTP_TIMEOUT_IN_MS = 1e3;
|
|
79464
79464
|
var AwsEcsDetector = class _AwsEcsDetector {
|
|
79465
79465
|
static CONTAINER_ID_LENGTH = 64;
|
|
79466
79466
|
static CONTAINER_ID_LENGTH_MIN = 32;
|
|
79467
79467
|
static DEFAULT_CGROUP_PATH = "/proc/self/cgroup";
|
|
79468
|
-
static readFileAsync = util.promisify(
|
|
79468
|
+
static readFileAsync = util.promisify(fs46.readFile);
|
|
79469
79469
|
detect() {
|
|
79470
79470
|
const attributes = api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), () => this._getAttributes());
|
|
79471
79471
|
return { attributes };
|
|
@@ -79669,7 +79669,7 @@ var require_AwsEksDetector = __commonJS({
|
|
|
79669
79669
|
var core_1 = require_src();
|
|
79670
79670
|
var semconv_1 = require_semconv30();
|
|
79671
79671
|
var https2 = __require("https");
|
|
79672
|
-
var
|
|
79672
|
+
var fs46 = __require("fs");
|
|
79673
79673
|
var util = __require("util");
|
|
79674
79674
|
var api_2 = (init_esm(), __toCommonJS(esm_exports));
|
|
79675
79675
|
var AwsEksDetector = class _AwsEksDetector {
|
|
@@ -79682,8 +79682,8 @@ var require_AwsEksDetector = __commonJS({
|
|
|
79682
79682
|
DEFAULT_CGROUP_PATH = "/proc/self/cgroup";
|
|
79683
79683
|
TIMEOUT_MS = 2e3;
|
|
79684
79684
|
UTF8_UNICODE = "utf8";
|
|
79685
|
-
static readFileAsync = util.promisify(
|
|
79686
|
-
static fileAccessAsync = util.promisify(
|
|
79685
|
+
static readFileAsync = util.promisify(fs46.readFile);
|
|
79686
|
+
static fileAccessAsync = util.promisify(fs46.access);
|
|
79687
79687
|
detect() {
|
|
79688
79688
|
const dataPromise = api_1.context.with((0, core_1.suppressTracing)(api_1.context.active()), () => this._gatherData());
|
|
79689
79689
|
const attrNames = [
|
|
@@ -80015,7 +80015,7 @@ var require_ContainerDetector = __commonJS({
|
|
|
80015
80015
|
"use strict";
|
|
80016
80016
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
80017
80017
|
exports2.containerDetector = exports2.ContainerDetector = void 0;
|
|
80018
|
-
var
|
|
80018
|
+
var fs46 = __require("fs");
|
|
80019
80019
|
var util = __require("util");
|
|
80020
80020
|
var api_1 = (init_esm(), __toCommonJS(esm_exports));
|
|
80021
80021
|
var core_1 = require_src();
|
|
@@ -80028,7 +80028,7 @@ var require_ContainerDetector = __commonJS({
|
|
|
80028
80028
|
UTF8_UNICODE = "utf8";
|
|
80029
80029
|
HOSTNAME = "hostname";
|
|
80030
80030
|
MARKING_PREFIX = ["containers", "overlay-containers"];
|
|
80031
|
-
static readFileAsync = util.promisify(
|
|
80031
|
+
static readFileAsync = util.promisify(fs46.readFile);
|
|
80032
80032
|
detect() {
|
|
80033
80033
|
const attributes = {
|
|
80034
80034
|
[semconv_1.ATTR_CONTAINER_ID]: this._getContainerIdWithSuppressedTracing()
|
|
@@ -91095,7 +91095,7 @@ var require_AzureAksDetector = __commonJS({
|
|
|
91095
91095
|
"use strict";
|
|
91096
91096
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
91097
91097
|
exports2.azureAksDetector = void 0;
|
|
91098
|
-
var
|
|
91098
|
+
var fs46 = __require("fs");
|
|
91099
91099
|
var api_1 = (init_esm(), __toCommonJS(esm_exports));
|
|
91100
91100
|
var semconv_1 = require_semconv33();
|
|
91101
91101
|
var types_1 = require_types13();
|
|
@@ -91136,10 +91136,10 @@ var require_AzureAksDetector = __commonJS({
|
|
|
91136
91136
|
}
|
|
91137
91137
|
getAksMetadataFromFile() {
|
|
91138
91138
|
try {
|
|
91139
|
-
if (!
|
|
91139
|
+
if (!fs46.existsSync(types_1.AKS_METADATA_FILE_PATH)) {
|
|
91140
91140
|
return void 0;
|
|
91141
91141
|
}
|
|
91142
|
-
const content =
|
|
91142
|
+
const content = fs46.readFileSync(types_1.AKS_METADATA_FILE_PATH, "utf8");
|
|
91143
91143
|
const metadata = {};
|
|
91144
91144
|
const lines = content.split("\n");
|
|
91145
91145
|
for (const line of lines) {
|
|
@@ -92919,7 +92919,7 @@ var require_atomic_sleep = __commonJS({
|
|
|
92919
92919
|
var require_sonic_boom = __commonJS({
|
|
92920
92920
|
"node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
92921
92921
|
"use strict";
|
|
92922
|
-
var
|
|
92922
|
+
var fs46 = __require("fs");
|
|
92923
92923
|
var EventEmitter = __require("events");
|
|
92924
92924
|
var inherits = __require("util").inherits;
|
|
92925
92925
|
var path47 = __require("path");
|
|
@@ -92976,20 +92976,20 @@ var require_sonic_boom = __commonJS({
|
|
|
92976
92976
|
const mode = sonic.mode;
|
|
92977
92977
|
if (sonic.sync) {
|
|
92978
92978
|
try {
|
|
92979
|
-
if (sonic.mkdir)
|
|
92980
|
-
const fd =
|
|
92979
|
+
if (sonic.mkdir) fs46.mkdirSync(path47.dirname(file2), { recursive: true });
|
|
92980
|
+
const fd = fs46.openSync(file2, flags, mode);
|
|
92981
92981
|
fileOpened(null, fd);
|
|
92982
92982
|
} catch (err) {
|
|
92983
92983
|
fileOpened(err);
|
|
92984
92984
|
throw err;
|
|
92985
92985
|
}
|
|
92986
92986
|
} else if (sonic.mkdir) {
|
|
92987
|
-
|
|
92987
|
+
fs46.mkdir(path47.dirname(file2), { recursive: true }, (err) => {
|
|
92988
92988
|
if (err) return fileOpened(err);
|
|
92989
|
-
|
|
92989
|
+
fs46.open(file2, flags, mode, fileOpened);
|
|
92990
92990
|
});
|
|
92991
92991
|
} else {
|
|
92992
|
-
|
|
92992
|
+
fs46.open(file2, flags, mode, fileOpened);
|
|
92993
92993
|
}
|
|
92994
92994
|
}
|
|
92995
92995
|
function SonicBoom(opts) {
|
|
@@ -93030,8 +93030,8 @@ var require_sonic_boom = __commonJS({
|
|
|
93030
93030
|
this.flush = flushBuffer;
|
|
93031
93031
|
this.flushSync = flushBufferSync;
|
|
93032
93032
|
this._actualWrite = actualWriteBuffer;
|
|
93033
|
-
fsWriteSync = () =>
|
|
93034
|
-
fsWrite = () =>
|
|
93033
|
+
fsWriteSync = () => fs46.writeSync(this.fd, this._writingBuf);
|
|
93034
|
+
fsWrite = () => fs46.write(this.fd, this._writingBuf, this.release);
|
|
93035
93035
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
93036
93036
|
this._writingBuf = "";
|
|
93037
93037
|
this.write = write2;
|
|
@@ -93040,15 +93040,15 @@ var require_sonic_boom = __commonJS({
|
|
|
93040
93040
|
this._actualWrite = actualWrite;
|
|
93041
93041
|
fsWriteSync = () => {
|
|
93042
93042
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
93043
|
-
return
|
|
93043
|
+
return fs46.writeSync(this.fd, this._writingBuf);
|
|
93044
93044
|
}
|
|
93045
|
-
return
|
|
93045
|
+
return fs46.writeSync(this.fd, this._writingBuf, "utf8");
|
|
93046
93046
|
};
|
|
93047
93047
|
fsWrite = () => {
|
|
93048
93048
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
93049
|
-
return
|
|
93049
|
+
return fs46.write(this.fd, this._writingBuf, this.release);
|
|
93050
93050
|
}
|
|
93051
|
-
return
|
|
93051
|
+
return fs46.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
93052
93052
|
};
|
|
93053
93053
|
} else {
|
|
93054
93054
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -93105,7 +93105,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93105
93105
|
}
|
|
93106
93106
|
}
|
|
93107
93107
|
if (this._fsync) {
|
|
93108
|
-
|
|
93108
|
+
fs46.fsyncSync(this.fd);
|
|
93109
93109
|
}
|
|
93110
93110
|
const len = this._len;
|
|
93111
93111
|
if (this._reopening) {
|
|
@@ -93219,7 +93219,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93219
93219
|
const onDrain = () => {
|
|
93220
93220
|
if (!this._fsync) {
|
|
93221
93221
|
try {
|
|
93222
|
-
|
|
93222
|
+
fs46.fsync(this.fd, (err) => {
|
|
93223
93223
|
this._flushPending = false;
|
|
93224
93224
|
cb(err);
|
|
93225
93225
|
});
|
|
@@ -93321,7 +93321,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93321
93321
|
const fd = this.fd;
|
|
93322
93322
|
this.once("ready", () => {
|
|
93323
93323
|
if (fd !== this.fd) {
|
|
93324
|
-
|
|
93324
|
+
fs46.close(fd, (err) => {
|
|
93325
93325
|
if (err) {
|
|
93326
93326
|
return this.emit("error", err);
|
|
93327
93327
|
}
|
|
@@ -93370,7 +93370,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93370
93370
|
buf = this._bufs[0];
|
|
93371
93371
|
}
|
|
93372
93372
|
try {
|
|
93373
|
-
const n = Buffer.isBuffer(buf) ?
|
|
93373
|
+
const n = Buffer.isBuffer(buf) ? fs46.writeSync(this.fd, buf) : fs46.writeSync(this.fd, buf, "utf8");
|
|
93374
93374
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
93375
93375
|
buf = releasedBufObj.writingBuf;
|
|
93376
93376
|
this._len = releasedBufObj.len;
|
|
@@ -93386,7 +93386,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93386
93386
|
}
|
|
93387
93387
|
}
|
|
93388
93388
|
try {
|
|
93389
|
-
|
|
93389
|
+
fs46.fsyncSync(this.fd);
|
|
93390
93390
|
} catch {
|
|
93391
93391
|
}
|
|
93392
93392
|
}
|
|
@@ -93407,7 +93407,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93407
93407
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
93408
93408
|
}
|
|
93409
93409
|
try {
|
|
93410
|
-
const n =
|
|
93410
|
+
const n = fs46.writeSync(this.fd, buf);
|
|
93411
93411
|
buf = buf.subarray(n);
|
|
93412
93412
|
this._len = Math.max(this._len - n, 0);
|
|
93413
93413
|
if (buf.length <= 0) {
|
|
@@ -93435,13 +93435,13 @@ var require_sonic_boom = __commonJS({
|
|
|
93435
93435
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
93436
93436
|
if (this.sync) {
|
|
93437
93437
|
try {
|
|
93438
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
93438
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs46.writeSync(this.fd, this._writingBuf) : fs46.writeSync(this.fd, this._writingBuf, "utf8");
|
|
93439
93439
|
release(null, written);
|
|
93440
93440
|
} catch (err) {
|
|
93441
93441
|
release(err);
|
|
93442
93442
|
}
|
|
93443
93443
|
} else {
|
|
93444
|
-
|
|
93444
|
+
fs46.write(this.fd, this._writingBuf, release);
|
|
93445
93445
|
}
|
|
93446
93446
|
}
|
|
93447
93447
|
function actualWriteBuffer() {
|
|
@@ -93450,7 +93450,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93450
93450
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
93451
93451
|
if (this.sync) {
|
|
93452
93452
|
try {
|
|
93453
|
-
const written =
|
|
93453
|
+
const written = fs46.writeSync(this.fd, this._writingBuf);
|
|
93454
93454
|
release(null, written);
|
|
93455
93455
|
} catch (err) {
|
|
93456
93456
|
release(err);
|
|
@@ -93459,7 +93459,7 @@ var require_sonic_boom = __commonJS({
|
|
|
93459
93459
|
if (kCopyBuffer) {
|
|
93460
93460
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
93461
93461
|
}
|
|
93462
|
-
|
|
93462
|
+
fs46.write(this.fd, this._writingBuf, release);
|
|
93463
93463
|
}
|
|
93464
93464
|
}
|
|
93465
93465
|
function actualClose(sonic) {
|
|
@@ -93475,12 +93475,12 @@ var require_sonic_boom = __commonJS({
|
|
|
93475
93475
|
sonic._lens = [];
|
|
93476
93476
|
assert2(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
93477
93477
|
try {
|
|
93478
|
-
|
|
93478
|
+
fs46.fsync(sonic.fd, closeWrapped);
|
|
93479
93479
|
} catch {
|
|
93480
93480
|
}
|
|
93481
93481
|
function closeWrapped() {
|
|
93482
93482
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
93483
|
-
|
|
93483
|
+
fs46.close(sonic.fd, done);
|
|
93484
93484
|
} else {
|
|
93485
93485
|
done();
|
|
93486
93486
|
}
|
|
@@ -96472,9 +96472,9 @@ var require_pump = __commonJS({
|
|
|
96472
96472
|
"node_modules/pump/index.js"(exports2, module2) {
|
|
96473
96473
|
var once = require_once();
|
|
96474
96474
|
var eos = require_end_of_stream();
|
|
96475
|
-
var
|
|
96475
|
+
var fs46;
|
|
96476
96476
|
try {
|
|
96477
|
-
|
|
96477
|
+
fs46 = __require("fs");
|
|
96478
96478
|
} catch (e2) {
|
|
96479
96479
|
}
|
|
96480
96480
|
var noop2 = function() {
|
|
@@ -96485,8 +96485,8 @@ var require_pump = __commonJS({
|
|
|
96485
96485
|
};
|
|
96486
96486
|
var isFS = function(stream) {
|
|
96487
96487
|
if (!ancient) return false;
|
|
96488
|
-
if (!
|
|
96489
|
-
return (stream instanceof (
|
|
96488
|
+
if (!fs46) return false;
|
|
96489
|
+
return (stream instanceof (fs46.ReadStream || noop2) || stream instanceof (fs46.WriteStream || noop2)) && isFn(stream.close);
|
|
96490
96490
|
};
|
|
96491
96491
|
var isRequest2 = function(stream) {
|
|
96492
96492
|
return stream.setHeader && isFn(stream.abort);
|
|
@@ -113905,8 +113905,8 @@ import fsSync from "node:fs";
|
|
|
113905
113905
|
import path5 from "node:path";
|
|
113906
113906
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
113907
113907
|
function getCurrentVersion() {
|
|
113908
|
-
if ("0.2.
|
|
113909
|
-
return "0.2.
|
|
113908
|
+
if ("0.2.23") {
|
|
113909
|
+
return "0.2.23";
|
|
113910
113910
|
}
|
|
113911
113911
|
try {
|
|
113912
113912
|
const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
|
|
@@ -115213,7 +115213,9 @@ var init_labels = __esm({
|
|
|
115213
115213
|
{ name: "type:research", color: "0075CA" },
|
|
115214
115214
|
{ name: "type:infra", color: "5319E7" },
|
|
115215
115215
|
{ name: "needs-human", color: "d73a4a" },
|
|
115216
|
-
{ name: "approved", color: "0e8a16" }
|
|
115216
|
+
{ name: "approved", color: "0e8a16" },
|
|
115217
|
+
{ name: "decomposition:parent", color: "1d76db" },
|
|
115218
|
+
{ name: "decomposition:child", color: "5319e7" }
|
|
115217
115219
|
];
|
|
115218
115220
|
STEP_ROUTING_COLOR = "#d93f0b";
|
|
115219
115221
|
NOTIFY_LABEL_PREFIX = "notify:";
|
|
@@ -117293,21 +117295,21 @@ var init_types4 = __esm({
|
|
|
117293
117295
|
});
|
|
117294
117296
|
|
|
117295
117297
|
// lib/github/file-event-store.ts
|
|
117296
|
-
import
|
|
117298
|
+
import fs28 from "node:fs/promises";
|
|
117297
117299
|
import path27 from "node:path";
|
|
117298
117300
|
function encodeId(id) {
|
|
117299
117301
|
return Buffer.from(id, "utf-8").toString("base64url");
|
|
117300
117302
|
}
|
|
117301
117303
|
async function atomicWriteJson(filePath, value) {
|
|
117302
|
-
await
|
|
117304
|
+
await fs28.mkdir(path27.dirname(filePath), { recursive: true });
|
|
117303
117305
|
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
117304
|
-
await
|
|
117305
|
-
await
|
|
117306
|
+
await fs28.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
117307
|
+
await fs28.rename(tmpPath, filePath);
|
|
117306
117308
|
}
|
|
117307
117309
|
async function writeNewJsonExclusive(filePath, value) {
|
|
117308
|
-
await
|
|
117310
|
+
await fs28.mkdir(path27.dirname(filePath), { recursive: true });
|
|
117309
117311
|
try {
|
|
117310
|
-
await
|
|
117312
|
+
await fs28.writeFile(filePath, JSON.stringify(value, null, 2) + "\n", {
|
|
117311
117313
|
encoding: "utf-8",
|
|
117312
117314
|
flag: "wx"
|
|
117313
117315
|
});
|
|
@@ -117319,7 +117321,7 @@ async function writeNewJsonExclusive(filePath, value) {
|
|
|
117319
117321
|
}
|
|
117320
117322
|
async function readJsonFile(filePath) {
|
|
117321
117323
|
try {
|
|
117322
|
-
const raw = await
|
|
117324
|
+
const raw = await fs28.readFile(filePath, "utf-8");
|
|
117323
117325
|
return JSON.parse(raw);
|
|
117324
117326
|
} catch (error48) {
|
|
117325
117327
|
if (error48?.code === "ENOENT") return null;
|
|
@@ -117378,7 +117380,7 @@ var init_file_event_store = __esm({
|
|
|
117378
117380
|
const dir = this.storeDir;
|
|
117379
117381
|
let entries = [];
|
|
117380
117382
|
try {
|
|
117381
|
-
entries = await
|
|
117383
|
+
entries = await fs28.readdir(dir);
|
|
117382
117384
|
} catch (error48) {
|
|
117383
117385
|
if (error48?.code === "ENOENT") return [];
|
|
117384
117386
|
throw error48;
|
|
@@ -117396,7 +117398,7 @@ var init_file_event_store = __esm({
|
|
|
117396
117398
|
const dir = this.storeDir;
|
|
117397
117399
|
let entries = [];
|
|
117398
117400
|
try {
|
|
117399
|
-
entries = await
|
|
117401
|
+
entries = await fs28.readdir(dir);
|
|
117400
117402
|
} catch (error48) {
|
|
117401
117403
|
if (error48?.code === "ENOENT") return [];
|
|
117402
117404
|
throw error48;
|
|
@@ -117466,14 +117468,14 @@ var init_file_event_store = __esm({
|
|
|
117466
117468
|
async save(run) {
|
|
117467
117469
|
const validated = fabricaRunSchema.parse(run);
|
|
117468
117470
|
try {
|
|
117469
|
-
const entries = await
|
|
117471
|
+
const entries = await fs28.readdir(this.storeDir);
|
|
117470
117472
|
for (const entry of entries) {
|
|
117471
117473
|
const filePath = path27.join(this.storeDir, entry);
|
|
117472
117474
|
const existing = await readJsonFile(filePath);
|
|
117473
117475
|
if (!existing) continue;
|
|
117474
117476
|
const parsed = fabricaRunSchema.parse(existing);
|
|
117475
117477
|
if (parsed.runId !== validated.runId && parsed.installationId === validated.installationId && parsed.repositoryId === validated.repositoryId && parsed.prNumber === validated.prNumber) {
|
|
117476
|
-
await
|
|
117478
|
+
await fs28.rm(filePath, { force: true });
|
|
117477
117479
|
}
|
|
117478
117480
|
}
|
|
117479
117481
|
} catch (error48) {
|
|
@@ -117489,7 +117491,7 @@ var init_file_event_store = __esm({
|
|
|
117489
117491
|
const dir = this.storeDir;
|
|
117490
117492
|
let entries = [];
|
|
117491
117493
|
try {
|
|
117492
|
-
entries = await
|
|
117494
|
+
entries = await fs28.readdir(dir);
|
|
117493
117495
|
} catch (error48) {
|
|
117494
117496
|
if (error48?.code === "ENOENT") return [];
|
|
117495
117497
|
throw error48;
|
|
@@ -117510,7 +117512,7 @@ var init_file_event_store = __esm({
|
|
|
117510
117512
|
});
|
|
117511
117513
|
|
|
117512
117514
|
// lib/github/sqlite-event-store.ts
|
|
117513
|
-
import
|
|
117515
|
+
import fs29 from "node:fs/promises";
|
|
117514
117516
|
import path28 from "node:path";
|
|
117515
117517
|
function asNumber(value) {
|
|
117516
117518
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
@@ -117569,7 +117571,7 @@ async function loadSqliteModule() {
|
|
|
117569
117571
|
return loaded;
|
|
117570
117572
|
}
|
|
117571
117573
|
async function openDatabase(dbPath) {
|
|
117572
|
-
await
|
|
117574
|
+
await fs29.mkdir(path28.dirname(dbPath), { recursive: true });
|
|
117573
117575
|
const sqlite = await loadSqliteModule();
|
|
117574
117576
|
const db = new sqlite.DatabaseSync(dbPath);
|
|
117575
117577
|
db.exec(`
|
|
@@ -118183,7 +118185,7 @@ var init_extract_json = __esm({
|
|
|
118183
118185
|
});
|
|
118184
118186
|
|
|
118185
118187
|
// lib/intake/lib/runtime-paths.ts
|
|
118186
|
-
import
|
|
118188
|
+
import fs31 from "node:fs";
|
|
118187
118189
|
import fsp from "node:fs/promises";
|
|
118188
118190
|
import os4 from "node:os";
|
|
118189
118191
|
import path30 from "node:path";
|
|
@@ -118191,7 +118193,7 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
|
118191
118193
|
function isUsableFile(candidate) {
|
|
118192
118194
|
if (!candidate) return false;
|
|
118193
118195
|
try {
|
|
118194
|
-
return
|
|
118196
|
+
return fs31.existsSync(candidate) && fs31.statSync(candidate).isFile();
|
|
118195
118197
|
} catch {
|
|
118196
118198
|
return false;
|
|
118197
118199
|
}
|
|
@@ -118199,7 +118201,7 @@ function isUsableFile(candidate) {
|
|
|
118199
118201
|
function isUsableDir(candidate) {
|
|
118200
118202
|
if (!candidate) return false;
|
|
118201
118203
|
try {
|
|
118202
|
-
return
|
|
118204
|
+
return fs31.existsSync(candidate) && fs31.statSync(candidate).isDirectory();
|
|
118203
118205
|
} catch {
|
|
118204
118206
|
return false;
|
|
118205
118207
|
}
|
|
@@ -118207,7 +118209,7 @@ function isUsableDir(candidate) {
|
|
|
118207
118209
|
function findNewestNvmOpenClaw(homeDir, env) {
|
|
118208
118210
|
const versionsDir = path30.join(homeDir, ".nvm", "versions", "node");
|
|
118209
118211
|
try {
|
|
118210
|
-
const versions =
|
|
118212
|
+
const versions = fs31.readdirSync(versionsDir).sort().reverse();
|
|
118211
118213
|
for (const version2 of versions) {
|
|
118212
118214
|
const candidate = path30.join(versionsDir, version2, "bin", "openclaw");
|
|
118213
118215
|
if (isUsableFile(candidate)) return candidate;
|
|
@@ -118618,9 +118620,9 @@ function fallbackSpecData(type, rawIdea) {
|
|
|
118618
118620
|
default:
|
|
118619
118621
|
base.scope_v1 = deriveFeatureScopeFromRawIdea(rawIdea);
|
|
118620
118622
|
base.acceptance_criteria = [
|
|
118621
|
-
"
|
|
118622
|
-
"
|
|
118623
|
-
"
|
|
118623
|
+
"Allows operators to complete the primary workflow end to end as requested",
|
|
118624
|
+
"Validates and enforces the role, permission, or delivery constraints described in the request",
|
|
118625
|
+
"Processes the asynchronous or background behavior required for the main operational path"
|
|
118624
118626
|
];
|
|
118625
118627
|
}
|
|
118626
118628
|
return base;
|
|
@@ -122062,6 +122064,37 @@ function getRoleWorker(project, role) {
|
|
|
122062
122064
|
function getIssueRuntime(project, issueId) {
|
|
122063
122065
|
return project.issueRuntime?.[String(issueId)];
|
|
122064
122066
|
}
|
|
122067
|
+
function isParentIssue(project, issueId) {
|
|
122068
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
122069
|
+
return !!(runtime && ((runtime.childIssueIds?.length ?? 0) > 0 || runtime.decompositionMode === "parent_child"));
|
|
122070
|
+
}
|
|
122071
|
+
function isChildIssue(project, issueId) {
|
|
122072
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
122073
|
+
return runtime?.parentIssueId != null;
|
|
122074
|
+
}
|
|
122075
|
+
function getParentIssueRuntime(project, issueId) {
|
|
122076
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
122077
|
+
if (!runtime?.parentIssueId) return void 0;
|
|
122078
|
+
return getIssueRuntime(project, runtime.parentIssueId);
|
|
122079
|
+
}
|
|
122080
|
+
function getChildIssueRuntimes(project, issueId) {
|
|
122081
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
122082
|
+
return (runtime?.childIssueIds ?? []).map((childIssueId) => ({
|
|
122083
|
+
issueId: childIssueId,
|
|
122084
|
+
runtime: getIssueRuntime(project, childIssueId)
|
|
122085
|
+
}));
|
|
122086
|
+
}
|
|
122087
|
+
function getDependencyIssueRuntimes(project, issueId) {
|
|
122088
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
122089
|
+
return (runtime?.dependencyIssueIds ?? []).map((dependencyIssueId) => ({
|
|
122090
|
+
issueId: dependencyIssueId,
|
|
122091
|
+
runtime: getIssueRuntime(project, dependencyIssueId)
|
|
122092
|
+
}));
|
|
122093
|
+
}
|
|
122094
|
+
function isIssueExecutionComplete(project, issueId) {
|
|
122095
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
122096
|
+
return !!(runtime?.decompositionStatus === "completed" || runtime?.artifactOfRecord?.mergedAt || runtime?.sessionCompletedAt);
|
|
122097
|
+
}
|
|
122065
122098
|
async function updateSlot(workspaceDir, slugOrChannelId, role, level, slotIndex, updates) {
|
|
122066
122099
|
const { data } = await withProjectsMutation(workspaceDir, (data2) => {
|
|
122067
122100
|
const slug = resolveProjectSlug(data2, slugOrChannelId);
|
|
@@ -122226,7 +122259,22 @@ async function clearIssueRuntime(workspaceDir, slugOrChannelId, issueId) {
|
|
|
122226
122259
|
}
|
|
122227
122260
|
const project = data2.projects[slug];
|
|
122228
122261
|
if (project.issueRuntime) {
|
|
122229
|
-
|
|
122262
|
+
const key = String(issueId);
|
|
122263
|
+
const existing = project.issueRuntime[key];
|
|
122264
|
+
const preserved = existing ? {
|
|
122265
|
+
parentIssueId: existing.parentIssueId ?? null,
|
|
122266
|
+
childIssueIds: existing.childIssueIds,
|
|
122267
|
+
dependencyIssueIds: existing.dependencyIssueIds,
|
|
122268
|
+
decompositionMode: existing.decompositionMode ?? null,
|
|
122269
|
+
decompositionStatus: existing.parentIssueId ? "completed" : existing.decompositionStatus ?? null,
|
|
122270
|
+
sessionCompletedAt: existing.sessionCompletedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
122271
|
+
artifactOfRecord: existing.artifactOfRecord ?? null
|
|
122272
|
+
} : null;
|
|
122273
|
+
if (preserved && (preserved.parentIssueId != null || (preserved.childIssueIds?.length ?? 0) > 0 || (preserved.dependencyIssueIds?.length ?? 0) > 0 || preserved.decompositionMode != null)) {
|
|
122274
|
+
project.issueRuntime[key] = preserved;
|
|
122275
|
+
} else {
|
|
122276
|
+
delete project.issueRuntime[key];
|
|
122277
|
+
}
|
|
122230
122278
|
}
|
|
122231
122279
|
});
|
|
122232
122280
|
return data;
|
|
@@ -123104,11 +123152,11 @@ var GitLabProvider = class {
|
|
|
123104
123152
|
const token = tokenRaw.stdout.trim();
|
|
123105
123153
|
if (!token) return null;
|
|
123106
123154
|
const os5 = await import("node:os");
|
|
123107
|
-
const
|
|
123155
|
+
const fs46 = await import("node:fs/promises");
|
|
123108
123156
|
const path47 = await import("node:path");
|
|
123109
|
-
const tmpDir = await
|
|
123157
|
+
const tmpDir = await fs46.mkdtemp(path47.join(os5.tmpdir(), "fabrica-upload-"));
|
|
123110
123158
|
const tmpFile = path47.join(tmpDir, file2.filename);
|
|
123111
|
-
await
|
|
123159
|
+
await fs46.writeFile(tmpFile, file2.buffer);
|
|
123112
123160
|
try {
|
|
123113
123161
|
const apiBase = webUrl.replace(/\/[^/]+\/[^/]+\/?$/, "");
|
|
123114
123162
|
const result = await this.runCommand(
|
|
@@ -123130,9 +123178,9 @@ var GitLabProvider = class {
|
|
|
123130
123178
|
if (parsed.url) return `${webUrl}${parsed.url}`;
|
|
123131
123179
|
return null;
|
|
123132
123180
|
} finally {
|
|
123133
|
-
await
|
|
123181
|
+
await fs46.unlink(tmpFile).catch(() => {
|
|
123134
123182
|
});
|
|
123135
|
-
await
|
|
123183
|
+
await fs46.rmdir(tmpDir).catch(() => {
|
|
123136
123184
|
});
|
|
123137
123185
|
}
|
|
123138
123186
|
} catch {
|
|
@@ -125007,12 +125055,12 @@ function prLink(url2) {
|
|
|
125007
125055
|
function buildMessage(event) {
|
|
125008
125056
|
switch (event.type) {
|
|
125009
125057
|
case "workerStart": {
|
|
125010
|
-
const
|
|
125058
|
+
const semanticAction = event.dispatchSemantic === "feedback_redispatch" ? "\u{1F501} Re-dispatched after feedback" : event.dispatchSemantic === "recovery_redispatch" ? "\u267B\uFE0F Re-dispatched after recovery" : event.dispatchSemantic === "bootstrap_initial_dispatch" ? "\u{1F9F1} Started from bootstrap" : event.sessionAction === "spawn" ? "\u{1F680} Started" : "\u25B6\uFE0F Resumed";
|
|
125011
125059
|
const worker = formatWorkerString(event.role, {
|
|
125012
125060
|
name: event.name,
|
|
125013
125061
|
level: event.level
|
|
125014
125062
|
});
|
|
125015
|
-
let msg = `${
|
|
125063
|
+
let msg = `${semanticAction} ${worker} on #${event.issueId}: ${event.issueTitle}
|
|
125016
125064
|
\u{1F517} [Issue #${event.issueId}](${event.issueUrl})`;
|
|
125017
125065
|
if (event.modelDowngraded && event.originalModel && event.effectiveModel) {
|
|
125018
125066
|
msg += `
|
|
@@ -125457,6 +125505,45 @@ init_audit();
|
|
|
125457
125505
|
|
|
125458
125506
|
// lib/services/queue-scan.ts
|
|
125459
125507
|
init_roles();
|
|
125508
|
+
|
|
125509
|
+
// lib/services/family-scheduler.ts
|
|
125510
|
+
function countActiveSiblingChildren(project, parentIssueId, currentIssueId) {
|
|
125511
|
+
const activeIssueIds = /* @__PURE__ */ new Set();
|
|
125512
|
+
for (const roleWorker of Object.values(project.workers ?? {})) {
|
|
125513
|
+
for (const slots of Object.values(roleWorker.levels ?? {})) {
|
|
125514
|
+
for (const slot of slots) {
|
|
125515
|
+
if (!slot.active || !slot.issueId) continue;
|
|
125516
|
+
const activeIssueId = Number(slot.issueId);
|
|
125517
|
+
if (!Number.isFinite(activeIssueId) || activeIssueId === currentIssueId) continue;
|
|
125518
|
+
activeIssueIds.add(activeIssueId);
|
|
125519
|
+
}
|
|
125520
|
+
}
|
|
125521
|
+
}
|
|
125522
|
+
return [...activeIssueIds].filter((issueId) => getIssueRuntime(project, issueId)?.parentIssueId === parentIssueId).length;
|
|
125523
|
+
}
|
|
125524
|
+
function getFamilyDispatchBlockReason(project, issueId, role) {
|
|
125525
|
+
const runtime = getIssueRuntime(project, issueId);
|
|
125526
|
+
if (isParentIssue(project, issueId)) {
|
|
125527
|
+
return role === "developer" ? "family_parent_coordinator_only" : "family_parent_not_executable";
|
|
125528
|
+
}
|
|
125529
|
+
if (isChildIssue(project, issueId)) {
|
|
125530
|
+
const parentRuntime = getParentIssueRuntime(project, issueId);
|
|
125531
|
+
if (parentRuntime?.decompositionStatus === "draft") return "family_parent_decomposition_draft";
|
|
125532
|
+
if (parentRuntime?.decompositionStatus === "blocked") return "family_parent_blocked";
|
|
125533
|
+
if (parentRuntime?.decompositionStatus === "completed") return "family_parent_completed";
|
|
125534
|
+
if (!runtime?.parentIssueId) return "family_child_missing_parent_binding";
|
|
125535
|
+
const maxParallelChildren = parentRuntime?.maxParallelChildren ?? null;
|
|
125536
|
+
if (maxParallelChildren && maxParallelChildren > 0) {
|
|
125537
|
+
const activeSiblingChildren = countActiveSiblingChildren(project, runtime.parentIssueId, issueId);
|
|
125538
|
+
if (activeSiblingChildren >= maxParallelChildren) return `family_parallel_limit_reached:${maxParallelChildren}`;
|
|
125539
|
+
}
|
|
125540
|
+
const incompleteDependencies = getDependencyIssueRuntimes(project, issueId).filter((dependency) => !isIssueExecutionComplete(project, dependency.issueId)).map((dependency) => dependency.issueId);
|
|
125541
|
+
if (incompleteDependencies.length > 0) return `family_child_dependencies_pending:${incompleteDependencies.join(",")}`;
|
|
125542
|
+
}
|
|
125543
|
+
return null;
|
|
125544
|
+
}
|
|
125545
|
+
|
|
125546
|
+
// lib/services/queue-scan.ts
|
|
125460
125547
|
init_workflow();
|
|
125461
125548
|
function detectRoleLevelFromLabels(labels) {
|
|
125462
125549
|
for (const label of labels) {
|
|
@@ -125476,19 +125563,144 @@ function detectStepRouting(labels, step) {
|
|
|
125476
125563
|
const match = labels.find((l) => l.toLowerCase().startsWith(prefix));
|
|
125477
125564
|
return match ? match.slice(prefix.length).toLowerCase() : null;
|
|
125478
125565
|
}
|
|
125479
|
-
async function findNextIssueForRole(provider, role, workflow, instanceName) {
|
|
125566
|
+
async function findNextIssueForRole(provider, role, workflow, instanceName, project) {
|
|
125480
125567
|
const labels = getQueueLabels(workflow, role);
|
|
125481
125568
|
for (const label of labels) {
|
|
125482
125569
|
try {
|
|
125483
125570
|
const issues = await provider.listIssuesByLabel(label);
|
|
125484
125571
|
const eligible = instanceName ? issues.filter((i2) => isOwnedByOrUnclaimed(i2.labels, instanceName)) : issues;
|
|
125485
|
-
|
|
125572
|
+
for (let index = eligible.length - 1; index >= 0; index -= 1) {
|
|
125573
|
+
const issue2 = eligible[index];
|
|
125574
|
+
const blockReason = project ? getFamilyDispatchBlockReason(project, issue2.iid, role) : null;
|
|
125575
|
+
if (!blockReason) return { issue: issue2, label };
|
|
125576
|
+
}
|
|
125486
125577
|
} catch {
|
|
125487
125578
|
}
|
|
125488
125579
|
}
|
|
125489
125580
|
return null;
|
|
125490
125581
|
}
|
|
125491
125582
|
|
|
125583
|
+
// lib/services/parent-lifecycle.ts
|
|
125584
|
+
function getTerminalLabel(workflow) {
|
|
125585
|
+
return Object.values(workflow.states).find((state) => state.type === "terminal")?.label ?? null;
|
|
125586
|
+
}
|
|
125587
|
+
function getCurrentWorkflowLabel(issueLabels, workflow) {
|
|
125588
|
+
const labels = new Set(issueLabels);
|
|
125589
|
+
return Object.values(workflow.states).find((state) => labels.has(state.label))?.label ?? null;
|
|
125590
|
+
}
|
|
125591
|
+
function sameIds(a, b) {
|
|
125592
|
+
const left = [...a ?? []].sort((x2, y) => x2 - y);
|
|
125593
|
+
const right = [...b ?? []].sort((x2, y) => x2 - y);
|
|
125594
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
125595
|
+
}
|
|
125596
|
+
function resolveChildRollupState(runtime) {
|
|
125597
|
+
if (!runtime) return "pending_without_runtime";
|
|
125598
|
+
if (runtime.decompositionStatus === "blocked") return "blocked";
|
|
125599
|
+
if (runtime.artifactOfRecord?.mergedAt) return "completed_via_artifact";
|
|
125600
|
+
if (runtime.sessionCompletedAt) return "completed_via_session";
|
|
125601
|
+
if (runtime.currentPrUrl && runtime.currentPrState && runtime.currentPrState !== "merged" && runtime.currentPrState !== "closed") {
|
|
125602
|
+
return "pending_with_open_pr";
|
|
125603
|
+
}
|
|
125604
|
+
if (runtime.currentPrUrl) return "pending_with_pr";
|
|
125605
|
+
return "pending_without_pr";
|
|
125606
|
+
}
|
|
125607
|
+
function formatChildRollupLine(child) {
|
|
125608
|
+
const runtime = child.runtime;
|
|
125609
|
+
const state = resolveChildRollupState(runtime);
|
|
125610
|
+
const prUrl = runtime?.artifactOfRecord?.url ?? runtime?.currentPrUrl ?? null;
|
|
125611
|
+
const mergedAt = runtime?.artifactOfRecord?.mergedAt ?? null;
|
|
125612
|
+
const headSha = runtime?.artifactOfRecord?.headSha ?? runtime?.lastHeadSha ?? null;
|
|
125613
|
+
const extras = [
|
|
125614
|
+
prUrl ? `PR: ${prUrl}` : null,
|
|
125615
|
+
mergedAt ? `mergedAt: ${mergedAt}` : null,
|
|
125616
|
+
headSha ? `headSha: ${headSha}` : null
|
|
125617
|
+
].filter(Boolean);
|
|
125618
|
+
return `- #${child.issueId} \u2014 ${state}${extras.length > 0 ? ` \u2014 ${extras.join(" \u2014 ")}` : ""}`;
|
|
125619
|
+
}
|
|
125620
|
+
var PARENT_ROLLUP_START = "<!-- fabrica:parent-rollup:start -->";
|
|
125621
|
+
var PARENT_ROLLUP_END = "<!-- fabrica:parent-rollup:end -->";
|
|
125622
|
+
function buildParentRollupComment(status, completedChildIds, blockedChildIds, children) {
|
|
125623
|
+
const allChildIds = children.map((child) => child.issueId);
|
|
125624
|
+
const pendingChildIds = allChildIds.filter((id) => !completedChildIds.includes(id) && !blockedChildIds.includes(id));
|
|
125625
|
+
return [
|
|
125626
|
+
"## Parent Rollup",
|
|
125627
|
+
`- Status: ${status}`,
|
|
125628
|
+
`- Completed children (${completedChildIds.length}/${allChildIds.length}): ${completedChildIds.length > 0 ? completedChildIds.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125629
|
+
`- Pending children: ${pendingChildIds.length > 0 ? pendingChildIds.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125630
|
+
`- Blocked children: ${blockedChildIds.length > 0 ? blockedChildIds.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125631
|
+
"",
|
|
125632
|
+
"### Child Status",
|
|
125633
|
+
...children.map((child) => formatChildRollupLine(child))
|
|
125634
|
+
].join("\n");
|
|
125635
|
+
}
|
|
125636
|
+
function upsertParentRollupBlock(body, rollup) {
|
|
125637
|
+
const current = body ?? "";
|
|
125638
|
+
const block = `${PARENT_ROLLUP_START}
|
|
125639
|
+
${rollup}
|
|
125640
|
+
${PARENT_ROLLUP_END}`;
|
|
125641
|
+
const pattern = new RegExp(`${PARENT_ROLLUP_START}[\\s\\S]*?${PARENT_ROLLUP_END}`, "m");
|
|
125642
|
+
if (pattern.test(current)) {
|
|
125643
|
+
return current.replace(pattern, block);
|
|
125644
|
+
}
|
|
125645
|
+
return `${current.trimEnd()}${current.trim().length > 0 ? "\n\n" : ""}${block}`;
|
|
125646
|
+
}
|
|
125647
|
+
async function reconcileParentLifecycleForIssue(opts) {
|
|
125648
|
+
const project = await loadProjectBySlug(opts.workspaceDir, opts.projectSlug);
|
|
125649
|
+
if (!project || !isChildIssue(project, opts.issueId)) return;
|
|
125650
|
+
const childRuntime = getIssueRuntime(project, opts.issueId);
|
|
125651
|
+
const parentRuntime = getParentIssueRuntime(project, opts.issueId);
|
|
125652
|
+
const parentIssueId = childRuntime?.parentIssueId;
|
|
125653
|
+
if (!parentIssueId || !parentRuntime) return;
|
|
125654
|
+
const children = getChildIssueRuntimes(project, parentIssueId);
|
|
125655
|
+
if (children.length === 0) return;
|
|
125656
|
+
const completedChildIds = children.filter((child) => isIssueExecutionComplete(project, child.issueId)).map((child) => child.issueId);
|
|
125657
|
+
const blockedChildIds = children.filter((child) => child.runtime?.decompositionStatus === "blocked").map((child) => child.issueId);
|
|
125658
|
+
const hasBlockedChild = blockedChildIds.length > 0;
|
|
125659
|
+
const allChildrenComplete = completedChildIds.length === children.length;
|
|
125660
|
+
const targetStatus = allChildrenComplete ? "completed" : hasBlockedChild ? "blocked" : "active";
|
|
125661
|
+
const statusChanged = parentRuntime.decompositionStatus !== targetStatus;
|
|
125662
|
+
const completedChanged = !sameIds(parentRuntime.completedChildIssueIds, completedChildIds);
|
|
125663
|
+
const blockedChanged = !sameIds(parentRuntime.blockedChildIssueIds, blockedChildIds);
|
|
125664
|
+
const shouldUpdateRuntime = statusChanged || completedChanged || blockedChanged;
|
|
125665
|
+
const shouldCommentRollup = statusChanged || blockedChanged;
|
|
125666
|
+
if (shouldUpdateRuntime) {
|
|
125667
|
+
await updateIssueRuntime(opts.workspaceDir, opts.projectSlug, parentIssueId, {
|
|
125668
|
+
decompositionStatus: targetStatus,
|
|
125669
|
+
completedChildIssueIds: completedChildIds,
|
|
125670
|
+
blockedChildIssueIds: blockedChildIds,
|
|
125671
|
+
lastParentRollupAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
125672
|
+
}).catch(() => {
|
|
125673
|
+
});
|
|
125674
|
+
}
|
|
125675
|
+
const parentIssue = await opts.provider.getIssue(parentIssueId).catch(() => null);
|
|
125676
|
+
if (!parentIssue) return;
|
|
125677
|
+
const rollup = buildParentRollupComment(targetStatus, completedChildIds, blockedChildIds, children);
|
|
125678
|
+
if (shouldUpdateRuntime) {
|
|
125679
|
+
await opts.provider.editIssue(parentIssueId, {
|
|
125680
|
+
body: upsertParentRollupBlock(parentIssue.description, rollup)
|
|
125681
|
+
}).catch(() => {
|
|
125682
|
+
});
|
|
125683
|
+
if (shouldCommentRollup) {
|
|
125684
|
+
await opts.provider.addComment(parentIssueId, rollup).catch(() => {
|
|
125685
|
+
});
|
|
125686
|
+
}
|
|
125687
|
+
}
|
|
125688
|
+
if (!allChildrenComplete) return;
|
|
125689
|
+
const terminalLabel = getTerminalLabel(opts.workflow);
|
|
125690
|
+
const currentLabel = getCurrentWorkflowLabel(parentIssue.labels, opts.workflow);
|
|
125691
|
+
if (terminalLabel && currentLabel && currentLabel !== terminalLabel && !parentIssue.labels.includes(terminalLabel)) {
|
|
125692
|
+
await opts.provider.transitionLabel(parentIssueId, currentLabel, terminalLabel).catch(() => {
|
|
125693
|
+
});
|
|
125694
|
+
}
|
|
125695
|
+
await opts.provider.addComment(parentIssueId, [
|
|
125696
|
+
"\u2705 Parent coordination complete.",
|
|
125697
|
+
"",
|
|
125698
|
+
"All child issues in this decomposition family have completed execution.",
|
|
125699
|
+
...children.map((child) => formatChildRollupLine(child))
|
|
125700
|
+
].join("\n")).catch(() => {
|
|
125701
|
+
});
|
|
125702
|
+
}
|
|
125703
|
+
|
|
125492
125704
|
// lib/services/pipeline.ts
|
|
125493
125705
|
init_workflow();
|
|
125494
125706
|
init_context3();
|
|
@@ -125765,7 +125977,33 @@ async function executeCompletion(opts) {
|
|
|
125765
125977
|
break;
|
|
125766
125978
|
}
|
|
125767
125979
|
}
|
|
125980
|
+
if (issueRuntime?.parentIssueId) {
|
|
125981
|
+
const childDecompositionStatus = effectiveResult === "blocked" || effectiveResult === "refine" || effectiveResult === "fail_infra" ? "blocked" : effectiveResult === "fail" || effectiveResult === "reject" ? "active" : issueRuntime.decompositionStatus;
|
|
125982
|
+
if (childDecompositionStatus !== issueRuntime.decompositionStatus) {
|
|
125983
|
+
await updateIssueRuntime(workspaceDir, projectSlug, issueId, {
|
|
125984
|
+
decompositionStatus: childDecompositionStatus
|
|
125985
|
+
}).catch(() => {
|
|
125986
|
+
});
|
|
125987
|
+
}
|
|
125988
|
+
}
|
|
125989
|
+
if (issueRuntime?.parentIssueId) {
|
|
125990
|
+
const childDecompositionStatus = effectiveResult === "blocked" || effectiveResult === "refine" || effectiveResult === "fail_infra" ? "blocked" : effectiveResult === "fail" || effectiveResult === "reject" ? "active" : issueRuntime.decompositionStatus;
|
|
125991
|
+
if (childDecompositionStatus !== issueRuntime.decompositionStatus) {
|
|
125992
|
+
await updateIssueRuntime(workspaceDir, projectSlug, issueId, {
|
|
125993
|
+
decompositionStatus: childDecompositionStatus
|
|
125994
|
+
}).catch(() => {
|
|
125995
|
+
});
|
|
125996
|
+
}
|
|
125997
|
+
}
|
|
125768
125998
|
await deactivateWorker(workspaceDir, projectSlug, role, { level: opts.level, slotIndex: opts.slotIndex, issueId: String(issueId) });
|
|
125999
|
+
await reconcileParentLifecycleForIssue({
|
|
126000
|
+
workspaceDir,
|
|
126001
|
+
projectSlug,
|
|
126002
|
+
issueId,
|
|
126003
|
+
provider,
|
|
126004
|
+
workflow
|
|
126005
|
+
}).catch(() => {
|
|
126006
|
+
});
|
|
125769
126007
|
notify(
|
|
125770
126008
|
{
|
|
125771
126009
|
type: "workerComplete",
|
|
@@ -126138,7 +126376,7 @@ function validateCanonicalQaEvidence(body) {
|
|
|
126138
126376
|
}
|
|
126139
126377
|
function formatQaEvidenceValidationFailure(validation, actor) {
|
|
126140
126378
|
const intro = actor === "developer" ? "Cannot mark work_finish(done) with invalid QA Evidence in the PR body." : "Cannot approve review with invalid QA Evidence in the PR body.";
|
|
126141
|
-
const guidance = actor === "developer" ? 'Replace the existing "## QA Evidence" section with fresh sanitized output from scripts/qa.sh (exactly one section, Exit code: 0), then call work_finish again.' : 'Reject the PR and instruct the developer to replace the existing "## QA Evidence" section in the PR body with fresh sanitized output from scripts/qa.sh (exactly one section, Exit code: 0).';
|
|
126379
|
+
const guidance = actor === "developer" ? 'Replace the existing "## QA Evidence" section with fresh sanitized output from scripts/qa.sh (exactly one section, Exit code: 0), then call work_finish again. Do not rewrite or weaken scripts/qa.sh into ad-hoc scenario checks \u2014 preserve the canonical lint/types/security/tests/coverage gates and fix the underlying code or project setup instead.' : 'Reject the PR and instruct the developer to replace the existing "## QA Evidence" section in the PR body with fresh sanitized output from scripts/qa.sh (exactly one section, Exit code: 0). Do not accept ad-hoc scenario scripts or weakened QA gates in place of the canonical lint/types/security/tests/coverage contract.';
|
|
126142
126380
|
const allIssues = [...validation.problems, ...validation.errors];
|
|
126143
126381
|
return `${intro}
|
|
126144
126382
|
|
|
@@ -126826,13 +127064,17 @@ function createTaskCreateTool(ctx) {
|
|
|
126826
127064
|
});
|
|
126827
127065
|
if (parentIssueId) {
|
|
126828
127066
|
await updateIssueRuntime(workspaceDir, project.slug, issue2.iid, {
|
|
126829
|
-
parentIssueId
|
|
127067
|
+
parentIssueId,
|
|
127068
|
+
decompositionMode: "none",
|
|
127069
|
+
decompositionStatus: null
|
|
126830
127070
|
}).catch(() => {
|
|
126831
127071
|
});
|
|
126832
127072
|
const parentRuntime = project.issueRuntime?.[String(parentIssueId)] ?? {};
|
|
126833
127073
|
const previousChildren = Array.isArray(parentRuntime.childIssueIds) ? parentRuntime.childIssueIds : [];
|
|
126834
127074
|
await updateIssueRuntime(workspaceDir, project.slug, parentIssueId, {
|
|
126835
|
-
childIssueIds: [.../* @__PURE__ */ new Set([...previousChildren, issue2.iid])]
|
|
127075
|
+
childIssueIds: [.../* @__PURE__ */ new Set([...previousChildren, issue2.iid])],
|
|
127076
|
+
decompositionMode: "parent_child",
|
|
127077
|
+
decompositionStatus: "active"
|
|
126836
127078
|
}).catch(() => {
|
|
126837
127079
|
});
|
|
126838
127080
|
provider.addComment(
|
|
@@ -127716,6 +127958,7 @@ function createTaskOwnerTool(ctx) {
|
|
|
127716
127958
|
|
|
127717
127959
|
// lib/dispatch/index.ts
|
|
127718
127960
|
init_audit();
|
|
127961
|
+
import fs22 from "node:fs/promises";
|
|
127719
127962
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
127720
127963
|
init_roles();
|
|
127721
127964
|
init_workflow();
|
|
@@ -128136,6 +128379,13 @@ init_roles();
|
|
|
128136
128379
|
function toBranchSlug(value) {
|
|
128137
128380
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "task";
|
|
128138
128381
|
}
|
|
128382
|
+
function resolveExecutionSetup(opts) {
|
|
128383
|
+
const branchName = opts.prBranchName?.trim() ? opts.prBranchName.trim() : `feature/${opts.issueId}-${toBranchSlug(opts.projectName)}`;
|
|
128384
|
+
return {
|
|
128385
|
+
branchName,
|
|
128386
|
+
worktreePath: `${opts.repo}.worktrees/${branchName}`
|
|
128387
|
+
};
|
|
128388
|
+
}
|
|
128139
128389
|
function buildTaskMessage(opts) {
|
|
128140
128390
|
const {
|
|
128141
128391
|
projectName,
|
|
@@ -128150,8 +128400,12 @@ function buildTaskMessage(opts) {
|
|
|
128150
128400
|
} = opts;
|
|
128151
128401
|
const repoDisplay = repo;
|
|
128152
128402
|
const isFeedbackCycle = !!opts.prFeedback;
|
|
128153
|
-
const branchName
|
|
128154
|
-
|
|
128403
|
+
const { branchName, worktreePath } = resolveExecutionSetup({
|
|
128404
|
+
projectName,
|
|
128405
|
+
issueId,
|
|
128406
|
+
repo: repoDisplay,
|
|
128407
|
+
prBranchName: opts.prFeedback?.branchName
|
|
128408
|
+
});
|
|
128155
128409
|
const parts = [
|
|
128156
128410
|
`${role.toUpperCase()} task for project "${projectName}" \u2014 Issue #${issueId}`,
|
|
128157
128411
|
``,
|
|
@@ -128172,10 +128426,11 @@ ${issueDescription}` : ""
|
|
|
128172
128426
|
``,
|
|
128173
128427
|
`## Execution Setup (do this before editing files)`,
|
|
128174
128428
|
`Repo: ${repoDisplay} | Base branch: ${baseBranch} | ${issueUrl}`,
|
|
128175
|
-
`Execution path: ${
|
|
128429
|
+
`Execution path: ${worktreePath}`,
|
|
128430
|
+
`Main checkout: ${repoDisplay}`,
|
|
128176
128431
|
`Required branch: ${branchName}`,
|
|
128177
128432
|
`Required worktree: ${worktreePath}`,
|
|
128178
|
-
`Before editing any file,
|
|
128433
|
+
`Before editing any file, change into the execution path above and work only there.`,
|
|
128179
128434
|
`Do not re-initialize or replace the scaffold in the main checkout (for example: do not run npm init, cargo init, uv init, or create a second project skeleton) when the repo already contains scaffolded files. Modify the existing scaffold inside the worktree instead.`,
|
|
128180
128435
|
`If the repo path is missing or inaccessible, return the canonical blocked result instead of improvising in ~/.openclaw/workspace.`
|
|
128181
128436
|
);
|
|
@@ -128242,8 +128497,12 @@ function buildConflictFixMessage(opts) {
|
|
|
128242
128497
|
prFeedback
|
|
128243
128498
|
} = opts;
|
|
128244
128499
|
const repoDisplay = repo;
|
|
128245
|
-
const branchName
|
|
128246
|
-
|
|
128500
|
+
const { branchName, worktreePath } = resolveExecutionSetup({
|
|
128501
|
+
projectName,
|
|
128502
|
+
issueId,
|
|
128503
|
+
repo: repoDisplay,
|
|
128504
|
+
prBranchName: prFeedback.branchName
|
|
128505
|
+
});
|
|
128247
128506
|
const parts = [
|
|
128248
128507
|
`${role.toUpperCase()} task for project "${projectName}" \u2014 Issue #${issueId}`,
|
|
128249
128508
|
``,
|
|
@@ -128256,10 +128515,11 @@ function buildConflictFixMessage(opts) {
|
|
|
128256
128515
|
``,
|
|
128257
128516
|
`Repo: ${repoDisplay} | Base branch: ${baseBranch} | ${issueUrl}`,
|
|
128258
128517
|
`Project: ${projectName} | Channel: ${channelId}`,
|
|
128259
|
-
`Execution path: ${
|
|
128518
|
+
`Execution path: ${worktreePath}`,
|
|
128519
|
+
`Main checkout: ${repoDisplay}`,
|
|
128260
128520
|
`Required branch: ${branchName}`,
|
|
128261
128521
|
`Required worktree: ${worktreePath}`,
|
|
128262
|
-
`Start by changing into the
|
|
128522
|
+
`Start by changing into the execution path above before reusing the PR branch. Reuse the exact PR branch named above; do not switch to a new canonical issue branch during a feedback cycle. Do not resolve the issue inside ~/.openclaw/workspace unless the repo path itself points there.`
|
|
128263
128523
|
);
|
|
128264
128524
|
parts.push(...buildCompletionContract(role));
|
|
128265
128525
|
return parts.join("\n");
|
|
@@ -128592,23 +128852,24 @@ ${roleInstructions}`;
|
|
|
128592
128852
|
}
|
|
128593
128853
|
return effortPrefix ?? roleInstructions ?? "";
|
|
128594
128854
|
}
|
|
128595
|
-
function sendToAgent(sessionKey, taskMessage, opts) {
|
|
128855
|
+
async function sendToAgent(sessionKey, taskMessage, opts) {
|
|
128596
128856
|
const epoch = opts.dispatchEpoch ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
128597
128857
|
const idempotencyKey = `fabrica-${opts.projectName}-${opts.issueId}-${opts.role}-${opts.level ?? "unknown"}-${opts.slotIndex ?? 0}-${opts.fromLabel ?? "unknown"}-${sessionKey}-${epoch}`;
|
|
128598
128858
|
if (opts.runtime?.subagent?.run) {
|
|
128599
|
-
|
|
128600
|
-
|
|
128601
|
-
|
|
128602
|
-
|
|
128603
|
-
|
|
128604
|
-
|
|
128605
|
-
|
|
128606
|
-
|
|
128859
|
+
try {
|
|
128860
|
+
const result = await opts.runtime.subagent.run({
|
|
128861
|
+
sessionKey,
|
|
128862
|
+
message: taskMessage,
|
|
128863
|
+
idempotencyKey,
|
|
128864
|
+
lane: "subagent",
|
|
128865
|
+
deliver: false,
|
|
128866
|
+
...opts.extraSystemPrompt ? { extraSystemPrompt: opts.extraSystemPrompt } : {}
|
|
128867
|
+
});
|
|
128607
128868
|
if (result?.runId) {
|
|
128608
|
-
bindDispatchRunIdBySessionKey(opts.workspaceDir, sessionKey, result.runId).catch(() => {
|
|
128869
|
+
await bindDispatchRunIdBySessionKey(opts.workspaceDir, sessionKey, result.runId).catch(() => {
|
|
128609
128870
|
});
|
|
128610
128871
|
}
|
|
128611
|
-
log(opts.workspaceDir, "dispatch_agent_sent", {
|
|
128872
|
+
await log(opts.workspaceDir, "dispatch_agent_sent", {
|
|
128612
128873
|
step: "sendToAgent",
|
|
128613
128874
|
sessionKey,
|
|
128614
128875
|
issue: opts.issueId,
|
|
@@ -128617,7 +128878,7 @@ function sendToAgent(sessionKey, taskMessage, opts) {
|
|
|
128617
128878
|
runId: result?.runId ?? null
|
|
128618
128879
|
}).catch(() => {
|
|
128619
128880
|
});
|
|
128620
|
-
recordIssueLifecycle({
|
|
128881
|
+
await recordIssueLifecycle({
|
|
128621
128882
|
workspaceDir: opts.workspaceDir,
|
|
128622
128883
|
slug: opts.projectSlug ?? opts.projectName,
|
|
128623
128884
|
issueId: opts.issueId,
|
|
@@ -128626,18 +128887,23 @@ function sendToAgent(sessionKey, taskMessage, opts) {
|
|
|
128626
128887
|
details: { method: "runtime.subagent.run" }
|
|
128627
128888
|
}).catch(() => {
|
|
128628
128889
|
});
|
|
128629
|
-
|
|
128890
|
+
return { method: "runtime.subagent.run", runId: result?.runId ?? null };
|
|
128891
|
+
} catch (err) {
|
|
128892
|
+
const errorMessage = err.message ?? String(err);
|
|
128630
128893
|
log(opts.workspaceDir, "dispatch_warning", {
|
|
128631
128894
|
step: "sendToAgent",
|
|
128632
128895
|
sessionKey,
|
|
128633
128896
|
issue: opts.issueId,
|
|
128634
128897
|
role: opts.role,
|
|
128635
128898
|
method: "runtime.subagent.run",
|
|
128636
|
-
error:
|
|
128899
|
+
error: errorMessage
|
|
128637
128900
|
}).catch(() => {
|
|
128638
128901
|
});
|
|
128639
|
-
|
|
128640
|
-
|
|
128902
|
+
if (/only available during a gateway request/i.test(errorMessage)) {
|
|
128903
|
+
} else {
|
|
128904
|
+
throw err;
|
|
128905
|
+
}
|
|
128906
|
+
}
|
|
128641
128907
|
}
|
|
128642
128908
|
const rc = opts.runCommand;
|
|
128643
128909
|
const gatewayParams = JSON.stringify({
|
|
@@ -128650,11 +128916,45 @@ function sendToAgent(sessionKey, taskMessage, opts) {
|
|
|
128650
128916
|
...opts.orchestratorSessionKey ? { spawnedBy: opts.orchestratorSessionKey } : {},
|
|
128651
128917
|
...opts.extraSystemPrompt ? { extraSystemPrompt: opts.extraSystemPrompt } : {}
|
|
128652
128918
|
});
|
|
128653
|
-
|
|
128654
|
-
|
|
128655
|
-
|
|
128656
|
-
|
|
128657
|
-
|
|
128919
|
+
try {
|
|
128920
|
+
const response = await rc(
|
|
128921
|
+
["openclaw", "gateway", "call", "agent", "--params", gatewayParams, "--expect-final", "--json"],
|
|
128922
|
+
{ timeoutMs: opts.dispatchTimeoutMs ?? 6e4 }
|
|
128923
|
+
);
|
|
128924
|
+
const stdout = String(response.stdout ?? "").trim();
|
|
128925
|
+
let runId = null;
|
|
128926
|
+
if (stdout) {
|
|
128927
|
+
try {
|
|
128928
|
+
const parsed = JSON.parse(stdout);
|
|
128929
|
+
runId = parsed.runId ?? parsed.result?.runId ?? null;
|
|
128930
|
+
} catch {
|
|
128931
|
+
}
|
|
128932
|
+
}
|
|
128933
|
+
if (runId) {
|
|
128934
|
+
await bindDispatchRunIdBySessionKey(opts.workspaceDir, sessionKey, runId).catch(() => {
|
|
128935
|
+
});
|
|
128936
|
+
}
|
|
128937
|
+
await log(opts.workspaceDir, "dispatch_agent_sent", {
|
|
128938
|
+
step: "sendToAgent",
|
|
128939
|
+
sessionKey,
|
|
128940
|
+
issue: opts.issueId,
|
|
128941
|
+
role: opts.role,
|
|
128942
|
+
method: "subprocess_fallback",
|
|
128943
|
+
runId
|
|
128944
|
+
}).catch(() => {
|
|
128945
|
+
});
|
|
128946
|
+
await recordIssueLifecycle({
|
|
128947
|
+
workspaceDir: opts.workspaceDir,
|
|
128948
|
+
slug: opts.projectSlug ?? opts.projectName,
|
|
128949
|
+
issueId: opts.issueId,
|
|
128950
|
+
stage: "agent_accepted",
|
|
128951
|
+
sessionKey,
|
|
128952
|
+
details: { method: "subprocess_fallback", runId }
|
|
128953
|
+
}).catch(() => {
|
|
128954
|
+
});
|
|
128955
|
+
return { method: "subprocess_fallback", runId };
|
|
128956
|
+
} catch (err) {
|
|
128957
|
+
await log(opts.workspaceDir, "dispatch_warning", {
|
|
128658
128958
|
step: "sendToAgent",
|
|
128659
128959
|
sessionKey,
|
|
128660
128960
|
issue: opts.issueId,
|
|
@@ -128663,7 +128963,8 @@ function sendToAgent(sessionKey, taskMessage, opts) {
|
|
|
128663
128963
|
error: err.message ?? String(err)
|
|
128664
128964
|
}).catch(() => {
|
|
128665
128965
|
});
|
|
128666
|
-
|
|
128966
|
+
throw err;
|
|
128967
|
+
}
|
|
128667
128968
|
}
|
|
128668
128969
|
function normalizeGatewaySessionLabel(label, maxLength = GATEWAY_SESSION_LABEL_MAX) {
|
|
128669
128970
|
if (!label) return { truncated: false };
|
|
@@ -128784,7 +129085,8 @@ async function dispatchTask(opts) {
|
|
|
128784
129085
|
existingSessionKey = null;
|
|
128785
129086
|
}
|
|
128786
129087
|
}
|
|
128787
|
-
|
|
129088
|
+
const feedbackFreshSession = !!(existingSessionKey && role === "developer" && isFeedbackState(resolvedConfig.workflow, fromLabel));
|
|
129089
|
+
if (feedbackFreshSession) {
|
|
128788
129090
|
await rc(
|
|
128789
129091
|
["openclaw", "gateway", "call", "sessions.delete", "--params", JSON.stringify({ key: existingSessionKey })],
|
|
128790
129092
|
{ timeoutMs: 1e4 }
|
|
@@ -128818,8 +129120,10 @@ async function dispatchTask(opts) {
|
|
|
128818
129120
|
existingSessionKey = null;
|
|
128819
129121
|
}
|
|
128820
129122
|
}
|
|
129123
|
+
const dispatchCycleId = randomUUID2();
|
|
128821
129124
|
const botName = slotName(project.name, role, level, slotIndex);
|
|
128822
|
-
const
|
|
129125
|
+
const deterministicSessionKey = `agent:${agentId ?? "unknown"}:subagent:${project.name}-${role}-${level}-${botName.toLowerCase()}`;
|
|
129126
|
+
const sessionKey = feedbackFreshSession ? `${deterministicSessionKey}-retry-${dispatchCycleId.slice(0, 8)}` : deterministicSessionKey;
|
|
128823
129127
|
if (existingSessionKey && existingSessionKey !== sessionKey) {
|
|
128824
129128
|
const existingKeyIsForSameRole = existingSessionKey.includes(`:${project.name}-${role}-`);
|
|
128825
129129
|
if (existingKeyIsForSameRole) {
|
|
@@ -128830,8 +129134,27 @@ async function dispatchTask(opts) {
|
|
|
128830
129134
|
existingSessionKey = null;
|
|
128831
129135
|
}
|
|
128832
129136
|
}
|
|
129137
|
+
if (!existingSessionKey) {
|
|
129138
|
+
const gatewaySessions = await fetchGatewaySessions(void 0, rc).catch(() => null);
|
|
129139
|
+
if (gatewaySessions && isSessionAlive(sessionKey, gatewaySessions)) {
|
|
129140
|
+
await rc(
|
|
129141
|
+
["openclaw", "gateway", "call", "sessions.delete", "--params", JSON.stringify({ key: sessionKey })],
|
|
129142
|
+
{ timeoutMs: 1e4 }
|
|
129143
|
+
).catch((err) => console.error("[fabrica] silent-catch:", err.message));
|
|
129144
|
+
await log(workspaceDir, "session_orphan_reset", {
|
|
129145
|
+
project: project.name,
|
|
129146
|
+
projectSlug: project.slug,
|
|
129147
|
+
issue: issueId,
|
|
129148
|
+
role,
|
|
129149
|
+
level,
|
|
129150
|
+
sessionKey,
|
|
129151
|
+
reason: feedbackFreshSession ? "gateway_session_alive_without_local_slot_tracking_after_feedback_reset" : "gateway_session_alive_without_local_slot_tracking"
|
|
129152
|
+
}).catch(() => {
|
|
129153
|
+
});
|
|
129154
|
+
}
|
|
129155
|
+
}
|
|
128833
129156
|
const sessionAction = existingSessionKey ? "send" : "spawn";
|
|
128834
|
-
const
|
|
129157
|
+
const dispatchSemantic = feedbackFreshSession ? "feedback_redispatch" : sessionAction === "send" ? "session_resume" : "fresh_dispatch";
|
|
128835
129158
|
const allComments = await provider.listComments(issueId);
|
|
128836
129159
|
const { workflow } = resolvedConfig;
|
|
128837
129160
|
const prFeedback = isFeedbackState(workflow, fromLabel) ? await fetchPrFeedback(provider, issueId, prSelector) : void 0;
|
|
@@ -128850,6 +129173,60 @@ async function dispatchTask(opts) {
|
|
|
128850
129173
|
const primaryChannelId = project.slug;
|
|
128851
129174
|
const isConflictFix = prFeedback?.reason === "merge_conflict";
|
|
128852
129175
|
const repoContext = project.repo ? resolveRepoPath(project.repo) : project.repoRemote?.replace(/\.git$/, "") || project.slug;
|
|
129176
|
+
const executionSetup = resolveExecutionSetup({
|
|
129177
|
+
projectName: project.name,
|
|
129178
|
+
issueId,
|
|
129179
|
+
repo: repoContext,
|
|
129180
|
+
prBranchName: prFeedback?.branchName
|
|
129181
|
+
});
|
|
129182
|
+
const localRepoPath = project.repo ? resolveRepoPath(project.repo) : null;
|
|
129183
|
+
if (localRepoPath) {
|
|
129184
|
+
const remoteStartRef = prFeedback?.branchName?.trim() ? `origin/${executionSetup.branchName}` : `origin/${project.baseBranch}`;
|
|
129185
|
+
await fs22.mkdir(`${localRepoPath}.worktrees`, { recursive: true }).catch(() => {
|
|
129186
|
+
});
|
|
129187
|
+
const worktreeExists = await fs22.stat(executionSetup.worktreePath).then(() => true).catch(() => false);
|
|
129188
|
+
if (!worktreeExists) {
|
|
129189
|
+
await rc(["git", "fetch", "origin", project.baseBranch], { timeoutMs: 3e4, cwd: localRepoPath }).catch(() => {
|
|
129190
|
+
});
|
|
129191
|
+
if (prFeedback?.branchName?.trim()) {
|
|
129192
|
+
await rc(["git", "fetch", "origin", executionSetup.branchName], { timeoutMs: 3e4, cwd: localRepoPath }).catch(() => {
|
|
129193
|
+
});
|
|
129194
|
+
}
|
|
129195
|
+
await rc(
|
|
129196
|
+
["git", "worktree", "add", executionSetup.worktreePath, "-B", executionSetup.branchName, remoteStartRef],
|
|
129197
|
+
{ timeoutMs: 6e4, cwd: localRepoPath }
|
|
129198
|
+
);
|
|
129199
|
+
await log(workspaceDir, "dispatch_worktree_prepared", {
|
|
129200
|
+
project: project.name,
|
|
129201
|
+
projectSlug: project.slug,
|
|
129202
|
+
issue: issueId,
|
|
129203
|
+
role,
|
|
129204
|
+
level,
|
|
129205
|
+
branchName: executionSetup.branchName,
|
|
129206
|
+
worktreePath: executionSetup.worktreePath,
|
|
129207
|
+
mode: "create"
|
|
129208
|
+
}).catch(() => {
|
|
129209
|
+
});
|
|
129210
|
+
} else {
|
|
129211
|
+
await rc(["git", "checkout", executionSetup.branchName], { timeoutMs: 3e4, cwd: executionSetup.worktreePath }).catch(() => {
|
|
129212
|
+
});
|
|
129213
|
+
await rc(["git", "reset", "--hard", remoteStartRef], { timeoutMs: 3e4, cwd: executionSetup.worktreePath }).catch(() => {
|
|
129214
|
+
});
|
|
129215
|
+
await rc(["git", "clean", "-fd"], { timeoutMs: 3e4, cwd: executionSetup.worktreePath }).catch(() => {
|
|
129216
|
+
});
|
|
129217
|
+
await log(workspaceDir, "dispatch_worktree_prepared", {
|
|
129218
|
+
project: project.name,
|
|
129219
|
+
projectSlug: project.slug,
|
|
129220
|
+
issue: issueId,
|
|
129221
|
+
role,
|
|
129222
|
+
level,
|
|
129223
|
+
branchName: executionSetup.branchName,
|
|
129224
|
+
worktreePath: executionSetup.worktreePath,
|
|
129225
|
+
mode: "reuse"
|
|
129226
|
+
}).catch(() => {
|
|
129227
|
+
});
|
|
129228
|
+
}
|
|
129229
|
+
}
|
|
128853
129230
|
const taskMessage = isConflictFix && prFeedback ? buildConflictFixMessage({
|
|
128854
129231
|
projectName: project.name,
|
|
128855
129232
|
channelId: primaryChannelId,
|
|
@@ -128993,6 +129370,62 @@ async function dispatchTask(opts) {
|
|
|
128993
129370
|
}
|
|
128994
129371
|
} catch {
|
|
128995
129372
|
}
|
|
129373
|
+
const dispatchEpoch = (/* @__PURE__ */ new Date()).toISOString();
|
|
129374
|
+
let sendResult;
|
|
129375
|
+
try {
|
|
129376
|
+
sendResult = await sendToAgent(sessionKey, taskMessage, {
|
|
129377
|
+
agentId,
|
|
129378
|
+
projectName: project.name,
|
|
129379
|
+
projectSlug: project.slug,
|
|
129380
|
+
issueId,
|
|
129381
|
+
role,
|
|
129382
|
+
level,
|
|
129383
|
+
slotIndex,
|
|
129384
|
+
fromLabel,
|
|
129385
|
+
orchestratorSessionKey: opts.sessionKey,
|
|
129386
|
+
workspaceDir,
|
|
129387
|
+
dispatchTimeoutMs: timeouts.dispatchMs,
|
|
129388
|
+
extraSystemPrompt: buildEffortPrompt(
|
|
129389
|
+
resolvedRole?.effort?.[level],
|
|
129390
|
+
roleInstructions.trim() || void 0
|
|
129391
|
+
) || void 0,
|
|
129392
|
+
runCommand: rc,
|
|
129393
|
+
runtime,
|
|
129394
|
+
dispatchEpoch
|
|
129395
|
+
});
|
|
129396
|
+
} catch (err) {
|
|
129397
|
+
try {
|
|
129398
|
+
await resilientLabelTransition(
|
|
129399
|
+
provider,
|
|
129400
|
+
issueId,
|
|
129401
|
+
toLabel,
|
|
129402
|
+
fromLabel,
|
|
129403
|
+
(msg) => log(workspaceDir, "dispatch_warning", { step: "send_rollback_label", issue: issueId, msg }).catch(() => {
|
|
129404
|
+
})
|
|
129405
|
+
);
|
|
129406
|
+
await deactivateWorker(workspaceDir, project.slug, role, { level, slotIndex, issueId: String(issueId) });
|
|
129407
|
+
} catch {
|
|
129408
|
+
}
|
|
129409
|
+
throw new Error(`Dispatch send failed for issue #${issueId}: ${err.message ?? String(err)}`);
|
|
129410
|
+
}
|
|
129411
|
+
await recordIssueLifecycle({
|
|
129412
|
+
workspaceDir,
|
|
129413
|
+
slug: project.slug,
|
|
129414
|
+
issueId,
|
|
129415
|
+
stage: "dispatch_requested",
|
|
129416
|
+
sessionKey,
|
|
129417
|
+
details: { role, level, slotIndex, sessionAction }
|
|
129418
|
+
}).catch((err) => {
|
|
129419
|
+
log(workspaceDir, "dispatch_warning", {
|
|
129420
|
+
project: project.name,
|
|
129421
|
+
issue: issueId,
|
|
129422
|
+
role,
|
|
129423
|
+
warning: "Lifecycle event failed after successful dispatch",
|
|
129424
|
+
error: err.message,
|
|
129425
|
+
sessionKey
|
|
129426
|
+
}).catch(() => {
|
|
129427
|
+
});
|
|
129428
|
+
});
|
|
128996
129429
|
const notifyConfig = getNotificationConfig(pluginConfig);
|
|
128997
129430
|
const notifyTarget = resolveNotifyChannel(issue2?.labels ?? [], project.channels);
|
|
128998
129431
|
notify(
|
|
@@ -129006,11 +129439,12 @@ async function dispatchTask(opts) {
|
|
|
129006
129439
|
level,
|
|
129007
129440
|
name: botName,
|
|
129008
129441
|
sessionAction,
|
|
129442
|
+
dispatchSemantic,
|
|
129009
129443
|
modelDowngraded: effectiveModel.downgraded,
|
|
129010
129444
|
originalModel: effectiveModel.downgraded ? resolvedModel : void 0,
|
|
129011
129445
|
effectiveModel: effectiveModel.downgraded ? model : void 0,
|
|
129012
129446
|
dispatchCycleId,
|
|
129013
|
-
dispatchRunId:
|
|
129447
|
+
dispatchRunId: sendResult.runId
|
|
129014
129448
|
},
|
|
129015
129449
|
{
|
|
129016
129450
|
workspaceDir,
|
|
@@ -129030,45 +129464,6 @@ async function dispatchTask(opts) {
|
|
|
129030
129464
|
error: err.message ?? String(err)
|
|
129031
129465
|
}).catch((auditErr) => console.error("[fabrica] silent-catch:", auditErr.message));
|
|
129032
129466
|
});
|
|
129033
|
-
const dispatchEpoch = (/* @__PURE__ */ new Date()).toISOString();
|
|
129034
|
-
sendToAgent(sessionKey, taskMessage, {
|
|
129035
|
-
agentId,
|
|
129036
|
-
projectName: project.name,
|
|
129037
|
-
projectSlug: project.slug,
|
|
129038
|
-
issueId,
|
|
129039
|
-
role,
|
|
129040
|
-
level,
|
|
129041
|
-
slotIndex,
|
|
129042
|
-
fromLabel,
|
|
129043
|
-
orchestratorSessionKey: opts.sessionKey,
|
|
129044
|
-
workspaceDir,
|
|
129045
|
-
dispatchTimeoutMs: timeouts.dispatchMs,
|
|
129046
|
-
extraSystemPrompt: buildEffortPrompt(
|
|
129047
|
-
resolvedRole?.effort?.[level],
|
|
129048
|
-
roleInstructions.trim() || void 0
|
|
129049
|
-
) || void 0,
|
|
129050
|
-
runCommand: rc,
|
|
129051
|
-
runtime,
|
|
129052
|
-
dispatchEpoch
|
|
129053
|
-
});
|
|
129054
|
-
await recordIssueLifecycle({
|
|
129055
|
-
workspaceDir,
|
|
129056
|
-
slug: project.slug,
|
|
129057
|
-
issueId,
|
|
129058
|
-
stage: "dispatch_requested",
|
|
129059
|
-
sessionKey,
|
|
129060
|
-
details: { role, level, slotIndex, sessionAction }
|
|
129061
|
-
}).catch((err) => {
|
|
129062
|
-
log(workspaceDir, "dispatch_warning", {
|
|
129063
|
-
project: project.name,
|
|
129064
|
-
issue: issueId,
|
|
129065
|
-
role,
|
|
129066
|
-
warning: "Lifecycle event failed after successful dispatch",
|
|
129067
|
-
error: err.message,
|
|
129068
|
-
sessionKey
|
|
129069
|
-
}).catch(() => {
|
|
129070
|
-
});
|
|
129071
|
-
});
|
|
129072
129467
|
await auditDispatch(workspaceDir, {
|
|
129073
129468
|
project: project.name,
|
|
129074
129469
|
issueId,
|
|
@@ -129786,7 +130181,7 @@ function createProjectStatusTool(ctx) {
|
|
|
129786
130181
|
}
|
|
129787
130182
|
|
|
129788
130183
|
// lib/tools/admin/project-register.ts
|
|
129789
|
-
import
|
|
130184
|
+
import fs24 from "node:fs/promises";
|
|
129790
130185
|
import path24 from "node:path";
|
|
129791
130186
|
init_audit();
|
|
129792
130187
|
init_roles();
|
|
@@ -129794,7 +130189,7 @@ init_workflow();
|
|
|
129794
130189
|
init_constants();
|
|
129795
130190
|
|
|
129796
130191
|
// lib/telegram/topic-service.ts
|
|
129797
|
-
import
|
|
130192
|
+
import fs23 from "node:fs/promises";
|
|
129798
130193
|
async function sleep2(ms) {
|
|
129799
130194
|
await new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
129800
130195
|
}
|
|
@@ -129824,7 +130219,7 @@ async function tryReadTokenFile(filePath) {
|
|
|
129824
130219
|
const resolvedPath = readString(filePath);
|
|
129825
130220
|
if (!resolvedPath) return void 0;
|
|
129826
130221
|
try {
|
|
129827
|
-
const token = await
|
|
130222
|
+
const token = await fs23.readFile(resolvedPath, "utf-8");
|
|
129828
130223
|
const trimmed = token.trim();
|
|
129829
130224
|
return trimmed || void 0;
|
|
129830
130225
|
} catch {
|
|
@@ -129985,14 +130380,14 @@ function buildForumTopicArtifactId(channelId, messageThreadId) {
|
|
|
129985
130380
|
async function scaffoldPromptFiles(workspaceDir, projectName) {
|
|
129986
130381
|
const projectDir = path24.join(workspaceDir, DATA_DIR, "projects", projectName);
|
|
129987
130382
|
const promptsDir = path24.join(projectDir, "prompts");
|
|
129988
|
-
await
|
|
130383
|
+
await fs24.mkdir(promptsDir, { recursive: true });
|
|
129989
130384
|
const readmePath = path24.join(projectDir, "README.md");
|
|
129990
130385
|
try {
|
|
129991
|
-
await
|
|
130386
|
+
await fs24.access(readmePath);
|
|
129992
130387
|
return false;
|
|
129993
130388
|
} catch {
|
|
129994
130389
|
const roles = getAllRoleIds().join(", ");
|
|
129995
|
-
await
|
|
130390
|
+
await fs24.writeFile(readmePath, `# Project Overrides
|
|
129996
130391
|
|
|
129997
130392
|
This directory holds project-specific configuration that overrides the workspace defaults.
|
|
129998
130393
|
|
|
@@ -130048,12 +130443,12 @@ function getProjectReadmePath(workspaceDir, projectName) {
|
|
|
130048
130443
|
async function ensureAutonomousWorkflowOverride(workspaceDir, projectName, reviewPolicy = "agent") {
|
|
130049
130444
|
const projectDir = getProjectDir(workspaceDir, projectName);
|
|
130050
130445
|
const workflowPath = getProjectWorkflowPath(workspaceDir, projectName);
|
|
130051
|
-
await
|
|
130446
|
+
await fs24.mkdir(projectDir, { recursive: true });
|
|
130052
130447
|
try {
|
|
130053
|
-
await
|
|
130448
|
+
await fs24.access(workflowPath);
|
|
130054
130449
|
return false;
|
|
130055
130450
|
} catch {
|
|
130056
|
-
await
|
|
130451
|
+
await fs24.writeFile(workflowPath, `workflow:
|
|
130057
130452
|
reviewPolicy: ${reviewPolicy}
|
|
130058
130453
|
`, "utf-8");
|
|
130059
130454
|
return true;
|
|
@@ -130062,7 +130457,7 @@ async function ensureAutonomousWorkflowOverride(workspaceDir, projectName, revie
|
|
|
130062
130457
|
async function hasProjectWorkflowOverride(workspaceDir, projectName) {
|
|
130063
130458
|
const workflowPath = getProjectWorkflowPath(workspaceDir, projectName);
|
|
130064
130459
|
try {
|
|
130065
|
-
await
|
|
130460
|
+
await fs24.access(workflowPath);
|
|
130066
130461
|
return true;
|
|
130067
130462
|
} catch {
|
|
130068
130463
|
return false;
|
|
@@ -130077,7 +130472,7 @@ async function pruneEmptyProjectDirs(workspaceDir, projectName) {
|
|
|
130077
130472
|
for (const dir of candidateDirs2) {
|
|
130078
130473
|
if (!dir.startsWith(stopDir)) continue;
|
|
130079
130474
|
try {
|
|
130080
|
-
await
|
|
130475
|
+
await fs24.rmdir(dir);
|
|
130081
130476
|
} catch (error48) {
|
|
130082
130477
|
const code = error48.code;
|
|
130083
130478
|
if (code !== "ENOENT" && code !== "ENOTEMPTY") {
|
|
@@ -130088,10 +130483,10 @@ async function pruneEmptyProjectDirs(workspaceDir, projectName) {
|
|
|
130088
130483
|
}
|
|
130089
130484
|
async function cleanupLocalRegisterResidue(workspaceDir, projectName, opts) {
|
|
130090
130485
|
if (opts.removeWorkflowOverride) {
|
|
130091
|
-
await
|
|
130486
|
+
await fs24.rm(getProjectWorkflowPath(workspaceDir, projectName), { force: true });
|
|
130092
130487
|
}
|
|
130093
130488
|
if (opts.removePromptReadme) {
|
|
130094
|
-
await
|
|
130489
|
+
await fs24.rm(getProjectReadmePath(workspaceDir, projectName), { force: true });
|
|
130095
130490
|
}
|
|
130096
130491
|
await pruneEmptyProjectDirs(workspaceDir, projectName);
|
|
130097
130492
|
}
|
|
@@ -130124,7 +130519,7 @@ var NODE_STACKS = /* @__PURE__ */ new Set(["nextjs", "node-cli", "express"]);
|
|
|
130124
130519
|
var PYTHON_STACKS = /* @__PURE__ */ new Set(["fastapi", "flask", "django", "python-cli"]);
|
|
130125
130520
|
async function pathExists(candidate) {
|
|
130126
130521
|
try {
|
|
130127
|
-
await
|
|
130522
|
+
await fs24.access(candidate);
|
|
130128
130523
|
return true;
|
|
130129
130524
|
} catch {
|
|
130130
130525
|
return false;
|
|
@@ -130549,7 +130944,7 @@ init_labels();
|
|
|
130549
130944
|
|
|
130550
130945
|
// lib/services/worker-completion.ts
|
|
130551
130946
|
init_audit();
|
|
130552
|
-
import
|
|
130947
|
+
import fs25 from "node:fs/promises";
|
|
130553
130948
|
init_workflow();
|
|
130554
130949
|
|
|
130555
130950
|
// lib/services/worker-result.ts
|
|
@@ -130688,7 +131083,7 @@ function parseSessionTranscript(raw) {
|
|
|
130688
131083
|
async function readWorkerResultFromSessionFile(role, sessionFile) {
|
|
130689
131084
|
if (!sessionFile) return null;
|
|
130690
131085
|
try {
|
|
130691
|
-
const raw = await
|
|
131086
|
+
const raw = await fs25.readFile(sessionFile, "utf-8");
|
|
130692
131087
|
const messages = parseSessionTranscript(raw);
|
|
130693
131088
|
const result = extractWorkerResultFromMessages(role, messages);
|
|
130694
131089
|
return result ? {
|
|
@@ -132778,7 +133173,7 @@ ${channelList}`;
|
|
|
132778
133173
|
// lib/setup/index.ts
|
|
132779
133174
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
132780
133175
|
init_roles();
|
|
132781
|
-
import
|
|
133176
|
+
import fs39 from "node:fs/promises";
|
|
132782
133177
|
import path39 from "node:path";
|
|
132783
133178
|
|
|
132784
133179
|
// lib/setup/binding-manager.ts
|
|
@@ -132801,7 +133196,7 @@ async function migrateChannelBinding(api, channel, fromAgentId, toAgentId) {
|
|
|
132801
133196
|
|
|
132802
133197
|
// lib/setup/agent.ts
|
|
132803
133198
|
init_logger();
|
|
132804
|
-
import
|
|
133199
|
+
import fs26 from "node:fs/promises";
|
|
132805
133200
|
import path25 from "node:path";
|
|
132806
133201
|
async function createAgent(api, name, runCommand, channelBinding) {
|
|
132807
133202
|
const rc = runCommand;
|
|
@@ -132830,11 +133225,11 @@ function resolveWorkspacePath(api, agentId) {
|
|
|
132830
133225
|
}
|
|
132831
133226
|
async function cleanupWorkspace(workspacePath) {
|
|
132832
133227
|
try {
|
|
132833
|
-
await
|
|
133228
|
+
await fs26.rm(path25.join(workspacePath, ".git"), { recursive: true });
|
|
132834
133229
|
} catch {
|
|
132835
133230
|
}
|
|
132836
133231
|
try {
|
|
132837
|
-
await
|
|
133232
|
+
await fs26.unlink(path25.join(workspacePath, "BOOTSTRAP.md"));
|
|
132838
133233
|
} catch {
|
|
132839
133234
|
}
|
|
132840
133235
|
}
|
|
@@ -132897,7 +133292,7 @@ init_workspace();
|
|
|
132897
133292
|
|
|
132898
133293
|
// lib/services/heartbeat/agent-discovery.ts
|
|
132899
133294
|
init_constants();
|
|
132900
|
-
import
|
|
133295
|
+
import fs27 from "node:fs";
|
|
132901
133296
|
import path26 from "node:path";
|
|
132902
133297
|
function discoverAgents(config2) {
|
|
132903
133298
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -132924,7 +133319,7 @@ function discoverAgents(config2) {
|
|
|
132924
133319
|
return agents;
|
|
132925
133320
|
}
|
|
132926
133321
|
function hasProjects(workspace) {
|
|
132927
|
-
return
|
|
133322
|
+
return fs27.existsSync(path26.join(workspace, DATA_DIR, "projects.json")) || fs27.existsSync(path26.join(workspace, "projects.json")) || fs27.existsSync(path26.join(workspace, "projects", "projects.json"));
|
|
132928
133323
|
}
|
|
132929
133324
|
|
|
132930
133325
|
// lib/services/heartbeat/config.ts
|
|
@@ -137273,7 +137668,7 @@ async function processPendingGitHubEventsForWorkspace(params) {
|
|
|
137273
137668
|
}
|
|
137274
137669
|
|
|
137275
137670
|
// lib/machines/lifecycle-service.ts
|
|
137276
|
-
import
|
|
137671
|
+
import fs30 from "node:fs/promises";
|
|
137277
137672
|
import path29 from "node:path";
|
|
137278
137673
|
init_constants();
|
|
137279
137674
|
init_migrate_layout();
|
|
@@ -137366,9 +137761,9 @@ async function lifecycleSnapshotPath(workspaceDir) {
|
|
|
137366
137761
|
}
|
|
137367
137762
|
async function persistSnapshot(workspaceDir, actor) {
|
|
137368
137763
|
const filePath = await lifecycleSnapshotPath(workspaceDir);
|
|
137369
|
-
await
|
|
137764
|
+
await fs30.mkdir(path29.dirname(filePath), { recursive: true });
|
|
137370
137765
|
const snapshot = actor.getSnapshot();
|
|
137371
|
-
await
|
|
137766
|
+
await fs30.writeFile(filePath, JSON.stringify({
|
|
137372
137767
|
value: snapshot.value,
|
|
137373
137768
|
context: snapshot.context,
|
|
137374
137769
|
updatedAt: snapshot.context.updatedAt
|
|
@@ -137461,7 +137856,7 @@ async function raceWithTimeout(fn, timeoutMs, onTimeout) {
|
|
|
137461
137856
|
// lib/dispatch/telegram-bootstrap-hook.ts
|
|
137462
137857
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
137463
137858
|
import { homedir as homedir2 } from "node:os";
|
|
137464
|
-
import
|
|
137859
|
+
import fs37 from "node:fs/promises";
|
|
137465
137860
|
import path37 from "node:path";
|
|
137466
137861
|
|
|
137467
137862
|
// lib/dispatch/attachment-hook.ts
|
|
@@ -137908,7 +138303,7 @@ init_conduct_interview();
|
|
|
137908
138303
|
init_generate_spec();
|
|
137909
138304
|
|
|
137910
138305
|
// lib/intake/lib/project-map.ts
|
|
137911
|
-
import
|
|
138306
|
+
import fs32 from "node:fs/promises";
|
|
137912
138307
|
import path31 from "node:path";
|
|
137913
138308
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
137914
138309
|
".git",
|
|
@@ -138008,7 +138403,7 @@ function matchProjectByRef(projects, ref) {
|
|
|
138008
138403
|
async function tryStatDir(candidate) {
|
|
138009
138404
|
if (!candidate) return false;
|
|
138010
138405
|
try {
|
|
138011
|
-
return (await
|
|
138406
|
+
return (await fs32.stat(candidate)).isDirectory();
|
|
138012
138407
|
} catch {
|
|
138013
138408
|
return false;
|
|
138014
138409
|
}
|
|
@@ -138109,7 +138504,7 @@ async function resolveProjectTarget(payload, workspaceDir, homeDir) {
|
|
|
138109
138504
|
async function collectFiles(root, relativeDir = "", files = [], limit = 800) {
|
|
138110
138505
|
if (files.length >= limit) return files;
|
|
138111
138506
|
const currentDir = path31.join(root, relativeDir);
|
|
138112
|
-
const entries = await
|
|
138507
|
+
const entries = await fs32.readdir(currentDir, { withFileTypes: true });
|
|
138113
138508
|
for (const entry of entries) {
|
|
138114
138509
|
if (files.length >= limit) break;
|
|
138115
138510
|
if (entry.name.startsWith(".") && entry.name !== ".env.example") {
|
|
@@ -138303,16 +138698,16 @@ var impactStep = {
|
|
|
138303
138698
|
const modules = map2?.modules ?? [];
|
|
138304
138699
|
const specText = `${spec.title} ${spec.objective} ${spec.scope_v1.join(" ")}`.toLowerCase();
|
|
138305
138700
|
const affected = symbols.filter((s2) => specText.includes(s2.name.toLowerCase())).map((s2) => s2.file);
|
|
138306
|
-
const
|
|
138701
|
+
const unique2 = [...new Set(affected)];
|
|
138307
138702
|
const affectedModules = modules.filter((moduleName2) => specText.includes(moduleName2.toLowerCase()));
|
|
138308
|
-
const confidence = map2?.confidence === "low" || !
|
|
138703
|
+
const confidence = map2?.confidence === "low" || !unique2.length && !affectedModules.length ? "low" : "high";
|
|
138309
138704
|
impact = {
|
|
138310
138705
|
is_greenfield: false,
|
|
138311
|
-
affected_files:
|
|
138706
|
+
affected_files: unique2,
|
|
138312
138707
|
affected_modules: [...new Set(affectedModules)],
|
|
138313
138708
|
new_files_needed: [],
|
|
138314
138709
|
risk_areas: spec.risks,
|
|
138315
|
-
estimated_files_changed: Math.max(1,
|
|
138710
|
+
estimated_files_changed: Math.max(1, unique2.length || affectedModules.length),
|
|
138316
138711
|
confidence
|
|
138317
138712
|
};
|
|
138318
138713
|
}
|
|
@@ -138334,7 +138729,7 @@ init_runtime_paths();
|
|
|
138334
138729
|
|
|
138335
138730
|
// lib/test-env/bootstrap.ts
|
|
138336
138731
|
import { createHash as createHash3 } from "node:crypto";
|
|
138337
|
-
import
|
|
138732
|
+
import fs33 from "node:fs/promises";
|
|
138338
138733
|
import path32 from "node:path";
|
|
138339
138734
|
var NODE_STACKS2 = /* @__PURE__ */ new Set(["nextjs", "node-cli", "express"]);
|
|
138340
138735
|
var PYTHON_STACKS2 = /* @__PURE__ */ new Set(["fastapi", "flask", "django", "python-cli"]);
|
|
@@ -138401,7 +138796,7 @@ var BOOTSTRAP_STATE_FILE = "test-env.sha256";
|
|
|
138401
138796
|
var DEFAULT_TIMEOUT = 18e4;
|
|
138402
138797
|
async function pathExists2(candidate) {
|
|
138403
138798
|
try {
|
|
138404
|
-
await
|
|
138799
|
+
await fs33.access(candidate);
|
|
138405
138800
|
return true;
|
|
138406
138801
|
} catch {
|
|
138407
138802
|
return false;
|
|
@@ -138409,7 +138804,7 @@ async function pathExists2(candidate) {
|
|
|
138409
138804
|
}
|
|
138410
138805
|
async function isValidBinary(filePath) {
|
|
138411
138806
|
try {
|
|
138412
|
-
const stat2 = await
|
|
138807
|
+
const stat2 = await fs33.stat(filePath);
|
|
138413
138808
|
return stat2.size > 0;
|
|
138414
138809
|
} catch {
|
|
138415
138810
|
return false;
|
|
@@ -138577,7 +138972,7 @@ async function computeFingerprint(repoPath, files) {
|
|
|
138577
138972
|
for (const file2 of existing.sort()) {
|
|
138578
138973
|
hash2.update(`${file2}
|
|
138579
138974
|
`);
|
|
138580
|
-
hash2.update(await
|
|
138975
|
+
hash2.update(await fs33.readFile(path32.join(repoPath, file2)));
|
|
138581
138976
|
hash2.update("\n");
|
|
138582
138977
|
}
|
|
138583
138978
|
return hash2.digest("hex");
|
|
@@ -138585,15 +138980,15 @@ async function computeFingerprint(repoPath, files) {
|
|
|
138585
138980
|
async function readBootstrapFingerprint(repoPath) {
|
|
138586
138981
|
const stateFile = path32.join(repoPath, BOOTSTRAP_STATE_DIR, BOOTSTRAP_STATE_FILE);
|
|
138587
138982
|
try {
|
|
138588
|
-
return (await
|
|
138983
|
+
return (await fs33.readFile(stateFile, "utf-8")).trim() || null;
|
|
138589
138984
|
} catch {
|
|
138590
138985
|
return null;
|
|
138591
138986
|
}
|
|
138592
138987
|
}
|
|
138593
138988
|
async function writeBootstrapFingerprint(repoPath, fingerprint) {
|
|
138594
138989
|
const stateDir = path32.join(repoPath, BOOTSTRAP_STATE_DIR);
|
|
138595
|
-
await
|
|
138596
|
-
await
|
|
138990
|
+
await fs33.mkdir(stateDir, { recursive: true });
|
|
138991
|
+
await fs33.writeFile(path32.join(stateDir, BOOTSTRAP_STATE_FILE), fingerprint, "utf-8");
|
|
138597
138992
|
}
|
|
138598
138993
|
async function toolExists(runCommand, cmd, args = ["--version"]) {
|
|
138599
138994
|
try {
|
|
@@ -138652,7 +139047,7 @@ Details: ${(install.stderr || install.stdout || "unknown error").trim()}`
|
|
|
138652
139047
|
const home = process.env.HOME ?? "";
|
|
138653
139048
|
const fallbackPath = path32.join(home, ".local", "bin", "uv");
|
|
138654
139049
|
try {
|
|
138655
|
-
await
|
|
139050
|
+
await fs33.access(fallbackPath);
|
|
138656
139051
|
emit2(`[test-env] uv installed at fallback path: ${fallbackPath}`);
|
|
138657
139052
|
return fallbackPath;
|
|
138658
139053
|
} catch {
|
|
@@ -138682,16 +139077,16 @@ async function ensurePythonToolchain(runCommand, homeDir) {
|
|
|
138682
139077
|
const expectedFp = await toolchainFingerprint(runCommand);
|
|
138683
139078
|
if (await isValidBinary(ruffPath)) {
|
|
138684
139079
|
try {
|
|
138685
|
-
const currentFp = (await
|
|
139080
|
+
const currentFp = (await fs33.readFile(fingerprintPath, "utf-8")).trim();
|
|
138686
139081
|
if (currentFp === expectedFp) {
|
|
138687
139082
|
return toolchainPath;
|
|
138688
139083
|
}
|
|
138689
139084
|
} catch {
|
|
138690
139085
|
}
|
|
138691
|
-
await
|
|
139086
|
+
await fs33.rm(toolchainPath, { recursive: true, force: true });
|
|
138692
139087
|
}
|
|
138693
139088
|
const uvCmd = await ensureUv(runCommand);
|
|
138694
|
-
await
|
|
139089
|
+
await fs33.mkdir(path32.dirname(toolchainPath), { recursive: true });
|
|
138695
139090
|
const venvResult = await runCommand(uvCmd, ["venv", toolchainPath], { timeout: 6e4 });
|
|
138696
139091
|
if (venvResult.exitCode !== 0) {
|
|
138697
139092
|
throw new Error(`Failed to create toolchain venv: ${venvResult.stderr}`);
|
|
@@ -138706,13 +139101,13 @@ async function ensurePythonToolchain(runCommand, homeDir) {
|
|
|
138706
139101
|
if (installResult.exitCode !== 0) {
|
|
138707
139102
|
throw new Error(`Failed to install toolchain packages: ${installResult.stderr}`);
|
|
138708
139103
|
}
|
|
138709
|
-
await
|
|
139104
|
+
await fs33.writeFile(fingerprintPath, expectedFp, "utf-8");
|
|
138710
139105
|
return toolchainPath;
|
|
138711
139106
|
}
|
|
138712
139107
|
async function hasPyprojectDevExtra(repoPath) {
|
|
138713
139108
|
const pyprojectPath = path32.join(repoPath, "pyproject.toml");
|
|
138714
139109
|
if (!await pathExists2(pyprojectPath)) return false;
|
|
138715
|
-
const content = await
|
|
139110
|
+
const content = await fs33.readFile(pyprojectPath, "utf-8");
|
|
138716
139111
|
const match = content.match(/\[project\.optional-dependencies\]([\s\S]*?)(?:\n\[|$)/);
|
|
138717
139112
|
if (!match) return false;
|
|
138718
139113
|
return /^\s*dev\s*=\s*\[/m.test(match[1] ?? "");
|
|
@@ -139352,7 +139747,7 @@ var scaffoldPassthroughStep = {
|
|
|
139352
139747
|
};
|
|
139353
139748
|
|
|
139354
139749
|
// lib/intake/lib/repository-provision-service.ts
|
|
139355
|
-
import
|
|
139750
|
+
import fs34 from "node:fs/promises";
|
|
139356
139751
|
import path34 from "node:path";
|
|
139357
139752
|
function normalizeText2(value) {
|
|
139358
139753
|
if (typeof value !== "string") return null;
|
|
@@ -139393,7 +139788,7 @@ function adaptRunCommand(ctx) {
|
|
|
139393
139788
|
async function pathExists3(candidate) {
|
|
139394
139789
|
if (!candidate) return false;
|
|
139395
139790
|
try {
|
|
139396
|
-
await
|
|
139791
|
+
await fs34.access(candidate);
|
|
139397
139792
|
return true;
|
|
139398
139793
|
} catch {
|
|
139399
139794
|
return false;
|
|
@@ -139764,6 +140159,287 @@ var securityReviewStep = {
|
|
|
139764
140159
|
|
|
139765
140160
|
// lib/intake/steps/create-task.ts
|
|
139766
140161
|
init_workflow();
|
|
140162
|
+
|
|
140163
|
+
// lib/dispatch/telegram-bootstrap-session.ts
|
|
140164
|
+
init_constants();
|
|
140165
|
+
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
140166
|
+
import fs35 from "node:fs/promises";
|
|
140167
|
+
import path35 from "node:path";
|
|
140168
|
+
var SESSION_TTL_MS = 10 * 6e4;
|
|
140169
|
+
var CLASSIFYING_TTL_MS = 15e3;
|
|
140170
|
+
var RELEASED_CLASSIFY_ERRORS = /* @__PURE__ */ new Set([
|
|
140171
|
+
"classify_invalid_result",
|
|
140172
|
+
"classify_low_confidence",
|
|
140173
|
+
"classify_not_project",
|
|
140174
|
+
"classify_result_expired"
|
|
140175
|
+
]);
|
|
140176
|
+
function toCanonicalTelegramBootstrapConversationId(conversationId) {
|
|
140177
|
+
const trimmed = conversationId.trim();
|
|
140178
|
+
if (!trimmed) return trimmed;
|
|
140179
|
+
return trimmed.startsWith("telegram:") ? trimmed : `telegram:${trimmed}`;
|
|
140180
|
+
}
|
|
140181
|
+
function sessionsDir(workspaceDir) {
|
|
140182
|
+
return path35.join(workspaceDir, DATA_DIR, "bootstrap-sessions");
|
|
140183
|
+
}
|
|
140184
|
+
function sessionPath(workspaceDir, conversationId) {
|
|
140185
|
+
return path35.join(
|
|
140186
|
+
sessionsDir(workspaceDir),
|
|
140187
|
+
`${toCanonicalTelegramBootstrapConversationId(conversationId)}.json`
|
|
140188
|
+
);
|
|
140189
|
+
}
|
|
140190
|
+
function alternateSessionPath(workspaceDir, conversationId) {
|
|
140191
|
+
const canonical = toCanonicalTelegramBootstrapConversationId(conversationId);
|
|
140192
|
+
const bare = canonical.startsWith("telegram:") ? canonical.slice("telegram:".length) : canonical;
|
|
140193
|
+
if (!bare || bare === canonical) return null;
|
|
140194
|
+
return path35.join(sessionsDir(workspaceDir), `${bare}.json`);
|
|
140195
|
+
}
|
|
140196
|
+
function normalizeStoredSession(session) {
|
|
140197
|
+
const canonicalConversationId = toCanonicalTelegramBootstrapConversationId(session.conversationId);
|
|
140198
|
+
if (canonicalConversationId === session.conversationId) {
|
|
140199
|
+
return session;
|
|
140200
|
+
}
|
|
140201
|
+
return {
|
|
140202
|
+
...session,
|
|
140203
|
+
id: buildBootstrapSessionId(canonicalConversationId, session.rawIdea),
|
|
140204
|
+
conversationId: canonicalConversationId
|
|
140205
|
+
};
|
|
140206
|
+
}
|
|
140207
|
+
function stableHash(input) {
|
|
140208
|
+
return createHash4("sha256").update(input).digest("hex").slice(0, 16);
|
|
140209
|
+
}
|
|
140210
|
+
function buildBootstrapSessionId(conversationId, rawIdea) {
|
|
140211
|
+
return `tgdm-${conversationId}-${stableHash(rawIdea.trim().toLowerCase())}`;
|
|
140212
|
+
}
|
|
140213
|
+
function buildBootstrapRequestFingerprint(input) {
|
|
140214
|
+
return stableHash(JSON.stringify({
|
|
140215
|
+
rawIdea: input.rawIdea.trim().toLowerCase(),
|
|
140216
|
+
projectName: input.projectName?.trim().toLowerCase() || null,
|
|
140217
|
+
stackHint: input.stackHint?.trim().toLowerCase() || null,
|
|
140218
|
+
repoUrl: input.repoUrl?.trim().toLowerCase() || null,
|
|
140219
|
+
repoPath: input.repoPath?.trim().toLowerCase() || null
|
|
140220
|
+
}));
|
|
140221
|
+
}
|
|
140222
|
+
function buildBootstrapRequestHash(input) {
|
|
140223
|
+
return buildBootstrapRequestFingerprint(input);
|
|
140224
|
+
}
|
|
140225
|
+
function nextSuppressUntil(status) {
|
|
140226
|
+
const ttl = status === "classifying" || status === "pending_classify" ? CLASSIFYING_TTL_MS : SESSION_TTL_MS;
|
|
140227
|
+
return new Date(Date.now() + ttl).toISOString();
|
|
140228
|
+
}
|
|
140229
|
+
function isReleasedClassifyFailure(error48) {
|
|
140230
|
+
return Boolean(error48 && RELEASED_CLASSIFY_ERRORS.has(error48));
|
|
140231
|
+
}
|
|
140232
|
+
function resolveNullableField(inputValue, existingValue, fallback = null) {
|
|
140233
|
+
return inputValue !== void 0 ? inputValue : existingValue ?? fallback;
|
|
140234
|
+
}
|
|
140235
|
+
function defaultNextRetryAtForStatus(status, existingValue) {
|
|
140236
|
+
if (status === "bootstrapping" || status === "dispatching") {
|
|
140237
|
+
return existingValue ?? null;
|
|
140238
|
+
}
|
|
140239
|
+
return null;
|
|
140240
|
+
}
|
|
140241
|
+
var MONOTONIC_BOOTSTRAP_FIELDS = [
|
|
140242
|
+
"ackSentAt",
|
|
140243
|
+
"projectRegisteredAt",
|
|
140244
|
+
"topicKickoffSentAt",
|
|
140245
|
+
"projectTickedAt",
|
|
140246
|
+
"completionAckSentAt"
|
|
140247
|
+
];
|
|
140248
|
+
function shouldPersistBootstrapCheckpoint(current, next) {
|
|
140249
|
+
if (!current) return { ok: true };
|
|
140250
|
+
if (current.conversationId !== next.conversationId) return { ok: true };
|
|
140251
|
+
const currentAttemptSeq = current.attemptSeq ?? null;
|
|
140252
|
+
const nextAttemptSeq = next.attemptSeq ?? null;
|
|
140253
|
+
if (currentAttemptSeq != null && nextAttemptSeq != null && nextAttemptSeq < currentAttemptSeq) {
|
|
140254
|
+
return { ok: false, reason: "stale_regression" };
|
|
140255
|
+
}
|
|
140256
|
+
const sameAttempt = Boolean(
|
|
140257
|
+
current.attemptId && next.attemptId && current.attemptSeq != null && next.attemptSeq != null && current.attemptId === next.attemptId && current.attemptSeq === next.attemptSeq
|
|
140258
|
+
);
|
|
140259
|
+
if (!sameAttempt) return { ok: true };
|
|
140260
|
+
for (const field of MONOTONIC_BOOTSTRAP_FIELDS) {
|
|
140261
|
+
if (current[field] && !next[field]) {
|
|
140262
|
+
return { ok: false, reason: "stale_regression" };
|
|
140263
|
+
}
|
|
140264
|
+
}
|
|
140265
|
+
return { ok: true };
|
|
140266
|
+
}
|
|
140267
|
+
async function readTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
140268
|
+
const canonicalConversationId = toCanonicalTelegramBootstrapConversationId(conversationId);
|
|
140269
|
+
const paths = [
|
|
140270
|
+
sessionPath(workspaceDir, canonicalConversationId),
|
|
140271
|
+
alternateSessionPath(workspaceDir, conversationId)
|
|
140272
|
+
].filter((entry) => Boolean(entry));
|
|
140273
|
+
try {
|
|
140274
|
+
for (const candidatePath of paths) {
|
|
140275
|
+
try {
|
|
140276
|
+
const raw = await fs35.readFile(candidatePath, "utf-8");
|
|
140277
|
+
const session = normalizeStoredSession(JSON.parse(raw));
|
|
140278
|
+
if (session.status === "failed" && isReleasedClassifyFailure(session.error)) {
|
|
140279
|
+
await deleteTelegramBootstrapSession(workspaceDir, canonicalConversationId);
|
|
140280
|
+
return null;
|
|
140281
|
+
}
|
|
140282
|
+
return session;
|
|
140283
|
+
} catch (error48) {
|
|
140284
|
+
if (error48?.code === "ENOENT") continue;
|
|
140285
|
+
throw error48;
|
|
140286
|
+
}
|
|
140287
|
+
}
|
|
140288
|
+
return null;
|
|
140289
|
+
} catch (error48) {
|
|
140290
|
+
if (error48?.code === "ENOENT") return null;
|
|
140291
|
+
throw error48;
|
|
140292
|
+
}
|
|
140293
|
+
}
|
|
140294
|
+
async function deleteTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
140295
|
+
await fs35.unlink(sessionPath(workspaceDir, conversationId)).catch(() => {
|
|
140296
|
+
});
|
|
140297
|
+
const legacyPath = alternateSessionPath(workspaceDir, conversationId);
|
|
140298
|
+
if (legacyPath) {
|
|
140299
|
+
await fs35.unlink(legacyPath).catch(() => {
|
|
140300
|
+
});
|
|
140301
|
+
}
|
|
140302
|
+
}
|
|
140303
|
+
async function writeTelegramBootstrapSession(workspaceDir, session) {
|
|
140304
|
+
const canonicalSession = normalizeStoredSession(session);
|
|
140305
|
+
const dir = sessionsDir(workspaceDir);
|
|
140306
|
+
await fs35.mkdir(dir, { recursive: true });
|
|
140307
|
+
const file2 = sessionPath(workspaceDir, canonicalSession.conversationId);
|
|
140308
|
+
const tmp = `${file2}.${randomUUID4()}.tmp`;
|
|
140309
|
+
await fs35.writeFile(tmp, JSON.stringify(canonicalSession, null, 2) + "\n", "utf-8");
|
|
140310
|
+
await fs35.rename(tmp, file2);
|
|
140311
|
+
const legacyPath = alternateSessionPath(workspaceDir, session.conversationId);
|
|
140312
|
+
if (legacyPath) {
|
|
140313
|
+
await fs35.unlink(legacyPath).catch(() => {
|
|
140314
|
+
});
|
|
140315
|
+
}
|
|
140316
|
+
}
|
|
140317
|
+
async function upsertTelegramBootstrapSession(workspaceDir, input) {
|
|
140318
|
+
const conversationId = toCanonicalTelegramBootstrapConversationId(input.conversationId);
|
|
140319
|
+
const existing = await readTelegramBootstrapSession(workspaceDir, conversationId);
|
|
140320
|
+
const resolvedSourceRoute = resolveNullableField(input.sourceRoute, existing?.sourceRoute);
|
|
140321
|
+
const resolvedProjectRoute = resolveNullableField(input.projectRoute, existing?.projectRoute);
|
|
140322
|
+
const resolvedProjectName = resolveNullableField(input.projectName, existing?.projectName);
|
|
140323
|
+
const resolvedStackHint = resolveNullableField(input.stackHint, existing?.stackHint);
|
|
140324
|
+
const resolvedRepoUrl = resolveNullableField(input.repoUrl, existing?.repoUrl);
|
|
140325
|
+
const resolvedRepoPath = resolveNullableField(input.repoPath, existing?.repoPath);
|
|
140326
|
+
const resolvedProjectSlug = resolveNullableField(input.projectSlug, existing?.projectSlug);
|
|
140327
|
+
const resolvedIssueId = resolveNullableField(input.issueId, existing?.issueId);
|
|
140328
|
+
const resolvedIssueUrl = resolveNullableField(input.issueUrl, existing?.issueUrl);
|
|
140329
|
+
const resolvedTriageReadyForDispatch = resolveNullableField(input.triageReadyForDispatch, existing?.triageReadyForDispatch);
|
|
140330
|
+
const resolvedTriageErrors = input.triageErrors !== void 0 ? input.triageErrors : existing?.triageErrors ?? null;
|
|
140331
|
+
const resolvedMessageThreadId = resolveNullableField(input.messageThreadId, existing?.messageThreadId);
|
|
140332
|
+
const resolvedProjectChannelId = resolveNullableField(input.projectChannelId, existing?.projectChannelId);
|
|
140333
|
+
const resolvedAttemptCount = resolveNullableField(input.attemptCount, existing?.attemptCount, 0);
|
|
140334
|
+
const resolvedAttemptId = resolveNullableField(input.attemptId, existing?.attemptId);
|
|
140335
|
+
const resolvedAttemptSeq = resolveNullableField(input.attemptSeq, existing?.attemptSeq);
|
|
140336
|
+
const resolvedClassifySessionKey = resolveNullableField(input.classifySessionKey, existing?.classifySessionKey);
|
|
140337
|
+
const resolvedClassifyRunId = resolveNullableField(input.classifyRunId, existing?.classifyRunId);
|
|
140338
|
+
const resolvedClassifyStartedAt = resolveNullableField(input.classifyStartedAt, existing?.classifyStartedAt);
|
|
140339
|
+
const resolvedBootstrapStep = resolveNullableField(input.bootstrapStep, existing?.bootstrapStep);
|
|
140340
|
+
const resolvedNextRetryAt = input.nextRetryAt !== void 0 ? input.nextRetryAt : defaultNextRetryAtForStatus(input.status, existing?.nextRetryAt);
|
|
140341
|
+
const resolvedAckSentAt = resolveNullableField(input.ackSentAt, existing?.ackSentAt);
|
|
140342
|
+
const resolvedProjectRegisteredAt = resolveNullableField(input.projectRegisteredAt, existing?.projectRegisteredAt);
|
|
140343
|
+
const resolvedTopicKickoffSentAt = resolveNullableField(input.topicKickoffSentAt, existing?.topicKickoffSentAt);
|
|
140344
|
+
const resolvedProjectTickedAt = resolveNullableField(input.projectTickedAt, existing?.projectTickedAt);
|
|
140345
|
+
const resolvedCompletionAckSentAt = resolveNullableField(input.completionAckSentAt, existing?.completionAckSentAt);
|
|
140346
|
+
const resolvedError = input.error !== void 0 ? input.error : input.lastError !== void 0 ? input.lastError : existing?.error ?? existing?.lastError ?? null;
|
|
140347
|
+
const requestHash = buildBootstrapRequestHash({
|
|
140348
|
+
rawIdea: input.rawIdea,
|
|
140349
|
+
projectName: resolvedProjectName,
|
|
140350
|
+
stackHint: resolvedStackHint,
|
|
140351
|
+
repoUrl: resolvedRepoUrl,
|
|
140352
|
+
repoPath: resolvedRepoPath
|
|
140353
|
+
});
|
|
140354
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
140355
|
+
const session = {
|
|
140356
|
+
id: existing?.id ?? buildBootstrapSessionId(conversationId, input.rawIdea),
|
|
140357
|
+
conversationId,
|
|
140358
|
+
sourceChannel: input.sourceChannel ?? input.sourceRoute?.channel ?? existing?.sourceChannel ?? "telegram",
|
|
140359
|
+
sourceRoute: resolvedSourceRoute,
|
|
140360
|
+
projectRoute: resolvedProjectRoute,
|
|
140361
|
+
requestHash,
|
|
140362
|
+
requestFingerprint: requestHash,
|
|
140363
|
+
lastCompletedRequestHash: input.status === "completed" ? requestHash : existing?.lastCompletedRequestHash ?? null,
|
|
140364
|
+
rawIdea: input.rawIdea,
|
|
140365
|
+
projectName: resolvedProjectName,
|
|
140366
|
+
stackHint: resolvedStackHint,
|
|
140367
|
+
repoUrl: resolvedRepoUrl,
|
|
140368
|
+
repoPath: resolvedRepoPath,
|
|
140369
|
+
projectSlug: resolvedProjectSlug,
|
|
140370
|
+
issueId: resolvedIssueId,
|
|
140371
|
+
issueUrl: resolvedIssueUrl,
|
|
140372
|
+
triageReadyForDispatch: resolvedTriageReadyForDispatch,
|
|
140373
|
+
triageErrors: resolvedTriageErrors,
|
|
140374
|
+
messageThreadId: resolvedMessageThreadId,
|
|
140375
|
+
projectChannelId: resolvedProjectChannelId,
|
|
140376
|
+
language: input.language ?? existing?.language,
|
|
140377
|
+
status: input.status,
|
|
140378
|
+
attemptId: resolvedAttemptId,
|
|
140379
|
+
attemptSeq: resolvedAttemptSeq,
|
|
140380
|
+
classifySessionKey: resolvedClassifySessionKey,
|
|
140381
|
+
classifyRunId: resolvedClassifyRunId,
|
|
140382
|
+
classifyStartedAt: resolvedClassifyStartedAt,
|
|
140383
|
+
bootstrapStep: resolvedBootstrapStep,
|
|
140384
|
+
attemptCount: resolvedAttemptCount,
|
|
140385
|
+
lastError: resolvedError,
|
|
140386
|
+
nextRetryAt: resolvedNextRetryAt,
|
|
140387
|
+
ackSentAt: resolvedAckSentAt,
|
|
140388
|
+
projectRegisteredAt: resolvedProjectRegisteredAt,
|
|
140389
|
+
topicKickoffSentAt: resolvedTopicKickoffSentAt,
|
|
140390
|
+
projectTickedAt: resolvedProjectTickedAt,
|
|
140391
|
+
completionAckSentAt: resolvedCompletionAckSentAt,
|
|
140392
|
+
pendingClarification: input.pendingClarification !== void 0 ? input.pendingClarification : existing?.pendingClarification ?? null,
|
|
140393
|
+
orphanedArtifacts: input.orphanedArtifacts !== void 0 ? input.orphanedArtifacts : existing?.orphanedArtifacts ?? null,
|
|
140394
|
+
createdAt: existing?.createdAt ?? now2,
|
|
140395
|
+
updatedAt: now2,
|
|
140396
|
+
suppressUntil: nextSuppressUntil(input.status),
|
|
140397
|
+
error: resolvedError
|
|
140398
|
+
};
|
|
140399
|
+
const writeDecision = shouldPersistBootstrapCheckpoint(existing, session);
|
|
140400
|
+
if (!writeDecision.ok && existing) {
|
|
140401
|
+
return existing;
|
|
140402
|
+
}
|
|
140403
|
+
await writeTelegramBootstrapSession(workspaceDir, session);
|
|
140404
|
+
return session;
|
|
140405
|
+
}
|
|
140406
|
+
function shouldSuppressTelegramBootstrapReply(session, request) {
|
|
140407
|
+
if (!session) return false;
|
|
140408
|
+
if (isTelegramBootstrapSessionExpired(session)) return false;
|
|
140409
|
+
if (session.status === "completed" || session.status === "failed") {
|
|
140410
|
+
if (session.status === "failed" && isReleasedClassifyFailure(session.error)) return false;
|
|
140411
|
+
if (!request) return false;
|
|
140412
|
+
return buildBootstrapRequestFingerprint(request) === session.requestHash;
|
|
140413
|
+
}
|
|
140414
|
+
if (!request) return true;
|
|
140415
|
+
return buildBootstrapRequestFingerprint(request) === session.requestHash;
|
|
140416
|
+
}
|
|
140417
|
+
function isTelegramBootstrapSessionExpired(session, now2 = Date.now()) {
|
|
140418
|
+
if (!session) return false;
|
|
140419
|
+
const suppressUntil = Date.parse(session.suppressUntil);
|
|
140420
|
+
return !Number.isNaN(suppressUntil) && suppressUntil < now2;
|
|
140421
|
+
}
|
|
140422
|
+
function isRecoverableTelegramBootstrapSession(session) {
|
|
140423
|
+
return session?.status === "bootstrapping" || session?.status === "dispatching";
|
|
140424
|
+
}
|
|
140425
|
+
function isClaimableTelegramBootstrapSession(session, now2 = Date.now()) {
|
|
140426
|
+
if (!isRecoverableTelegramBootstrapSession(session)) return false;
|
|
140427
|
+
if (!session.nextRetryAt) return true;
|
|
140428
|
+
const retryAt = Date.parse(session.nextRetryAt);
|
|
140429
|
+
return Number.isNaN(retryAt) || retryAt <= now2;
|
|
140430
|
+
}
|
|
140431
|
+
function isSupersededTelegramBootstrapAttempt(current, candidate) {
|
|
140432
|
+
if (!current || !candidate) return false;
|
|
140433
|
+
if (current.conversationId !== candidate.conversationId) return true;
|
|
140434
|
+
const currentHasAttempt = current.attemptId != null && current.attemptSeq != null;
|
|
140435
|
+
const candidateHasAttempt = candidate.attemptId != null && candidate.attemptSeq != null;
|
|
140436
|
+
if (currentHasAttempt || candidateHasAttempt) {
|
|
140437
|
+
return current.attemptId !== candidate.attemptId || current.attemptSeq !== candidate.attemptSeq;
|
|
140438
|
+
}
|
|
140439
|
+
return current.requestHash !== candidate.requestHash || current.status !== candidate.status || current.updatedAt !== candidate.updatedAt;
|
|
140440
|
+
}
|
|
140441
|
+
|
|
140442
|
+
// lib/intake/steps/create-task.ts
|
|
139767
140443
|
function buildIssueBody(payload) {
|
|
139768
140444
|
const spec = payload.spec;
|
|
139769
140445
|
const sections = [];
|
|
@@ -139835,6 +140511,26 @@ var createTaskStep = {
|
|
|
139835
140511
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
139836
140512
|
};
|
|
139837
140513
|
ctx.log(`Issue created via provider: #${issue3.number} \u2014 ${issue3.url}`);
|
|
140514
|
+
const bootstrapConversationId2 = payload.metadata?.source === "telegram-dm-bootstrap" ? payload.metadata?.channel_id : null;
|
|
140515
|
+
if (bootstrapConversationId2) {
|
|
140516
|
+
await upsertTelegramBootstrapSession(ctx.workspaceDir, {
|
|
140517
|
+
conversationId: String(bootstrapConversationId2),
|
|
140518
|
+
rawIdea: payload.raw_idea,
|
|
140519
|
+
projectName: payload.metadata?.project_name ?? null,
|
|
140520
|
+
stackHint: payload.metadata?.stack_hint ?? null,
|
|
140521
|
+
repoUrl: repoUrl ?? null,
|
|
140522
|
+
repoPath: repoPath ?? null,
|
|
140523
|
+
status: "dispatching",
|
|
140524
|
+
bootstrapStep: "project_registered",
|
|
140525
|
+
projectSlug: projectSlug ?? null,
|
|
140526
|
+
issueId: issue3.number,
|
|
140527
|
+
issueUrl: issue3.url,
|
|
140528
|
+
projectChannelId: payload.metadata?.channel_id ?? null,
|
|
140529
|
+
messageThreadId: payload.metadata?.message_thread_id ?? null,
|
|
140530
|
+
projectRegisteredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
140531
|
+
}).catch(() => {
|
|
140532
|
+
});
|
|
140533
|
+
}
|
|
139838
140534
|
return {
|
|
139839
140535
|
...payload,
|
|
139840
140536
|
step: "create-task",
|
|
@@ -139870,6 +140566,26 @@ var createTaskStep = {
|
|
|
139870
140566
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
139871
140567
|
};
|
|
139872
140568
|
ctx.log(`Issue created via compatibility fallback: #${issueNumber} \u2014 ${issueUrl}`);
|
|
140569
|
+
const bootstrapConversationId = payload.metadata?.source === "telegram-dm-bootstrap" ? payload.metadata?.channel_id : null;
|
|
140570
|
+
if (bootstrapConversationId) {
|
|
140571
|
+
await upsertTelegramBootstrapSession(ctx.workspaceDir, {
|
|
140572
|
+
conversationId: String(bootstrapConversationId),
|
|
140573
|
+
rawIdea: payload.raw_idea,
|
|
140574
|
+
projectName: payload.metadata?.project_name ?? null,
|
|
140575
|
+
stackHint: payload.metadata?.stack_hint ?? null,
|
|
140576
|
+
repoUrl: repoUrl ?? null,
|
|
140577
|
+
repoPath: repoPath ?? null,
|
|
140578
|
+
status: "dispatching",
|
|
140579
|
+
bootstrapStep: "project_registered",
|
|
140580
|
+
projectSlug: projectSlug ?? null,
|
|
140581
|
+
issueId: issue2.number,
|
|
140582
|
+
issueUrl: issue2.url,
|
|
140583
|
+
projectChannelId: payload.metadata?.channel_id ?? null,
|
|
140584
|
+
messageThreadId: payload.metadata?.message_thread_id ?? null,
|
|
140585
|
+
projectRegisteredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
140586
|
+
}).catch(() => {
|
|
140587
|
+
});
|
|
140588
|
+
}
|
|
139873
140589
|
return {
|
|
139874
140590
|
...payload,
|
|
139875
140591
|
step: "create-task",
|
|
@@ -139982,9 +140698,66 @@ function determineLevel(effort, targetState) {
|
|
|
139982
140698
|
if (targetState === "To Research" && level === "medior") level = "junior";
|
|
139983
140699
|
return level;
|
|
139984
140700
|
}
|
|
140701
|
+
function assessComplexity(input) {
|
|
140702
|
+
const broadSignals = detectRawIdeaComplexity(input.rawIdea).signals.length;
|
|
140703
|
+
if (input.filesChanged >= 18 || input.acCount >= 8 || broadSignals >= 4 || input.totalRisks >= 4) return "high";
|
|
140704
|
+
if (input.filesChanged >= 8 || input.acCount >= 4 || broadSignals >= 2 || input.totalRisks >= 2) return "medium";
|
|
140705
|
+
return "low";
|
|
140706
|
+
}
|
|
140707
|
+
function assessCoupling(input) {
|
|
140708
|
+
const text = `${input.rawIdea} ${input.scopeText}`.toLowerCase();
|
|
140709
|
+
const crossCuttingSignals = [
|
|
140710
|
+
/\bauth\b|\bpermission\b|\brbac\b/,
|
|
140711
|
+
/\bmigration\b|\bschema\b|\bdatabase\b/,
|
|
140712
|
+
/\bbackground\b|\bworker\b|\bqueue\b|\bcron\b/,
|
|
140713
|
+
/\bfrontend\b|\bdashboard\b|\bui\b/,
|
|
140714
|
+
/\bintegration\b|\bwebhook\b|\bexternal\b/
|
|
140715
|
+
].filter((regex) => regex.test(text)).length;
|
|
140716
|
+
if (crossCuttingSignals >= 4) return "high";
|
|
140717
|
+
if (crossCuttingSignals >= 2) return "medium";
|
|
140718
|
+
return "low";
|
|
140719
|
+
}
|
|
140720
|
+
function assessParallelizability(input, coupling) {
|
|
140721
|
+
const text = `${input.rawIdea} ${input.scopeText}`.toLowerCase();
|
|
140722
|
+
const capabilitySignals = [
|
|
140723
|
+
/\bauth\b/,
|
|
140724
|
+
/\bapi\b|\bendpoint\b|\broute\b/,
|
|
140725
|
+
/\bfrontend\b|\bdashboard\b|\bui\b/,
|
|
140726
|
+
/\bworker\b|\bqueue\b|\bbackground\b/,
|
|
140727
|
+
/\bnotification\b|\bemail\b|\bsms\b|\balert\b/,
|
|
140728
|
+
/\bdatabase\b|\bmigration\b|\bschema\b/
|
|
140729
|
+
].filter((regex) => regex.test(text)).length;
|
|
140730
|
+
if (coupling === "high") return "low";
|
|
140731
|
+
if (capabilitySignals >= 4) return "high";
|
|
140732
|
+
if (capabilitySignals >= 2) return "medium";
|
|
140733
|
+
return "low";
|
|
140734
|
+
}
|
|
140735
|
+
function assessQualityCriticality(input) {
|
|
140736
|
+
const text = `${input.rawIdea} ${input.objective} ${input.scopeText}`.toLowerCase();
|
|
140737
|
+
if (/\bauth\b|\boauth\b|\bjwt\b|\brbac\b|\bpayment\b|\bbilling\b|\bpii\b|\bsecure\b|\bsecurity\b/.test(text)) return "high";
|
|
140738
|
+
if (/\bperformance\b|\bscalable\b|\bbackground\b|\bworker\b|\bintegration\b|\bwebhook\b/.test(text)) return "medium";
|
|
140739
|
+
return "low";
|
|
140740
|
+
}
|
|
140741
|
+
function buildRiskProfile(input) {
|
|
140742
|
+
const text = `${input.rawIdea} ${input.objective} ${input.scopeText}`.toLowerCase();
|
|
140743
|
+
const risks = [
|
|
140744
|
+
/\bauth\b|\boauth\b|\bjwt\b|\brbac\b/.test(text) ? "auth" : null,
|
|
140745
|
+
/\bpayment\b|\bbilling\b|\bsubscription\b/.test(text) ? "payments" : null,
|
|
140746
|
+
/\bdatabase\b|\bmigration\b|\bschema\b/.test(text) ? "data_model" : null,
|
|
140747
|
+
/\bworker\b|\bqueue\b|\bbackground\b|\bcron\b/.test(text) ? "async_processing" : null,
|
|
140748
|
+
/\bintegration\b|\bwebhook\b|\bexternal\b/.test(text) ? "external_integration" : null,
|
|
140749
|
+
/\bperformance\b|\blatency\b|\bthroughput\b/.test(text) ? "performance" : null
|
|
140750
|
+
];
|
|
140751
|
+
return Array.from(new Set(risks.filter((risk) => Boolean(risk))));
|
|
140752
|
+
}
|
|
139985
140753
|
function runTriageLogic(input, matrix) {
|
|
139986
140754
|
const effort = calculateEffort(input.filesChanged, input.acCount, input.rawIdea);
|
|
139987
140755
|
const { priority, label: priorityLabel } = calculatePriority(input.type, effort, input.totalRisks, matrix);
|
|
140756
|
+
const complexity = assessComplexity(input);
|
|
140757
|
+
const coupling = assessCoupling(input);
|
|
140758
|
+
const parallelizability = assessParallelizability(input, coupling);
|
|
140759
|
+
const qualityCriticality = assessQualityCriticality(input);
|
|
140760
|
+
const riskProfile = buildRiskProfile(input);
|
|
139988
140761
|
const effortLabel = matrix.effort_rules[effort]?.label ?? `effort:${effort}`;
|
|
139989
140762
|
const typeLabel = matrix.auto_labels[input.type] ?? "";
|
|
139990
140763
|
const targetState = matrix.target_state_by_type[input.type] ?? matrix.target_state_by_type.default ?? "To Do";
|
|
@@ -139996,7 +140769,7 @@ function runTriageLogic(input, matrix) {
|
|
|
139996
140769
|
objective: input.objective ?? input.rawIdea,
|
|
139997
140770
|
scopeItems: (input.scopeText ?? "").split("\n").filter((l) => l.trim()),
|
|
139998
140771
|
acceptanceCriteria: (input.acText ?? "").split("\n").filter((l) => l.trim()),
|
|
139999
|
-
dod: input.
|
|
140772
|
+
dod: input.dodText ?? ""
|
|
140000
140773
|
});
|
|
140001
140774
|
const specQualityBlock = specQualityErrors.length > 0;
|
|
140002
140775
|
return {
|
|
@@ -140004,6 +140777,11 @@ function runTriageLogic(input, matrix) {
|
|
|
140004
140777
|
priorityLabel,
|
|
140005
140778
|
effort,
|
|
140006
140779
|
effortLabel,
|
|
140780
|
+
complexity,
|
|
140781
|
+
coupling,
|
|
140782
|
+
parallelizability,
|
|
140783
|
+
qualityCriticality,
|
|
140784
|
+
riskProfile,
|
|
140007
140785
|
typeLabel,
|
|
140008
140786
|
targetState,
|
|
140009
140787
|
dispatchLabel,
|
|
@@ -140038,47 +140816,333 @@ function validateSpecQuality(input) {
|
|
|
140038
140816
|
return errors;
|
|
140039
140817
|
}
|
|
140040
140818
|
|
|
140819
|
+
// lib/intake/lib/decomposition-planner.ts
|
|
140820
|
+
var CAPABILITY_PATTERNS = [
|
|
140821
|
+
{ area: "auth", label: "Authentication & Access", regex: /\b(auth|oauth|jwt|login|register|session|permission|rbac|role)\b/i },
|
|
140822
|
+
{ area: "worker", label: "Background Jobs & Automation", regex: /\b(worker|background|queue|job|scheduler|reminder|async|cron)\b/i },
|
|
140823
|
+
{ area: "notifications", label: "Notifications & Delivery", regex: /\b(notification|notify|email|sms|webhook|alert|message)\b/i },
|
|
140824
|
+
{ area: "frontend", label: "Frontend & UX", regex: /\b(ui|frontend|page|screen|dashboard|view|form)\b/i },
|
|
140825
|
+
{ area: "data", label: "Data Model & Persistence", regex: /\b(database|schema|model|migration|persist|storage|repository|crud)\b/i },
|
|
140826
|
+
{ area: "api", label: "API & Application Flow", regex: /\b(api|endpoint|route|http|rest|graphql|handler)\b/i },
|
|
140827
|
+
{ area: "integration", label: "Integrations & External Services", regex: /\b(integration|provider|third-party|external|sync|import|export)\b/i },
|
|
140828
|
+
{ area: "qa", label: "QA, Docs & Release Readiness", regex: /\b(test|qa|coverage|document|docs|guide|readme|validation)\b/i },
|
|
140829
|
+
{ area: "core", label: "Core Workflow", regex: /./i }
|
|
140830
|
+
];
|
|
140831
|
+
var AREA_LABEL = Object.fromEntries(
|
|
140832
|
+
CAPABILITY_PATTERNS.map((entry) => [entry.area, entry.label])
|
|
140833
|
+
);
|
|
140834
|
+
function normalizeLines(items) {
|
|
140835
|
+
return items.map((item) => item.trim()).filter(Boolean);
|
|
140836
|
+
}
|
|
140837
|
+
function detectCapabilityArea(text, deliveryTarget) {
|
|
140838
|
+
const normalized = text.trim();
|
|
140839
|
+
for (const entry of CAPABILITY_PATTERNS) {
|
|
140840
|
+
if (entry.regex.test(normalized)) return entry.area;
|
|
140841
|
+
}
|
|
140842
|
+
if (deliveryTarget === "api") return "api";
|
|
140843
|
+
if (deliveryTarget === "web-ui") return "frontend";
|
|
140844
|
+
return "core";
|
|
140845
|
+
}
|
|
140846
|
+
function matchesArea(text, area) {
|
|
140847
|
+
const entry = CAPABILITY_PATTERNS.find((candidate) => candidate.area === area);
|
|
140848
|
+
return entry ? entry.regex.test(text) : false;
|
|
140849
|
+
}
|
|
140850
|
+
function estimateEffort(scopeItems, area) {
|
|
140851
|
+
if (scopeItems.length >= 3 || ["auth", "worker", "integration"].includes(area)) return "large";
|
|
140852
|
+
if (scopeItems.length === 2 || ["api", "data", "frontend", "notifications"].includes(area)) return "medium";
|
|
140853
|
+
return "small";
|
|
140854
|
+
}
|
|
140855
|
+
function recommendedLevelFor(area, estimatedEffort) {
|
|
140856
|
+
if (estimatedEffort === "large") return "senior";
|
|
140857
|
+
if (["auth", "worker", "integration"].includes(area)) return "senior";
|
|
140858
|
+
if (estimatedEffort === "medium") return "medior";
|
|
140859
|
+
if (area === "qa") return "junior";
|
|
140860
|
+
return "medior";
|
|
140861
|
+
}
|
|
140862
|
+
function isFoundationArea(area) {
|
|
140863
|
+
return area === "auth" || area === "data" || area === "core";
|
|
140864
|
+
}
|
|
140865
|
+
function filterRelevantItems(items, scopeItems, area) {
|
|
140866
|
+
const normalized = normalizeLines(items);
|
|
140867
|
+
if (normalized.length === 0) return [];
|
|
140868
|
+
const scopeText = scopeItems.join(" ");
|
|
140869
|
+
const matches2 = normalized.filter((item) => matchesArea(item, area) || scopeItems.some((scopeItem) => item.includes(scopeItem)) || item.includes(scopeText));
|
|
140870
|
+
if (matches2.length > 0) return matches2;
|
|
140871
|
+
return normalized.slice(0, Math.min(normalized.length, Math.max(2, scopeItems.length)));
|
|
140872
|
+
}
|
|
140873
|
+
function buildObjective(parentObjective, area, scopeItems) {
|
|
140874
|
+
const label = AREA_LABEL[area];
|
|
140875
|
+
const primaryScope = scopeItems[0] ?? parentObjective;
|
|
140876
|
+
return `Deliver the ${label.toLowerCase()} slice of the parent initiative by completing ${primaryScope.toLowerCase()}.`;
|
|
140877
|
+
}
|
|
140878
|
+
function buildDependencyIndexes(area, priorAreas) {
|
|
140879
|
+
const indexes = [];
|
|
140880
|
+
priorAreas.forEach((priorArea, index) => {
|
|
140881
|
+
if (isFoundationArea(priorArea) && !isFoundationArea(area)) indexes.push(index);
|
|
140882
|
+
if (area !== "auth" && priorArea === "auth") indexes.push(index);
|
|
140883
|
+
if (["api", "frontend", "worker", "notifications"].includes(area) && priorArea === "data") indexes.push(index);
|
|
140884
|
+
if (["worker", "notifications", "integration"].includes(area) && priorArea === "api") indexes.push(index);
|
|
140885
|
+
});
|
|
140886
|
+
return Array.from(new Set(indexes));
|
|
140887
|
+
}
|
|
140888
|
+
function buildDependencyHints(area, priorAreas, allAreas) {
|
|
140889
|
+
const hints = [];
|
|
140890
|
+
if (area !== "auth" && allAreas.includes("auth")) hints.push("Coordinate with the authentication/access child before changing protected flows.");
|
|
140891
|
+
if (["worker", "notifications", "integration"].includes(area) && allAreas.includes("api")) {
|
|
140892
|
+
hints.push("Align payload contracts and triggering conditions with the API/application-flow child.");
|
|
140893
|
+
}
|
|
140894
|
+
if (["api", "frontend", "worker", "notifications"].includes(area) && allAreas.includes("data")) {
|
|
140895
|
+
hints.push("Reuse the canonical data model/migration decisions from the persistence child.");
|
|
140896
|
+
}
|
|
140897
|
+
if (priorAreas.some(isFoundationArea) && !isFoundationArea(area)) {
|
|
140898
|
+
hints.push("Start after foundational contracts are stable, but keep implementation isolated enough for a separate PR.");
|
|
140899
|
+
}
|
|
140900
|
+
return Array.from(new Set(hints));
|
|
140901
|
+
}
|
|
140902
|
+
function buildPlannedChildren(spec, effort) {
|
|
140903
|
+
const scopeItems = normalizeLines(spec.scope_v1);
|
|
140904
|
+
const maxChildren = effort === "xlarge" ? 4 : 3;
|
|
140905
|
+
const orderedAreas = [];
|
|
140906
|
+
const areaToItems = /* @__PURE__ */ new Map();
|
|
140907
|
+
for (const item of scopeItems) {
|
|
140908
|
+
const area = detectCapabilityArea(item, spec.delivery_target);
|
|
140909
|
+
if (!areaToItems.has(area)) {
|
|
140910
|
+
areaToItems.set(area, []);
|
|
140911
|
+
orderedAreas.push(area);
|
|
140912
|
+
}
|
|
140913
|
+
areaToItems.get(area).push(item);
|
|
140914
|
+
}
|
|
140915
|
+
let planned = orderedAreas.map((area) => ({ capabilityArea: area, scopeItems: areaToItems.get(area) ?? [] }));
|
|
140916
|
+
if (planned.length > maxChildren) {
|
|
140917
|
+
const kept = planned.slice(0, maxChildren - 1);
|
|
140918
|
+
const merged = planned.slice(maxChildren - 1).flatMap((entry) => entry.scopeItems);
|
|
140919
|
+
kept.push({ capabilityArea: planned[maxChildren - 1]?.capabilityArea ?? "core", scopeItems: merged });
|
|
140920
|
+
planned = kept;
|
|
140921
|
+
}
|
|
140922
|
+
if (planned.length === 1 && scopeItems.length >= 4) {
|
|
140923
|
+
const midpoint = Math.ceil(scopeItems.length / 2);
|
|
140924
|
+
planned = [
|
|
140925
|
+
{ capabilityArea: planned[0]?.capabilityArea ?? "core", scopeItems: scopeItems.slice(0, midpoint) },
|
|
140926
|
+
{ capabilityArea: planned[0]?.capabilityArea ?? "core", scopeItems: scopeItems.slice(midpoint) }
|
|
140927
|
+
];
|
|
140928
|
+
}
|
|
140929
|
+
return planned.filter((entry) => entry.scopeItems.length > 0);
|
|
140930
|
+
}
|
|
140931
|
+
function buildDescription(opts) {
|
|
140932
|
+
const { issueNumber, child, outOfScope, constraints, risks } = opts;
|
|
140933
|
+
return [
|
|
140934
|
+
"## Objective",
|
|
140935
|
+
child.objective,
|
|
140936
|
+
"",
|
|
140937
|
+
"## Parent Issue",
|
|
140938
|
+
`Parent issue: #${issueNumber}`,
|
|
140939
|
+
"",
|
|
140940
|
+
"## Capability Area",
|
|
140941
|
+
`- ${AREA_LABEL[child.capabilityArea]}`,
|
|
140942
|
+
"",
|
|
140943
|
+
"## Execution Profile",
|
|
140944
|
+
`- Recommended level: ${child.recommendedLevel}`,
|
|
140945
|
+
`- Estimated effort: ${child.estimatedEffort}`,
|
|
140946
|
+
`- Parallelizable: ${child.parallelizable ? "yes" : "no"}`,
|
|
140947
|
+
...child.dependencyHints.map((hint) => `- Dependency hint: ${hint}`),
|
|
140948
|
+
"",
|
|
140949
|
+
"## Scope",
|
|
140950
|
+
...child.scopeItems.map((item) => `- ${item}`),
|
|
140951
|
+
"",
|
|
140952
|
+
"## Acceptance Criteria",
|
|
140953
|
+
...child.acceptanceCriteria.map((item) => `- ${item}`),
|
|
140954
|
+
"",
|
|
140955
|
+
"## Definition of Done",
|
|
140956
|
+
...child.definitionOfDone.map((item) => `- ${item}`),
|
|
140957
|
+
...outOfScope.length > 0 ? ["", "## Out of Scope", ...outOfScope.map((item) => `- ${item}`)] : [],
|
|
140958
|
+
...constraints?.trim() ? ["", "## Constraints", constraints.trim()] : [],
|
|
140959
|
+
...risks.length > 0 ? ["", "## Risks / Coordination Notes", ...risks.map((item) => `- ${item}`)] : []
|
|
140960
|
+
].join("\n");
|
|
140961
|
+
}
|
|
140962
|
+
function buildDecompositionChildDrafts(spec, issueNumber, effort) {
|
|
140963
|
+
const plannedChildren = buildPlannedChildren(spec, effort);
|
|
140964
|
+
const allAreas = plannedChildren.map((entry) => entry.capabilityArea);
|
|
140965
|
+
const areaCounts = /* @__PURE__ */ new Map();
|
|
140966
|
+
return plannedChildren.map((plannedChild, index) => {
|
|
140967
|
+
const estimatedEffort = estimateEffort(plannedChild.scopeItems, plannedChild.capabilityArea);
|
|
140968
|
+
const recommendedLevel = recommendedLevelFor(plannedChild.capabilityArea, estimatedEffort);
|
|
140969
|
+
const priorAreas = plannedChildren.slice(0, index).map((entry) => entry.capabilityArea);
|
|
140970
|
+
const dependencyIndexes = buildDependencyIndexes(plannedChild.capabilityArea, priorAreas);
|
|
140971
|
+
const dependencyHints = buildDependencyHints(plannedChild.capabilityArea, priorAreas, allAreas);
|
|
140972
|
+
const parallelizable = dependencyIndexes.length === 0 && !isFoundationArea(plannedChild.capabilityArea);
|
|
140973
|
+
const acceptanceCriteria = filterRelevantItems(spec.acceptance_criteria, plannedChild.scopeItems, plannedChild.capabilityArea);
|
|
140974
|
+
const definitionOfDone = filterRelevantItems(spec.definition_of_done, plannedChild.scopeItems, plannedChild.capabilityArea);
|
|
140975
|
+
const occurrence = (areaCounts.get(plannedChild.capabilityArea) ?? 0) + 1;
|
|
140976
|
+
areaCounts.set(plannedChild.capabilityArea, occurrence);
|
|
140977
|
+
const titleSuffix = occurrence > 1 ? ` ${occurrence}` : "";
|
|
140978
|
+
const title = `${spec.title} \u2014 ${AREA_LABEL[plannedChild.capabilityArea]}${titleSuffix}`;
|
|
140979
|
+
const child = {
|
|
140980
|
+
title,
|
|
140981
|
+
objective: buildObjective(spec.objective, plannedChild.capabilityArea, plannedChild.scopeItems),
|
|
140982
|
+
scopeItems: plannedChild.scopeItems,
|
|
140983
|
+
acceptanceCriteria,
|
|
140984
|
+
definitionOfDone,
|
|
140985
|
+
recommendedLevel,
|
|
140986
|
+
estimatedEffort,
|
|
140987
|
+
dependencyHints,
|
|
140988
|
+
dependencyIndexes,
|
|
140989
|
+
capabilityArea: plannedChild.capabilityArea,
|
|
140990
|
+
parallelizable,
|
|
140991
|
+
description: ""
|
|
140992
|
+
};
|
|
140993
|
+
child.description = buildDescription({
|
|
140994
|
+
issueNumber,
|
|
140995
|
+
parentObjective: spec.objective,
|
|
140996
|
+
child,
|
|
140997
|
+
outOfScope: normalizeLines(spec.out_of_scope),
|
|
140998
|
+
constraints: spec.constraints,
|
|
140999
|
+
risks: normalizeLines(spec.risks)
|
|
141000
|
+
});
|
|
141001
|
+
return child;
|
|
141002
|
+
});
|
|
141003
|
+
}
|
|
141004
|
+
|
|
141005
|
+
// lib/intake/lib/fidelity-brief.ts
|
|
141006
|
+
var HARD_CONSTRAINT_PATTERNS = [
|
|
141007
|
+
{ regex: /\bmust use ([a-z0-9.+#_-]+)/i, value: "explicit_stack_requirement" },
|
|
141008
|
+
{ regex: /\buse ([a-z0-9.+#_-]+)\b/i, value: "explicit_technology_requirement" },
|
|
141009
|
+
{ regex: /\bwithout ([a-z0-9.+#_-]+)/i, value: "explicit_technology_exclusion" },
|
|
141010
|
+
{ regex: /\bself-host(?:ed)?\b/i, value: "self_hosted" },
|
|
141011
|
+
{ regex: /\boffline\b/i, value: "offline_capable" },
|
|
141012
|
+
{ regex: /\bopen source\b/i, value: "open_source_only" }
|
|
141013
|
+
];
|
|
141014
|
+
var SOFT_PREFERENCE_PATTERNS = [
|
|
141015
|
+
{ regex: /\bprefer\b/i, value: "explicit_preference" },
|
|
141016
|
+
{ regex: /\bclean (?:architecture|code)\b/i, value: "clean_code_preference" },
|
|
141017
|
+
{ regex: /\bmodern\b/i, value: "modern_stack_preference" },
|
|
141018
|
+
{ regex: /\bsimple\b/i, value: "simplicity_preference" }
|
|
141019
|
+
];
|
|
141020
|
+
var QUALITY_EXPECTATION_PATTERNS = [
|
|
141021
|
+
{ regex: /\bproduction[- ]ready\b/i, value: "production_ready" },
|
|
141022
|
+
{ regex: /\bhigh[- ]performance\b|\bperformant\b/i, value: "performance" },
|
|
141023
|
+
{ regex: /\bsecure\b|\bsecurity\b/i, value: "security" },
|
|
141024
|
+
{ regex: /\bscalable\b|\bscale\b/i, value: "scalability" },
|
|
141025
|
+
{ regex: /\bmaintainable\b|\bclean code\b/i, value: "maintainability" },
|
|
141026
|
+
{ regex: /\bwell[- ]tested\b|\bhigh[- ]quality\b/i, value: "high_quality" }
|
|
141027
|
+
];
|
|
141028
|
+
var RISK_SIGNAL_PATTERNS = [
|
|
141029
|
+
{ regex: /\bauth\b|\blogin\b|\boauth\b|\bjwt\b|\brbac\b|\bpermission/i, value: "auth_security_sensitive" },
|
|
141030
|
+
{ regex: /\bpayment\b|\bbilling\b|\bsubscription/i, value: "payment_sensitive" },
|
|
141031
|
+
{ regex: /\bmigration\b|\bschema\b|\bdatabase\b/i, value: "data_model_change" },
|
|
141032
|
+
{ regex: /\bworker\b|\bqueue\b|\bcron\b|\bbackground job\b/i, value: "async_orchestration" },
|
|
141033
|
+
{ regex: /\bintegration\b|\bwebhook\b|\bthird-party\b|\bexternal service\b/i, value: "external_integration" },
|
|
141034
|
+
{ regex: /\bperformance\b|\bhigh traffic\b|\blow latency\b/i, value: "performance_sensitive" }
|
|
141035
|
+
];
|
|
141036
|
+
var EXPLICIT_NON_GOAL_PATTERNS = [
|
|
141037
|
+
{ regex: /\bdo not add ([^.\n]+)/i, value: "no_extra_features" },
|
|
141038
|
+
{ regex: /\bout of scope\b/i, value: "explicit_out_of_scope" },
|
|
141039
|
+
{ regex: /\bnot required\b/i, value: "explicit_not_required" }
|
|
141040
|
+
];
|
|
141041
|
+
function unique(values) {
|
|
141042
|
+
return Array.from(new Set(values.filter((value) => Boolean(value && value.trim()))));
|
|
141043
|
+
}
|
|
141044
|
+
function normalizeText3(text) {
|
|
141045
|
+
return text.replace(/\s+/g, " ").trim();
|
|
141046
|
+
}
|
|
141047
|
+
function inferDeliverable(rawIdea, spec, metadata) {
|
|
141048
|
+
const target = spec?.delivery_target ?? metadata?.delivery_target;
|
|
141049
|
+
if (target && target !== "unknown") return target;
|
|
141050
|
+
const text = rawIdea.toLowerCase();
|
|
141051
|
+
if (/\bcli\b|\bcommand line\b/.test(text)) return "cli";
|
|
141052
|
+
if (/\bapi\b|\bendpoint\b|\brest\b|\bgraphql\b/.test(text)) return "api";
|
|
141053
|
+
if (/\bweb app\b|\bfrontend\b|\bui\b|\bdashboard\b|\bpage\b/.test(text)) return "web-ui";
|
|
141054
|
+
if (/\blibrary\b|\bsdk\b|\bpackage\b/.test(text)) return "library";
|
|
141055
|
+
if (/\bautomation\b|\bworker\b|\bjob\b|\bbot\b/.test(text)) return "automation";
|
|
141056
|
+
return "unknown";
|
|
141057
|
+
}
|
|
141058
|
+
function inferPrimaryObjective(rawIdea, spec) {
|
|
141059
|
+
if (spec?.objective?.trim()) return normalizeText3(spec.objective);
|
|
141060
|
+
const sentence = rawIdea.split(/(?<=[.!?])\s+/)[0] ?? rawIdea;
|
|
141061
|
+
return normalizeText3(sentence);
|
|
141062
|
+
}
|
|
141063
|
+
function collectMatches(text, patterns) {
|
|
141064
|
+
return unique(patterns.map((pattern) => pattern.regex.test(text) ? pattern.value : null));
|
|
141065
|
+
}
|
|
141066
|
+
function deriveAmbiguityFlags(rawIdea, deliverable, requestedStack, inferredStack, spec) {
|
|
141067
|
+
const flags = [];
|
|
141068
|
+
if (deliverable === "unknown") flags.push("ambiguous_deliverable");
|
|
141069
|
+
if (!requestedStack && !inferredStack && /\bbuild|create|make\b/i.test(rawIdea) && deliverable !== "unknown") {
|
|
141070
|
+
flags.push("missing_stack_preference");
|
|
141071
|
+
}
|
|
141072
|
+
if (!spec?.scope_v1?.length) flags.push("missing_structured_scope");
|
|
141073
|
+
if (spec && spec.acceptance_criteria.length === 0) flags.push("missing_acceptance_criteria");
|
|
141074
|
+
return unique(flags);
|
|
141075
|
+
}
|
|
141076
|
+
function deriveConfidence(ambiguityFlags, spec, metadata) {
|
|
141077
|
+
if (metadata?.stack_confidence === "high" && ambiguityFlags.length === 0 && (spec?.acceptance_criteria.length ?? 0) >= 2) {
|
|
141078
|
+
return "high";
|
|
141079
|
+
}
|
|
141080
|
+
if (ambiguityFlags.some((flag) => flag === "ambiguous_deliverable" || flag === "missing_structured_scope")) {
|
|
141081
|
+
return "low";
|
|
141082
|
+
}
|
|
141083
|
+
return "medium";
|
|
141084
|
+
}
|
|
141085
|
+
function buildFidelityBrief(opts) {
|
|
141086
|
+
const rawIdea = normalizeText3(opts.rawIdea);
|
|
141087
|
+
const spec = opts.spec;
|
|
141088
|
+
const metadata = opts.metadata;
|
|
141089
|
+
const combinedText = [
|
|
141090
|
+
rawIdea,
|
|
141091
|
+
spec?.objective,
|
|
141092
|
+
spec?.constraints,
|
|
141093
|
+
...spec?.scope_v1 ?? [],
|
|
141094
|
+
...spec?.out_of_scope ?? [],
|
|
141095
|
+
...spec?.acceptance_criteria ?? [],
|
|
141096
|
+
...spec?.definition_of_done ?? [],
|
|
141097
|
+
...spec?.risks ?? []
|
|
141098
|
+
].filter(Boolean).join("\n");
|
|
141099
|
+
const requestedDeliverable = inferDeliverable(rawIdea, spec, metadata);
|
|
141100
|
+
const requestedStack = metadata?.stack_hint ?? null;
|
|
141101
|
+
const inferredStack = requestedStack ?? null;
|
|
141102
|
+
const hardConstraints = collectMatches(combinedText, HARD_CONSTRAINT_PATTERNS);
|
|
141103
|
+
const softPreferences = collectMatches(combinedText, SOFT_PREFERENCE_PATTERNS);
|
|
141104
|
+
const explicitNonGoals = unique([
|
|
141105
|
+
...collectMatches(combinedText, EXPLICIT_NON_GOAL_PATTERNS),
|
|
141106
|
+
...(spec?.out_of_scope ?? []).map((item) => normalizeText3(item))
|
|
141107
|
+
]);
|
|
141108
|
+
const qualityExpectations = collectMatches(combinedText, QUALITY_EXPECTATION_PATTERNS);
|
|
141109
|
+
const riskSignals = collectMatches(combinedText, RISK_SIGNAL_PATTERNS);
|
|
141110
|
+
const ambiguityFlags = deriveAmbiguityFlags(rawIdea, requestedDeliverable, requestedStack, inferredStack, spec);
|
|
141111
|
+
const confidence = deriveConfidence(ambiguityFlags, spec, metadata);
|
|
141112
|
+
return {
|
|
141113
|
+
primary_objective: inferPrimaryObjective(rawIdea, spec),
|
|
141114
|
+
requested_deliverable: requestedDeliverable,
|
|
141115
|
+
requested_stack: requestedStack,
|
|
141116
|
+
inferred_stack: inferredStack,
|
|
141117
|
+
hard_constraints: hardConstraints,
|
|
141118
|
+
soft_preferences: softPreferences,
|
|
141119
|
+
explicit_non_goals: explicitNonGoals,
|
|
141120
|
+
quality_expectations: qualityExpectations,
|
|
141121
|
+
ambiguity_flags: ambiguityFlags,
|
|
141122
|
+
risk_signals: riskSignals,
|
|
141123
|
+
confidence
|
|
141124
|
+
};
|
|
141125
|
+
}
|
|
141126
|
+
|
|
140041
141127
|
// lib/intake/steps/triage.ts
|
|
140042
141128
|
import { createRequire as createRequire3 } from "node:module";
|
|
140043
141129
|
var _require3 = createRequire3(import.meta.url);
|
|
140044
141130
|
var cachedMatrix = null;
|
|
141131
|
+
function shouldAutoDecompose(decision) {
|
|
141132
|
+
if (!decision.readyForDispatch || decision.specQualityBlock) return false;
|
|
141133
|
+
if (decision.effort === "xlarge") return true;
|
|
141134
|
+
if (decision.effort !== "large") return false;
|
|
141135
|
+
return decision.parallelizability !== "low" && decision.coupling !== "high";
|
|
141136
|
+
}
|
|
141137
|
+
function computeMaxParallelChildren(drafts) {
|
|
141138
|
+
const parallelizableCount = drafts.filter((draft) => draft.parallelizable).length;
|
|
141139
|
+
return Math.min(4, Math.max(1, parallelizableCount || 1));
|
|
141140
|
+
}
|
|
140045
141141
|
function loadMatrix() {
|
|
140046
141142
|
if (cachedMatrix) return cachedMatrix;
|
|
140047
141143
|
cachedMatrix = _require3("../configs/triage-matrix.json");
|
|
140048
141144
|
return cachedMatrix;
|
|
140049
141145
|
}
|
|
140050
|
-
function buildChildDrafts(payload, issueNumber, effort) {
|
|
140051
|
-
const spec = payload.spec;
|
|
140052
|
-
const scopeItems = spec.scope_v1.filter((item) => item.trim().length > 0);
|
|
140053
|
-
const maxChildren = effort === "xlarge" ? 4 : 3;
|
|
140054
|
-
const chunkSize = 2;
|
|
140055
|
-
const drafts = [];
|
|
140056
|
-
for (let i2 = 0; i2 < scopeItems.length && drafts.length < maxChildren; i2 += chunkSize) {
|
|
140057
|
-
const slice = scopeItems.slice(i2, i2 + chunkSize);
|
|
140058
|
-
if (slice.length === 0) continue;
|
|
140059
|
-
const childIndex = drafts.length + 1;
|
|
140060
|
-
drafts.push({
|
|
140061
|
-
title: `${spec.title} \u2014 Part ${childIndex}`,
|
|
140062
|
-
description: [
|
|
140063
|
-
`## Objective`,
|
|
140064
|
-
spec.objective,
|
|
140065
|
-
"",
|
|
140066
|
-
`## Parent Issue`,
|
|
140067
|
-
`Parent issue: #${issueNumber}`,
|
|
140068
|
-
"",
|
|
140069
|
-
`## This Part`,
|
|
140070
|
-
...slice.map((item) => `- ${item}`),
|
|
140071
|
-
"",
|
|
140072
|
-
`## Acceptance Criteria`,
|
|
140073
|
-
...spec.acceptance_criteria.map((item) => `- ${item}`),
|
|
140074
|
-
"",
|
|
140075
|
-
`## Definition of Done`,
|
|
140076
|
-
...spec.definition_of_done.map((item) => `- ${item}`)
|
|
140077
|
-
].join("\n")
|
|
140078
|
-
});
|
|
140079
|
-
}
|
|
140080
|
-
return drafts;
|
|
140081
|
-
}
|
|
140082
141146
|
var triageStep = {
|
|
140083
141147
|
name: "triage",
|
|
140084
141148
|
shouldRun: (payload) => !!payload.issues?.length && !payload.dry_run,
|
|
@@ -140088,6 +141152,11 @@ var triageStep = {
|
|
|
140088
141152
|
const impact = payload.impact;
|
|
140089
141153
|
const issue2 = payload.issues[0];
|
|
140090
141154
|
ctx.log(`Running triage for issue #${issue2.number}`);
|
|
141155
|
+
const fidelityBrief = buildFidelityBrief({
|
|
141156
|
+
rawIdea: payload.raw_idea,
|
|
141157
|
+
spec,
|
|
141158
|
+
metadata: payload.metadata
|
|
141159
|
+
});
|
|
140091
141160
|
const decision = runTriageLogic({
|
|
140092
141161
|
type: spec.type,
|
|
140093
141162
|
deliveryTarget: spec.delivery_target,
|
|
@@ -140100,8 +141169,9 @@ var triageStep = {
|
|
|
140100
141169
|
rawIdea: payload.raw_idea,
|
|
140101
141170
|
acText: spec.acceptance_criteria.join("\n"),
|
|
140102
141171
|
scopeText: spec.scope_v1.join("\n"),
|
|
141172
|
+
dodText: spec.definition_of_done.join("\n"),
|
|
140103
141173
|
oosText: spec.out_of_scope.join("\n"),
|
|
140104
|
-
authSignal: payload.
|
|
141174
|
+
authSignal: /\b(login|register|jwt|oauth|auth|role-based access|rbac|permission)\b/i.test(`${payload.raw_idea} ${spec.objective}`)
|
|
140105
141175
|
}, matrix);
|
|
140106
141176
|
const repoUrl = payload.scaffold?.repo_url ?? payload.metadata?.repo_url ?? "";
|
|
140107
141177
|
const repoPath = payload.provisioning?.repo_local ?? payload.scaffold?.repo_local ?? payload.metadata?.repo_path ?? payload.project_map?.root ?? void 0;
|
|
@@ -140112,8 +141182,9 @@ var triageStep = {
|
|
|
140112
141182
|
if (decision.typeLabel) allLabels.push(decision.typeLabel);
|
|
140113
141183
|
if (!decision.readyForDispatch || decision.specQualityBlock) allLabels.push("needs-human");
|
|
140114
141184
|
const uniqueLabels = Array.from(new Set(allLabels.filter(Boolean)));
|
|
140115
|
-
const
|
|
140116
|
-
const
|
|
141185
|
+
const decompositionRequested = decision.readyForDispatch && !decision.specQualityBlock && ["large", "xlarge"].includes(decision.effort);
|
|
141186
|
+
const shouldDecompose = shouldAutoDecompose(decision);
|
|
141187
|
+
const decompositionDrafts = shouldDecompose ? buildDecompositionChildDrafts(spec, issue2.number, decision.effort) : [];
|
|
140117
141188
|
const canDecompose = decompositionDrafts.length >= 2;
|
|
140118
141189
|
let createdChildIssueNumbers = [];
|
|
140119
141190
|
if (ctx.createIssueProvider && repoPath) {
|
|
@@ -140134,25 +141205,55 @@ var triageStep = {
|
|
|
140134
141205
|
} else if (canDecompose) {
|
|
140135
141206
|
await provider.addLabel(issue2.number, "decomposition:parent");
|
|
140136
141207
|
const childIssues = [];
|
|
141208
|
+
const createdChildren = [];
|
|
140137
141209
|
for (const draft of decompositionDrafts) {
|
|
140138
141210
|
const child = await provider.createIssue(draft.title, draft.description, initialLabel);
|
|
140139
141211
|
childIssues.push({ iid: child.iid, title: child.title, web_url: child.web_url });
|
|
141212
|
+
createdChildren.push({ iid: child.iid, title: child.title, web_url: child.web_url, draft });
|
|
140140
141213
|
createdChildIssueNumbers.push(child.iid);
|
|
140141
141214
|
for (const label of uniqueLabels) {
|
|
140142
141215
|
await provider.addLabel(child.iid, label);
|
|
140143
141216
|
}
|
|
140144
141217
|
await provider.addLabel(child.iid, "decomposition:child");
|
|
141218
|
+
await provider.transitionLabel(child.iid, initialLabel, decision.targetState);
|
|
141219
|
+
if (decision.targetState === "To Do" && decision.dispatchLabel) {
|
|
141220
|
+
await provider.addLabel(child.iid, decision.dispatchLabel);
|
|
141221
|
+
}
|
|
141222
|
+
if (decision.targetState === "To Do") {
|
|
141223
|
+
await provider.addLabel(child.iid, `developer:${draft.recommendedLevel}`);
|
|
141224
|
+
}
|
|
141225
|
+
}
|
|
141226
|
+
if (projectSlug) {
|
|
141227
|
+
for (const child of createdChildren) {
|
|
141228
|
+
await updateIssueRuntime(ctx.workspaceDir, projectSlug, child.iid, {
|
|
141229
|
+
parentIssueId: issue2.number,
|
|
141230
|
+
dependencyIssueIds: child.draft.dependencyIndexes.map((index) => createdChildren[index]?.iid).filter((value) => Number.isFinite(value)),
|
|
141231
|
+
childReadyForDispatch: decision.targetState === "To Do",
|
|
141232
|
+
parallelizable: child.draft.parallelizable,
|
|
141233
|
+
recommendedLevel: child.draft.recommendedLevel,
|
|
141234
|
+
decompositionMode: "none",
|
|
141235
|
+
decompositionStatus: null
|
|
141236
|
+
}).catch(() => {
|
|
141237
|
+
});
|
|
141238
|
+
}
|
|
141239
|
+
await updateIssueRuntime(ctx.workspaceDir, projectSlug, issue2.number, {
|
|
141240
|
+
childIssueIds: createdChildIssueNumbers,
|
|
141241
|
+
maxParallelChildren: computeMaxParallelChildren(decompositionDrafts),
|
|
141242
|
+
decompositionMode: "parent_child",
|
|
141243
|
+
decompositionStatus: "active"
|
|
141244
|
+
}).catch(() => {
|
|
141245
|
+
});
|
|
140145
141246
|
}
|
|
140146
141247
|
await provider.addComment(issue2.number, [
|
|
140147
141248
|
"## Decomposition Plan",
|
|
140148
141249
|
...childIssues.map((child) => `- [ ] #${child.iid} ${child.title}`)
|
|
140149
141250
|
].join("\n"));
|
|
140150
|
-
} else if (shouldDecompose) {
|
|
141251
|
+
} else if (decompositionRequested && shouldDecompose) {
|
|
140151
141252
|
await provider.addLabel(issue2.number, "needs-human");
|
|
140152
141253
|
await provider.addComment(issue2.number, [
|
|
140153
|
-
"\u26A0\uFE0F Automatic decomposition was requested, but the generated spec did not yield at least two independently scoped child tasks.",
|
|
141254
|
+
shouldDecompose ? "\u26A0\uFE0F Automatic decomposition was requested, but the generated spec did not yield at least two independently scoped child tasks." : "\u26A0\uFE0F This request is large, but triage detected high coupling / low parallelizability. Fabrica will keep it as one executable issue unless a human explicitly splits it.",
|
|
140154
141255
|
"",
|
|
140155
|
-
"Refine the scope/acceptance criteria or split the plan manually before dispatch."
|
|
141256
|
+
shouldDecompose ? "Refine the scope/acceptance criteria or split the plan manually before dispatch." : "If you still want multiple child issues, split the plan manually along stable boundaries before dispatch."
|
|
140156
141257
|
].join("\n"));
|
|
140157
141258
|
} else if (decision.readyForDispatch) {
|
|
140158
141259
|
await provider.transitionLabel(issue2.number, initialLabel, decision.targetState);
|
|
@@ -140214,6 +141315,11 @@ var triageStep = {
|
|
|
140214
141315
|
const triage = {
|
|
140215
141316
|
priority: decision.priority,
|
|
140216
141317
|
effort: decision.effort,
|
|
141318
|
+
complexity: decision.complexity,
|
|
141319
|
+
coupling: decision.coupling,
|
|
141320
|
+
parallelizability: decision.parallelizability,
|
|
141321
|
+
quality_criticality: decision.qualityCriticality,
|
|
141322
|
+
risk_profile: decision.riskProfile,
|
|
140217
141323
|
target_state: decision.targetState,
|
|
140218
141324
|
project_slug: payload.scaffold?.project_slug ?? null,
|
|
140219
141325
|
project_channel_id: null,
|
|
@@ -140223,15 +141329,41 @@ var triageStep = {
|
|
|
140223
141329
|
errors: [
|
|
140224
141330
|
...decision.errors,
|
|
140225
141331
|
...decision.specQualityBlock ? ["spec_quality_block"] : [],
|
|
140226
|
-
...shouldDecompose && !canDecompose ? ["decomposition_needs_human"] : []
|
|
141332
|
+
...decompositionRequested && shouldDecompose && !canDecompose ? ["decomposition_needs_human"] : []
|
|
140227
141333
|
],
|
|
140228
141334
|
decomposition_mode: canDecompose ? "parent_child" : "none",
|
|
140229
141335
|
child_issue_numbers: createdChildIssueNumbers
|
|
140230
141336
|
};
|
|
140231
141337
|
ctx.log(`Triage: ${triage.priority}, effort=${triage.effort}, ready=${triage.ready_for_dispatch}`);
|
|
141338
|
+
const bootstrapConversationId = payload.metadata?.source === "telegram-dm-bootstrap" ? payload.metadata?.channel_id : null;
|
|
141339
|
+
if (bootstrapConversationId) {
|
|
141340
|
+
await upsertTelegramBootstrapSession(ctx.workspaceDir, {
|
|
141341
|
+
conversationId: String(bootstrapConversationId),
|
|
141342
|
+
rawIdea: payload.raw_idea,
|
|
141343
|
+
projectName: payload.metadata?.project_name ?? null,
|
|
141344
|
+
stackHint: payload.metadata?.stack_hint ?? null,
|
|
141345
|
+
repoUrl: payload.provisioning?.repo_url ?? payload.scaffold?.repo_url ?? payload.metadata?.repo_url ?? null,
|
|
141346
|
+
repoPath: payload.provisioning?.repo_local ?? payload.scaffold?.repo_local ?? payload.metadata?.repo_path ?? null,
|
|
141347
|
+
status: triage.ready_for_dispatch ? "dispatching" : "completed",
|
|
141348
|
+
bootstrapStep: triage.ready_for_dispatch ? "project_ticked" : "completed",
|
|
141349
|
+
projectSlug: payload.metadata?.project_slug ?? payload.scaffold?.project_slug ?? null,
|
|
141350
|
+
issueId: issue2.number,
|
|
141351
|
+
issueUrl: issue2.url,
|
|
141352
|
+
projectChannelId: triage.project_channel_id ?? payload.metadata?.channel_id ?? null,
|
|
141353
|
+
messageThreadId: payload.metadata?.message_thread_id ?? null,
|
|
141354
|
+
triageReadyForDispatch: triage.ready_for_dispatch,
|
|
141355
|
+
triageErrors: triage.errors
|
|
141356
|
+
}).catch(() => {
|
|
141357
|
+
});
|
|
141358
|
+
}
|
|
140232
141359
|
return {
|
|
140233
141360
|
...payload,
|
|
140234
141361
|
step: "triage",
|
|
141362
|
+
fidelity_brief: fidelityBrief,
|
|
141363
|
+
metadata: {
|
|
141364
|
+
...payload.metadata,
|
|
141365
|
+
fidelity_brief: fidelityBrief
|
|
141366
|
+
},
|
|
140235
141367
|
triage
|
|
140236
141368
|
};
|
|
140237
141369
|
}
|
|
@@ -140359,6 +141491,43 @@ function mergeArtifacts(primary, secondary) {
|
|
|
140359
141491
|
return merged;
|
|
140360
141492
|
}
|
|
140361
141493
|
|
|
141494
|
+
// lib/intake/lib/clarification-policy.ts
|
|
141495
|
+
function detectScopeClarificationNeed(rawIdea, stackHint) {
|
|
141496
|
+
const text = rawIdea.toLowerCase();
|
|
141497
|
+
if (/\b(livre|free.?choice|your.?call|pode.?escolher|voc[eê].?decide|qualquer)\b/i.test(text)) {
|
|
141498
|
+
return { ask: false };
|
|
141499
|
+
}
|
|
141500
|
+
const subsystems = [
|
|
141501
|
+
/\bauth\b|\blogin\b|\bregister\b|\bjwt\b|\boauth\b/,
|
|
141502
|
+
/\bpayment\b|\bbilling\b|\bsubscription\b/,
|
|
141503
|
+
/\bemail\b|\bnotification\b|\balert\b|\bsms\b/,
|
|
141504
|
+
/\badmin\b|\bdashboard\b|\breport\b/,
|
|
141505
|
+
/\bqueue\b|\bworker\b|\bcron\b|\bjob\b/,
|
|
141506
|
+
/\bdatabase\b|\bmigration\b|\bschema\b/,
|
|
141507
|
+
/\bapi\b|\bwebhook\b|\bintegration\b|\bexternal service\b/
|
|
141508
|
+
].filter((regex) => regex.test(text)).length;
|
|
141509
|
+
const missingKeyTechChoice = !stackHint || (!/postgres|mysql|sqlite|mongodb/i.test(text) || !/jwt|oauth|session|rbac/i.test(text));
|
|
141510
|
+
if (subsystems >= 3 && missingKeyTechChoice) {
|
|
141511
|
+
return { ask: true, kind: "scope", reason: "scope_ambiguity_across_multiple_subsystems" };
|
|
141512
|
+
}
|
|
141513
|
+
return { ask: false };
|
|
141514
|
+
}
|
|
141515
|
+
function decideBootstrapClarification(opts) {
|
|
141516
|
+
if (opts.scopeAmbiguous) {
|
|
141517
|
+
return { ask: true, kind: "scope", reason: "scope_ambiguity_requires_structuring" };
|
|
141518
|
+
}
|
|
141519
|
+
if (!opts.stackHint && !opts.projectName) {
|
|
141520
|
+
return { ask: true, kind: "stack_and_name", reason: "missing_stack_and_name" };
|
|
141521
|
+
}
|
|
141522
|
+
if (!opts.stackHint) {
|
|
141523
|
+
return { ask: true, kind: "stack", reason: "missing_stack" };
|
|
141524
|
+
}
|
|
141525
|
+
if (!opts.projectName) {
|
|
141526
|
+
return { ask: true, kind: "name", reason: "missing_project_name" };
|
|
141527
|
+
}
|
|
141528
|
+
return { ask: false };
|
|
141529
|
+
}
|
|
141530
|
+
|
|
140362
141531
|
// lib/services/tick.ts
|
|
140363
141532
|
init_audit();
|
|
140364
141533
|
|
|
@@ -140576,23 +141745,25 @@ init_workflow();
|
|
|
140576
141745
|
|
|
140577
141746
|
// lib/dispatch/dispatch-dedup.ts
|
|
140578
141747
|
init_constants();
|
|
140579
|
-
import
|
|
140580
|
-
import
|
|
140581
|
-
import { createHash as
|
|
141748
|
+
import fs36 from "node:fs/promises";
|
|
141749
|
+
import path36 from "node:path";
|
|
141750
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
140582
141751
|
var DEDUP_FILE = "dispatch-dedup.ndjson";
|
|
140583
141752
|
var BUCKET_MS = 5 * 6e4;
|
|
140584
141753
|
var DEFAULT_TTL_MS2 = 30 * 6e4;
|
|
141754
|
+
var ACTIVE_CLAIM_TTL_MS = 10 * 6e4;
|
|
141755
|
+
var activeClaims = /* @__PURE__ */ new Map();
|
|
140585
141756
|
function dedupPath(workspaceDir) {
|
|
140586
|
-
return
|
|
141757
|
+
return path36.join(workspaceDir, DATA_DIR, DEDUP_FILE);
|
|
140587
141758
|
}
|
|
140588
141759
|
function computeDispatchId(projectSlug, issueId, role, level, now2 = Date.now()) {
|
|
140589
141760
|
const bucket = Math.floor(now2 / BUCKET_MS);
|
|
140590
141761
|
const input = `${projectSlug}:${issueId}:${role}:${level}:${bucket}`;
|
|
140591
|
-
return
|
|
141762
|
+
return createHash5("sha256").update(input).digest("hex").slice(0, 16);
|
|
140592
141763
|
}
|
|
140593
141764
|
async function readEntries2(filePath) {
|
|
140594
141765
|
try {
|
|
140595
|
-
const content = await
|
|
141766
|
+
const content = await fs36.readFile(filePath, "utf-8");
|
|
140596
141767
|
return content.split("\n").filter(Boolean).map((line) => {
|
|
140597
141768
|
try {
|
|
140598
141769
|
return JSON.parse(line);
|
|
@@ -140605,15 +141776,34 @@ async function readEntries2(filePath) {
|
|
|
140605
141776
|
throw err;
|
|
140606
141777
|
}
|
|
140607
141778
|
}
|
|
141779
|
+
function sweepExpiredClaims(now2 = Date.now()) {
|
|
141780
|
+
for (const [dispatchId, expiresAt] of activeClaims.entries()) {
|
|
141781
|
+
if (expiresAt <= now2) activeClaims.delete(dispatchId);
|
|
141782
|
+
}
|
|
141783
|
+
}
|
|
140608
141784
|
async function isDuplicate(workspaceDir, dispatchId) {
|
|
141785
|
+
sweepExpiredClaims();
|
|
141786
|
+
if (activeClaims.has(dispatchId)) return true;
|
|
140609
141787
|
const entries = await readEntries2(dedupPath(workspaceDir));
|
|
140610
141788
|
return entries.some((e2) => e2.id === dispatchId);
|
|
140611
141789
|
}
|
|
141790
|
+
async function claimDispatch(workspaceDir, dispatchId) {
|
|
141791
|
+
sweepExpiredClaims();
|
|
141792
|
+
if (activeClaims.has(dispatchId)) return false;
|
|
141793
|
+
const entries = await readEntries2(dedupPath(workspaceDir));
|
|
141794
|
+
if (entries.some((e2) => e2.id === dispatchId)) return false;
|
|
141795
|
+
activeClaims.set(dispatchId, Date.now() + ACTIVE_CLAIM_TTL_MS);
|
|
141796
|
+
return true;
|
|
141797
|
+
}
|
|
141798
|
+
function releaseDispatchClaim(dispatchId) {
|
|
141799
|
+
activeClaims.delete(dispatchId);
|
|
141800
|
+
}
|
|
140612
141801
|
async function recordDispatch(workspaceDir, dispatchId) {
|
|
140613
141802
|
const filePath = dedupPath(workspaceDir);
|
|
140614
|
-
await
|
|
141803
|
+
await fs36.mkdir(path36.dirname(filePath), { recursive: true });
|
|
140615
141804
|
const entry = { id: dispatchId, ts: Date.now() };
|
|
140616
|
-
await
|
|
141805
|
+
await fs36.appendFile(filePath, JSON.stringify(entry) + "\n", "utf-8");
|
|
141806
|
+
activeClaims.delete(dispatchId);
|
|
140617
141807
|
}
|
|
140618
141808
|
async function cleanupExpired(workspaceDir, ttlMs = DEFAULT_TTL_MS2) {
|
|
140619
141809
|
const filePath = dedupPath(workspaceDir);
|
|
@@ -140623,8 +141813,8 @@ async function cleanupExpired(workspaceDir, ttlMs = DEFAULT_TTL_MS2) {
|
|
|
140623
141813
|
if (kept.length < entries.length) {
|
|
140624
141814
|
const content = kept.map((e2) => JSON.stringify(e2)).join("\n") + (kept.length > 0 ? "\n" : "");
|
|
140625
141815
|
const tmpPath = filePath + ".tmp";
|
|
140626
|
-
await
|
|
140627
|
-
await
|
|
141816
|
+
await fs36.writeFile(tmpPath, content, "utf-8");
|
|
141817
|
+
await fs36.rename(tmpPath, filePath);
|
|
140628
141818
|
}
|
|
140629
141819
|
}
|
|
140630
141820
|
|
|
@@ -140701,7 +141891,7 @@ async function projectTick(opts) {
|
|
|
140701
141891
|
continue;
|
|
140702
141892
|
}
|
|
140703
141893
|
}
|
|
140704
|
-
const next = await findNextIssueForRole(provider, role, workflow, instanceName);
|
|
141894
|
+
const next = await findNextIssueForRole(provider, role, workflow, instanceName, fresh);
|
|
140705
141895
|
if (!next) continue;
|
|
140706
141896
|
const { issue: issue2, label: currentLabel } = next;
|
|
140707
141897
|
const targetLabel = getActiveLabel(workflow, role);
|
|
@@ -140842,11 +142032,6 @@ async function projectTick(opts) {
|
|
|
140842
142032
|
skipped.push({ role, reason: `${effectiveLevel} slots full` });
|
|
140843
142033
|
continue;
|
|
140844
142034
|
}
|
|
140845
|
-
const dispatchId = computeDispatchId(projectSlug, issue2.iid, role, effectiveLevel);
|
|
140846
|
-
if (await isDuplicate(workspaceDir, dispatchId)) {
|
|
140847
|
-
skipped.push({ role, reason: `dispatch_dedup: ${dispatchId}` });
|
|
140848
|
-
continue;
|
|
140849
|
-
}
|
|
140850
142035
|
if (role === "developer" || role === "tester") {
|
|
140851
142036
|
const stack = fresh.stack;
|
|
140852
142037
|
if (!stack) {
|
|
@@ -140893,6 +142078,17 @@ async function projectTick(opts) {
|
|
|
140893
142078
|
continue;
|
|
140894
142079
|
}
|
|
140895
142080
|
}
|
|
142081
|
+
const dispatchId = computeDispatchId(projectSlug, issue2.iid, role, effectiveLevel);
|
|
142082
|
+
if (!dryRun) {
|
|
142083
|
+
if (await isDuplicate(workspaceDir, dispatchId)) {
|
|
142084
|
+
skipped.push({ role, reason: `dispatch_dedup: ${dispatchId}` });
|
|
142085
|
+
continue;
|
|
142086
|
+
}
|
|
142087
|
+
if (!await claimDispatch(workspaceDir, dispatchId)) {
|
|
142088
|
+
skipped.push({ role, reason: `dispatch_claimed: ${dispatchId}` });
|
|
142089
|
+
continue;
|
|
142090
|
+
}
|
|
142091
|
+
}
|
|
140896
142092
|
if (dryRun) {
|
|
140897
142093
|
const existingSession = roleWorker.levels[effectiveLevel]?.[freeSlot]?.sessionKey;
|
|
140898
142094
|
pickups.push({
|
|
@@ -140942,6 +142138,7 @@ async function projectTick(opts) {
|
|
|
140942
142138
|
await recordDispatch(workspaceDir, dispatchId).catch(() => {
|
|
140943
142139
|
});
|
|
140944
142140
|
} catch (err) {
|
|
142141
|
+
releaseDispatchClaim(dispatchId);
|
|
140945
142142
|
skipped.push({ role, reason: `Dispatch failed: ${err.message}` });
|
|
140946
142143
|
continue;
|
|
140947
142144
|
}
|
|
@@ -140967,281 +142164,6 @@ function getFeedbackQueueLabel(workflow) {
|
|
|
140967
142164
|
// lib/dispatch/telegram-bootstrap-hook.ts
|
|
140968
142165
|
init_audit();
|
|
140969
142166
|
init_zod();
|
|
140970
|
-
|
|
140971
|
-
// lib/dispatch/telegram-bootstrap-session.ts
|
|
140972
|
-
init_constants();
|
|
140973
|
-
import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
|
|
140974
|
-
import fs35 from "node:fs/promises";
|
|
140975
|
-
import path36 from "node:path";
|
|
140976
|
-
var SESSION_TTL_MS = 10 * 6e4;
|
|
140977
|
-
var CLASSIFYING_TTL_MS = 15e3;
|
|
140978
|
-
var RELEASED_CLASSIFY_ERRORS = /* @__PURE__ */ new Set([
|
|
140979
|
-
"classify_invalid_result",
|
|
140980
|
-
"classify_low_confidence",
|
|
140981
|
-
"classify_not_project",
|
|
140982
|
-
"classify_result_expired"
|
|
140983
|
-
]);
|
|
140984
|
-
function toCanonicalTelegramBootstrapConversationId(conversationId) {
|
|
140985
|
-
const trimmed = conversationId.trim();
|
|
140986
|
-
if (!trimmed) return trimmed;
|
|
140987
|
-
return trimmed.startsWith("telegram:") ? trimmed : `telegram:${trimmed}`;
|
|
140988
|
-
}
|
|
140989
|
-
function sessionsDir(workspaceDir) {
|
|
140990
|
-
return path36.join(workspaceDir, DATA_DIR, "bootstrap-sessions");
|
|
140991
|
-
}
|
|
140992
|
-
function sessionPath(workspaceDir, conversationId) {
|
|
140993
|
-
return path36.join(
|
|
140994
|
-
sessionsDir(workspaceDir),
|
|
140995
|
-
`${toCanonicalTelegramBootstrapConversationId(conversationId)}.json`
|
|
140996
|
-
);
|
|
140997
|
-
}
|
|
140998
|
-
function alternateSessionPath(workspaceDir, conversationId) {
|
|
140999
|
-
const canonical = toCanonicalTelegramBootstrapConversationId(conversationId);
|
|
141000
|
-
const bare = canonical.startsWith("telegram:") ? canonical.slice("telegram:".length) : canonical;
|
|
141001
|
-
if (!bare || bare === canonical) return null;
|
|
141002
|
-
return path36.join(sessionsDir(workspaceDir), `${bare}.json`);
|
|
141003
|
-
}
|
|
141004
|
-
function normalizeStoredSession(session) {
|
|
141005
|
-
const canonicalConversationId = toCanonicalTelegramBootstrapConversationId(session.conversationId);
|
|
141006
|
-
if (canonicalConversationId === session.conversationId) {
|
|
141007
|
-
return session;
|
|
141008
|
-
}
|
|
141009
|
-
return {
|
|
141010
|
-
...session,
|
|
141011
|
-
id: buildBootstrapSessionId(canonicalConversationId, session.rawIdea),
|
|
141012
|
-
conversationId: canonicalConversationId
|
|
141013
|
-
};
|
|
141014
|
-
}
|
|
141015
|
-
function stableHash(input) {
|
|
141016
|
-
return createHash5("sha256").update(input).digest("hex").slice(0, 16);
|
|
141017
|
-
}
|
|
141018
|
-
function buildBootstrapSessionId(conversationId, rawIdea) {
|
|
141019
|
-
return `tgdm-${conversationId}-${stableHash(rawIdea.trim().toLowerCase())}`;
|
|
141020
|
-
}
|
|
141021
|
-
function buildBootstrapRequestFingerprint(input) {
|
|
141022
|
-
return stableHash(JSON.stringify({
|
|
141023
|
-
rawIdea: input.rawIdea.trim().toLowerCase(),
|
|
141024
|
-
projectName: input.projectName?.trim().toLowerCase() || null,
|
|
141025
|
-
stackHint: input.stackHint?.trim().toLowerCase() || null,
|
|
141026
|
-
repoUrl: input.repoUrl?.trim().toLowerCase() || null,
|
|
141027
|
-
repoPath: input.repoPath?.trim().toLowerCase() || null
|
|
141028
|
-
}));
|
|
141029
|
-
}
|
|
141030
|
-
function buildBootstrapRequestHash(input) {
|
|
141031
|
-
return buildBootstrapRequestFingerprint(input);
|
|
141032
|
-
}
|
|
141033
|
-
function nextSuppressUntil(status) {
|
|
141034
|
-
const ttl = status === "classifying" || status === "pending_classify" ? CLASSIFYING_TTL_MS : SESSION_TTL_MS;
|
|
141035
|
-
return new Date(Date.now() + ttl).toISOString();
|
|
141036
|
-
}
|
|
141037
|
-
function isReleasedClassifyFailure(error48) {
|
|
141038
|
-
return Boolean(error48 && RELEASED_CLASSIFY_ERRORS.has(error48));
|
|
141039
|
-
}
|
|
141040
|
-
function resolveNullableField(inputValue, existingValue, fallback = null) {
|
|
141041
|
-
return inputValue !== void 0 ? inputValue : existingValue ?? fallback;
|
|
141042
|
-
}
|
|
141043
|
-
function defaultNextRetryAtForStatus(status, existingValue) {
|
|
141044
|
-
if (status === "bootstrapping" || status === "dispatching") {
|
|
141045
|
-
return existingValue ?? null;
|
|
141046
|
-
}
|
|
141047
|
-
return null;
|
|
141048
|
-
}
|
|
141049
|
-
var MONOTONIC_BOOTSTRAP_FIELDS = [
|
|
141050
|
-
"ackSentAt",
|
|
141051
|
-
"projectRegisteredAt",
|
|
141052
|
-
"topicKickoffSentAt",
|
|
141053
|
-
"projectTickedAt",
|
|
141054
|
-
"completionAckSentAt"
|
|
141055
|
-
];
|
|
141056
|
-
function shouldPersistBootstrapCheckpoint(current, next) {
|
|
141057
|
-
if (!current) return { ok: true };
|
|
141058
|
-
if (current.conversationId !== next.conversationId) return { ok: true };
|
|
141059
|
-
const currentAttemptSeq = current.attemptSeq ?? null;
|
|
141060
|
-
const nextAttemptSeq = next.attemptSeq ?? null;
|
|
141061
|
-
if (currentAttemptSeq != null && nextAttemptSeq != null && nextAttemptSeq < currentAttemptSeq) {
|
|
141062
|
-
return { ok: false, reason: "stale_regression" };
|
|
141063
|
-
}
|
|
141064
|
-
const sameAttempt = Boolean(
|
|
141065
|
-
current.attemptId && next.attemptId && current.attemptSeq != null && next.attemptSeq != null && current.attemptId === next.attemptId && current.attemptSeq === next.attemptSeq
|
|
141066
|
-
);
|
|
141067
|
-
if (!sameAttempt) return { ok: true };
|
|
141068
|
-
for (const field of MONOTONIC_BOOTSTRAP_FIELDS) {
|
|
141069
|
-
if (current[field] && !next[field]) {
|
|
141070
|
-
return { ok: false, reason: "stale_regression" };
|
|
141071
|
-
}
|
|
141072
|
-
}
|
|
141073
|
-
return { ok: true };
|
|
141074
|
-
}
|
|
141075
|
-
async function readTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
141076
|
-
const canonicalConversationId = toCanonicalTelegramBootstrapConversationId(conversationId);
|
|
141077
|
-
const paths = [
|
|
141078
|
-
sessionPath(workspaceDir, canonicalConversationId),
|
|
141079
|
-
alternateSessionPath(workspaceDir, conversationId)
|
|
141080
|
-
].filter((entry) => Boolean(entry));
|
|
141081
|
-
try {
|
|
141082
|
-
for (const candidatePath of paths) {
|
|
141083
|
-
try {
|
|
141084
|
-
const raw = await fs35.readFile(candidatePath, "utf-8");
|
|
141085
|
-
const session = normalizeStoredSession(JSON.parse(raw));
|
|
141086
|
-
if (session.status === "failed" && isReleasedClassifyFailure(session.error)) {
|
|
141087
|
-
await deleteTelegramBootstrapSession(workspaceDir, canonicalConversationId);
|
|
141088
|
-
return null;
|
|
141089
|
-
}
|
|
141090
|
-
return session;
|
|
141091
|
-
} catch (error48) {
|
|
141092
|
-
if (error48?.code === "ENOENT") continue;
|
|
141093
|
-
throw error48;
|
|
141094
|
-
}
|
|
141095
|
-
}
|
|
141096
|
-
return null;
|
|
141097
|
-
} catch (error48) {
|
|
141098
|
-
if (error48?.code === "ENOENT") return null;
|
|
141099
|
-
throw error48;
|
|
141100
|
-
}
|
|
141101
|
-
}
|
|
141102
|
-
async function deleteTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
141103
|
-
await fs35.unlink(sessionPath(workspaceDir, conversationId)).catch(() => {
|
|
141104
|
-
});
|
|
141105
|
-
const legacyPath = alternateSessionPath(workspaceDir, conversationId);
|
|
141106
|
-
if (legacyPath) {
|
|
141107
|
-
await fs35.unlink(legacyPath).catch(() => {
|
|
141108
|
-
});
|
|
141109
|
-
}
|
|
141110
|
-
}
|
|
141111
|
-
async function writeTelegramBootstrapSession(workspaceDir, session) {
|
|
141112
|
-
const canonicalSession = normalizeStoredSession(session);
|
|
141113
|
-
const dir = sessionsDir(workspaceDir);
|
|
141114
|
-
await fs35.mkdir(dir, { recursive: true });
|
|
141115
|
-
const file2 = sessionPath(workspaceDir, canonicalSession.conversationId);
|
|
141116
|
-
const tmp = `${file2}.${randomUUID4()}.tmp`;
|
|
141117
|
-
await fs35.writeFile(tmp, JSON.stringify(canonicalSession, null, 2) + "\n", "utf-8");
|
|
141118
|
-
await fs35.rename(tmp, file2);
|
|
141119
|
-
const legacyPath = alternateSessionPath(workspaceDir, session.conversationId);
|
|
141120
|
-
if (legacyPath) {
|
|
141121
|
-
await fs35.unlink(legacyPath).catch(() => {
|
|
141122
|
-
});
|
|
141123
|
-
}
|
|
141124
|
-
}
|
|
141125
|
-
async function upsertTelegramBootstrapSession(workspaceDir, input) {
|
|
141126
|
-
const conversationId = toCanonicalTelegramBootstrapConversationId(input.conversationId);
|
|
141127
|
-
const existing = await readTelegramBootstrapSession(workspaceDir, conversationId);
|
|
141128
|
-
const resolvedSourceRoute = resolveNullableField(input.sourceRoute, existing?.sourceRoute);
|
|
141129
|
-
const resolvedProjectRoute = resolveNullableField(input.projectRoute, existing?.projectRoute);
|
|
141130
|
-
const resolvedProjectName = resolveNullableField(input.projectName, existing?.projectName);
|
|
141131
|
-
const resolvedStackHint = resolveNullableField(input.stackHint, existing?.stackHint);
|
|
141132
|
-
const resolvedRepoUrl = resolveNullableField(input.repoUrl, existing?.repoUrl);
|
|
141133
|
-
const resolvedRepoPath = resolveNullableField(input.repoPath, existing?.repoPath);
|
|
141134
|
-
const resolvedProjectSlug = resolveNullableField(input.projectSlug, existing?.projectSlug);
|
|
141135
|
-
const resolvedIssueId = resolveNullableField(input.issueId, existing?.issueId);
|
|
141136
|
-
const resolvedMessageThreadId = resolveNullableField(input.messageThreadId, existing?.messageThreadId);
|
|
141137
|
-
const resolvedProjectChannelId = resolveNullableField(input.projectChannelId, existing?.projectChannelId);
|
|
141138
|
-
const resolvedAttemptCount = resolveNullableField(input.attemptCount, existing?.attemptCount, 0);
|
|
141139
|
-
const resolvedAttemptId = resolveNullableField(input.attemptId, existing?.attemptId);
|
|
141140
|
-
const resolvedAttemptSeq = resolveNullableField(input.attemptSeq, existing?.attemptSeq);
|
|
141141
|
-
const resolvedClassifySessionKey = resolveNullableField(input.classifySessionKey, existing?.classifySessionKey);
|
|
141142
|
-
const resolvedClassifyRunId = resolveNullableField(input.classifyRunId, existing?.classifyRunId);
|
|
141143
|
-
const resolvedClassifyStartedAt = resolveNullableField(input.classifyStartedAt, existing?.classifyStartedAt);
|
|
141144
|
-
const resolvedBootstrapStep = resolveNullableField(input.bootstrapStep, existing?.bootstrapStep);
|
|
141145
|
-
const resolvedNextRetryAt = input.nextRetryAt !== void 0 ? input.nextRetryAt : defaultNextRetryAtForStatus(input.status, existing?.nextRetryAt);
|
|
141146
|
-
const resolvedAckSentAt = resolveNullableField(input.ackSentAt, existing?.ackSentAt);
|
|
141147
|
-
const resolvedProjectRegisteredAt = resolveNullableField(input.projectRegisteredAt, existing?.projectRegisteredAt);
|
|
141148
|
-
const resolvedTopicKickoffSentAt = resolveNullableField(input.topicKickoffSentAt, existing?.topicKickoffSentAt);
|
|
141149
|
-
const resolvedProjectTickedAt = resolveNullableField(input.projectTickedAt, existing?.projectTickedAt);
|
|
141150
|
-
const resolvedCompletionAckSentAt = resolveNullableField(input.completionAckSentAt, existing?.completionAckSentAt);
|
|
141151
|
-
const resolvedError = input.error !== void 0 ? input.error : input.lastError !== void 0 ? input.lastError : existing?.error ?? existing?.lastError ?? null;
|
|
141152
|
-
const requestHash = buildBootstrapRequestHash({
|
|
141153
|
-
rawIdea: input.rawIdea,
|
|
141154
|
-
projectName: resolvedProjectName,
|
|
141155
|
-
stackHint: resolvedStackHint,
|
|
141156
|
-
repoUrl: resolvedRepoUrl,
|
|
141157
|
-
repoPath: resolvedRepoPath
|
|
141158
|
-
});
|
|
141159
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
141160
|
-
const session = {
|
|
141161
|
-
id: existing?.id ?? buildBootstrapSessionId(conversationId, input.rawIdea),
|
|
141162
|
-
conversationId,
|
|
141163
|
-
sourceChannel: input.sourceChannel ?? input.sourceRoute?.channel ?? existing?.sourceChannel ?? "telegram",
|
|
141164
|
-
sourceRoute: resolvedSourceRoute,
|
|
141165
|
-
projectRoute: resolvedProjectRoute,
|
|
141166
|
-
requestHash,
|
|
141167
|
-
requestFingerprint: requestHash,
|
|
141168
|
-
lastCompletedRequestHash: input.status === "completed" ? requestHash : existing?.lastCompletedRequestHash ?? null,
|
|
141169
|
-
rawIdea: input.rawIdea,
|
|
141170
|
-
projectName: resolvedProjectName,
|
|
141171
|
-
stackHint: resolvedStackHint,
|
|
141172
|
-
repoUrl: resolvedRepoUrl,
|
|
141173
|
-
repoPath: resolvedRepoPath,
|
|
141174
|
-
projectSlug: resolvedProjectSlug,
|
|
141175
|
-
issueId: resolvedIssueId,
|
|
141176
|
-
messageThreadId: resolvedMessageThreadId,
|
|
141177
|
-
projectChannelId: resolvedProjectChannelId,
|
|
141178
|
-
language: input.language ?? existing?.language,
|
|
141179
|
-
status: input.status,
|
|
141180
|
-
attemptId: resolvedAttemptId,
|
|
141181
|
-
attemptSeq: resolvedAttemptSeq,
|
|
141182
|
-
classifySessionKey: resolvedClassifySessionKey,
|
|
141183
|
-
classifyRunId: resolvedClassifyRunId,
|
|
141184
|
-
classifyStartedAt: resolvedClassifyStartedAt,
|
|
141185
|
-
bootstrapStep: resolvedBootstrapStep,
|
|
141186
|
-
attemptCount: resolvedAttemptCount,
|
|
141187
|
-
lastError: resolvedError,
|
|
141188
|
-
nextRetryAt: resolvedNextRetryAt,
|
|
141189
|
-
ackSentAt: resolvedAckSentAt,
|
|
141190
|
-
projectRegisteredAt: resolvedProjectRegisteredAt,
|
|
141191
|
-
topicKickoffSentAt: resolvedTopicKickoffSentAt,
|
|
141192
|
-
projectTickedAt: resolvedProjectTickedAt,
|
|
141193
|
-
completionAckSentAt: resolvedCompletionAckSentAt,
|
|
141194
|
-
pendingClarification: input.pendingClarification !== void 0 ? input.pendingClarification : existing?.pendingClarification ?? null,
|
|
141195
|
-
orphanedArtifacts: input.orphanedArtifacts !== void 0 ? input.orphanedArtifacts : existing?.orphanedArtifacts ?? null,
|
|
141196
|
-
createdAt: existing?.createdAt ?? now2,
|
|
141197
|
-
updatedAt: now2,
|
|
141198
|
-
suppressUntil: nextSuppressUntil(input.status),
|
|
141199
|
-
error: resolvedError
|
|
141200
|
-
};
|
|
141201
|
-
const writeDecision = shouldPersistBootstrapCheckpoint(existing, session);
|
|
141202
|
-
if (!writeDecision.ok && existing) {
|
|
141203
|
-
return existing;
|
|
141204
|
-
}
|
|
141205
|
-
await writeTelegramBootstrapSession(workspaceDir, session);
|
|
141206
|
-
return session;
|
|
141207
|
-
}
|
|
141208
|
-
function shouldSuppressTelegramBootstrapReply(session, request) {
|
|
141209
|
-
if (!session) return false;
|
|
141210
|
-
if (isTelegramBootstrapSessionExpired(session)) return false;
|
|
141211
|
-
if (session.status === "completed" || session.status === "failed") {
|
|
141212
|
-
if (session.status === "failed" && isReleasedClassifyFailure(session.error)) return false;
|
|
141213
|
-
if (!request) return false;
|
|
141214
|
-
return buildBootstrapRequestFingerprint(request) === session.requestHash;
|
|
141215
|
-
}
|
|
141216
|
-
if (!request) return true;
|
|
141217
|
-
return buildBootstrapRequestFingerprint(request) === session.requestHash;
|
|
141218
|
-
}
|
|
141219
|
-
function isTelegramBootstrapSessionExpired(session, now2 = Date.now()) {
|
|
141220
|
-
if (!session) return false;
|
|
141221
|
-
const suppressUntil = Date.parse(session.suppressUntil);
|
|
141222
|
-
return !Number.isNaN(suppressUntil) && suppressUntil < now2;
|
|
141223
|
-
}
|
|
141224
|
-
function isRecoverableTelegramBootstrapSession(session) {
|
|
141225
|
-
return session?.status === "bootstrapping" || session?.status === "dispatching";
|
|
141226
|
-
}
|
|
141227
|
-
function isClaimableTelegramBootstrapSession(session, now2 = Date.now()) {
|
|
141228
|
-
if (!isRecoverableTelegramBootstrapSession(session)) return false;
|
|
141229
|
-
if (!session.nextRetryAt) return true;
|
|
141230
|
-
const retryAt = Date.parse(session.nextRetryAt);
|
|
141231
|
-
return Number.isNaN(retryAt) || retryAt <= now2;
|
|
141232
|
-
}
|
|
141233
|
-
function isSupersededTelegramBootstrapAttempt(current, candidate) {
|
|
141234
|
-
if (!current || !candidate) return false;
|
|
141235
|
-
if (current.conversationId !== candidate.conversationId) return true;
|
|
141236
|
-
const currentHasAttempt = current.attemptId != null && current.attemptSeq != null;
|
|
141237
|
-
const candidateHasAttempt = candidate.attemptId != null && candidate.attemptSeq != null;
|
|
141238
|
-
if (currentHasAttempt || candidateHasAttempt) {
|
|
141239
|
-
return current.attemptId !== candidate.attemptId || current.attemptSeq !== candidate.attemptSeq;
|
|
141240
|
-
}
|
|
141241
|
-
return current.requestHash !== candidate.requestHash || current.status !== candidate.status || current.updatedAt !== candidate.updatedAt;
|
|
141242
|
-
}
|
|
141243
|
-
|
|
141244
|
-
// lib/dispatch/telegram-bootstrap-hook.ts
|
|
141245
142167
|
init_constants();
|
|
141246
142168
|
var BOOTSTRAP_RETRY_DELAY_MS = 15e3;
|
|
141247
142169
|
var LAYER3_CONFIDENCE_THRESHOLD = 0.6;
|
|
@@ -141332,26 +142254,7 @@ I'll continue the flow at ${link}`
|
|
|
141332
142254
|
}
|
|
141333
142255
|
};
|
|
141334
142256
|
function detectScopeAmbiguity(rawIdea, stackHint) {
|
|
141335
|
-
|
|
141336
|
-
if (/\b(livre|free.?choice|your.?call|pode.?escolher|voc[eê].?decide|qualquer)\b/i.test(text)) {
|
|
141337
|
-
return false;
|
|
141338
|
-
}
|
|
141339
|
-
const subsystems = [
|
|
141340
|
-
/\b(worker|background.?job|queue|task.?runner|celery|bull)\b/i,
|
|
141341
|
-
/\b(websocket|sse|real.?time|realtime|push.?notif)\b/i,
|
|
141342
|
-
/\b(auth|oauth|jwt|login|register|signup|autenticac)\b/i,
|
|
141343
|
-
/\b(notif|alert|assinatura|subscription|subscribe|email|sms)\b/i,
|
|
141344
|
-
/\b(banco|database|db|postgres|mysql|mongodb|redis|sqlite)\b/i,
|
|
141345
|
-
/\b(api\s+rest|rest\s+api|endpoint|graphql|grpc)\b/i,
|
|
141346
|
-
/\b(dashboard|frontend|interface|ui|tela)\b/i
|
|
141347
|
-
];
|
|
141348
|
-
const matchedSubsystems = subsystems.filter((r2) => r2.test(text)).length;
|
|
141349
|
-
if (matchedSubsystems < 3) return false;
|
|
141350
|
-
const hasExplicitDB = /\b(postgres(ql)?|mysql|mongodb|mongo|redis|sqlite|supabase|dynamodb|cockroach)\b/i.test(text);
|
|
141351
|
-
const hasExplicitAuth = /\b(jwt|oauth2?|basic.?auth|api.?key|session.?based|cookie.?auth)\b/i.test(text);
|
|
141352
|
-
const hasExplicitStack = !!stackHint && !["api", "rest-api", "backend"].includes(stackHint);
|
|
141353
|
-
const unspecifiedDimensions = [!hasExplicitDB, !hasExplicitAuth, !hasExplicitStack].filter(Boolean).length;
|
|
141354
|
-
return unspecifiedDimensions >= 2;
|
|
142257
|
+
return detectScopeClarificationNeed(rawIdea, stackHint).ask;
|
|
141355
142258
|
}
|
|
141356
142259
|
function inferProjectSlug(text) {
|
|
141357
142260
|
let cleaned = text.replace(/^(create|build|crie|cria|criar|fazer?|quero|i need|i want)\s+(uma|um|me\s+a?|an|a)?\s*/i, "").replace(/\s+(that|which|que|para|for|pra)\s+.*/i, "").trim();
|
|
@@ -141360,11 +142263,11 @@ function inferProjectSlug(text) {
|
|
|
141360
142263
|
return slug || void 0;
|
|
141361
142264
|
}
|
|
141362
142265
|
function normalizeProjectNameCandidate(value) {
|
|
141363
|
-
const normalized =
|
|
142266
|
+
const normalized = normalizeText4(value);
|
|
141364
142267
|
if (!normalized) return void 0;
|
|
141365
142268
|
return inferProjectSlug(normalized) ?? void 0;
|
|
141366
142269
|
}
|
|
141367
|
-
function
|
|
142270
|
+
function normalizeText4(value) {
|
|
141368
142271
|
const trimmed = value?.trim();
|
|
141369
142272
|
return trimmed ? trimmed : void 0;
|
|
141370
142273
|
}
|
|
@@ -141389,15 +142292,19 @@ function parseField(text, labels) {
|
|
|
141389
142292
|
for (const label of labels) {
|
|
141390
142293
|
const regex = new RegExp(`(?:^|[\\n.,;!?]\\s*)${label}\\s*:\\s*(.+)$`, "im");
|
|
141391
142294
|
const match = text.match(regex);
|
|
141392
|
-
if (match?.[1]) return
|
|
142295
|
+
if (match?.[1]) return normalizeText4(match[1]);
|
|
141393
142296
|
}
|
|
141394
142297
|
return void 0;
|
|
141395
142298
|
}
|
|
141396
142299
|
function parseIdeaBlock(text) {
|
|
141397
142300
|
const match = text.match(/^\s*(idea|ideia|objetivo)\s*:\s*([\s\S]+)$/im);
|
|
141398
|
-
return
|
|
142301
|
+
return normalizeText4(match?.[2]);
|
|
141399
142302
|
}
|
|
141400
142303
|
function parseExplicitProjectName(text) {
|
|
142304
|
+
const naturalLanguageMatch = text.match(/\b(?:please\s+)?(?:use|set|keep)\s+(?:the\s+)?(?:project name|repo name|repository name|nome do projeto)\s+(?:as\s+)?[`"'“”‘’]?([a-z0-9][a-z0-9-]{1,63})[`"'“”‘’]?(?=$|[\s.,!?;:])/i);
|
|
142305
|
+
if (naturalLanguageMatch?.[1]) {
|
|
142306
|
+
return normalizeProjectNameCandidate(naturalLanguageMatch[1].toLowerCase());
|
|
142307
|
+
}
|
|
141401
142308
|
const match = text.match(/\b(?:called|named|chamado)\s+[`"'“”‘’]?([a-z0-9][a-z0-9-]{1,63})[`"'“”‘’]?(?=$|[\s.,!?;:])/i);
|
|
141402
142309
|
return normalizeProjectNameCandidate(match?.[1]?.toLowerCase());
|
|
141403
142310
|
}
|
|
@@ -141635,6 +142542,9 @@ function buildClarificationMessage(parsed, pendingClarification, language = "pt"
|
|
|
141635
142542
|
if (pendingClarification === "name") {
|
|
141636
142543
|
return BOOTSTRAP_MESSAGES.clarifyName[language];
|
|
141637
142544
|
}
|
|
142545
|
+
if (pendingClarification === "scope") {
|
|
142546
|
+
return BOOTSTRAP_MESSAGES.clarifyScope[language](parsed.rawIdea);
|
|
142547
|
+
}
|
|
141638
142548
|
if (pendingClarification === "stack_and_name" || !parsed.stackHint && !parsed.projectName) {
|
|
141639
142549
|
return BOOTSTRAP_MESSAGES.clarifyBoth[language];
|
|
141640
142550
|
}
|
|
@@ -141835,12 +142745,12 @@ async function handleTelegramBootstrapDmMessage(ctx, rawConversationId, content)
|
|
|
141835
142745
|
conversationId,
|
|
141836
142746
|
rawIdea: content,
|
|
141837
142747
|
stackHint: parsed.stackHint ?? void 0,
|
|
141838
|
-
projectName: parsed.projectName ?? parsed.
|
|
142748
|
+
projectName: parsed.projectName ?? inferProjectSlug(parsed.rawIdea) ?? void 0,
|
|
141839
142749
|
status: "clarifying",
|
|
141840
142750
|
pendingClarification: "scope",
|
|
141841
142751
|
language
|
|
141842
142752
|
});
|
|
141843
|
-
const clarifyMsg =
|
|
142753
|
+
const clarifyMsg = buildClarificationMessage(parsed, "scope", language);
|
|
141844
142754
|
await sendTelegramText(ctx, rawConversationId, clarifyMsg);
|
|
141845
142755
|
return;
|
|
141846
142756
|
}
|
|
@@ -142026,6 +142936,9 @@ async function persistOwnedBootstrapCheckpoint(workspaceDir, owner, input) {
|
|
|
142026
142936
|
bootstrapStep: input.bootstrapStep,
|
|
142027
142937
|
projectSlug: input.projectSlug ?? void 0,
|
|
142028
142938
|
issueId: input.issueId,
|
|
142939
|
+
issueUrl: input.issueUrl,
|
|
142940
|
+
triageReadyForDispatch: input.triageReadyForDispatch,
|
|
142941
|
+
triageErrors: input.triageErrors,
|
|
142029
142942
|
messageThreadId: input.messageThreadId,
|
|
142030
142943
|
projectChannelId: input.projectChannelId,
|
|
142031
142944
|
language: input.language,
|
|
@@ -142491,7 +143404,7 @@ async function recoverDueTelegramBootstrapSessions(ctx, workspaceDir) {
|
|
|
142491
143404
|
const sessionsDir2 = path37.join(workspaceDir, DATA_DIR, "bootstrap-sessions");
|
|
142492
143405
|
let files;
|
|
142493
143406
|
try {
|
|
142494
|
-
files = await
|
|
143407
|
+
files = await fs37.readdir(sessionsDir2);
|
|
142495
143408
|
} catch {
|
|
142496
143409
|
return 0;
|
|
142497
143410
|
}
|
|
@@ -142877,20 +143790,25 @@ async function continueBootstrap(ctx, conversationId, workspaceDir, request, sou
|
|
|
142877
143790
|
return;
|
|
142878
143791
|
}
|
|
142879
143792
|
const telegramConfig = readFabricaTelegramConfig(ctx.pluginConfig);
|
|
142880
|
-
const
|
|
142881
|
-
|
|
142882
|
-
|
|
142883
|
-
|
|
143793
|
+
const existingSession = await readTelegramBootstrapSession(workspaceDir, conversationId);
|
|
143794
|
+
const lang = existingSession?.language ?? "pt";
|
|
143795
|
+
const inferredProjectName = request.projectName ?? inferProjectSlug(request.rawIdea) ?? null;
|
|
143796
|
+
const clarificationDecision = decideBootstrapClarification({
|
|
143797
|
+
projectName: inferredProjectName,
|
|
143798
|
+
stackHint: request.stackHint
|
|
143799
|
+
});
|
|
143800
|
+
if (clarificationDecision.ask && (clarificationDecision.kind === "stack" || clarificationDecision.kind === "stack_and_name")) {
|
|
143801
|
+
const clarificationKind = clarificationDecision.kind;
|
|
142884
143802
|
const owner = buildBootstrapAttemptOwner(existingSession);
|
|
142885
143803
|
if (owner) {
|
|
142886
143804
|
await persistOwnedBootstrapCheckpoint(workspaceDir, owner, {
|
|
142887
143805
|
rawIdea: request.rawIdea,
|
|
142888
|
-
projectName:
|
|
143806
|
+
projectName: inferredProjectName ?? void 0,
|
|
142889
143807
|
stackHint: request.stackHint ?? void 0,
|
|
142890
143808
|
sourceRoute,
|
|
142891
143809
|
status: "clarifying",
|
|
142892
143810
|
bootstrapStep: existingSession?.bootstrapStep ?? "awaiting_pipeline",
|
|
142893
|
-
pendingClarification:
|
|
143811
|
+
pendingClarification: clarificationKind,
|
|
142894
143812
|
language: lang,
|
|
142895
143813
|
ackSentAt: existingSession?.ackSentAt ?? null,
|
|
142896
143814
|
lastError: null,
|
|
@@ -142900,64 +143818,63 @@ async function continueBootstrap(ctx, conversationId, workspaceDir, request, sou
|
|
|
142900
143818
|
await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142901
143819
|
conversationId,
|
|
142902
143820
|
rawIdea: request.rawIdea,
|
|
142903
|
-
projectName:
|
|
143821
|
+
projectName: inferredProjectName ?? void 0,
|
|
142904
143822
|
status: "clarifying",
|
|
142905
|
-
pendingClarification:
|
|
143823
|
+
pendingClarification: clarificationKind,
|
|
142906
143824
|
language: lang
|
|
142907
143825
|
});
|
|
142908
143826
|
}
|
|
142909
143827
|
await sendTelegramText(ctx, conversationId, buildClarificationMessage(
|
|
142910
|
-
{ rawIdea: request.rawIdea, projectName:
|
|
142911
|
-
|
|
143828
|
+
{ rawIdea: request.rawIdea, projectName: inferredProjectName ?? void 0, stackHint: request.stackHint ?? void 0 },
|
|
143829
|
+
clarificationKind,
|
|
142912
143830
|
lang
|
|
142913
143831
|
));
|
|
142914
143832
|
return;
|
|
142915
143833
|
}
|
|
142916
|
-
if (!request.projectName) {
|
|
143834
|
+
if (inferredProjectName && !request.projectName) {
|
|
143835
|
+
request.projectName = inferredProjectName;
|
|
143836
|
+
}
|
|
143837
|
+
if (clarificationDecision.ask && clarificationDecision.kind === "name") {
|
|
142917
143838
|
const inferredSlug = inferProjectSlug(request.rawIdea);
|
|
142918
143839
|
if (inferredSlug) {
|
|
142919
143840
|
request.projectName = inferredSlug;
|
|
143841
|
+
} else if (existingSession?.pendingClarification === "name") {
|
|
143842
|
+
request.projectName = `project-${Date.now()}`;
|
|
142920
143843
|
} else {
|
|
142921
|
-
const
|
|
142922
|
-
if (existingSession
|
|
142923
|
-
|
|
143844
|
+
const owner = existingSession ? buildBootstrapAttemptOwner(existingSession) : null;
|
|
143845
|
+
if (existingSession && owner) {
|
|
143846
|
+
await persistOwnedBootstrapCheckpoint(workspaceDir, owner, {
|
|
143847
|
+
rawIdea: request.rawIdea,
|
|
143848
|
+
stackHint: request.stackHint ?? void 0,
|
|
143849
|
+
sourceRoute,
|
|
143850
|
+
status: "clarifying",
|
|
143851
|
+
bootstrapStep: existingSession.bootstrapStep ?? "awaiting_pipeline",
|
|
143852
|
+
pendingClarification: "name",
|
|
143853
|
+
language: lang,
|
|
143854
|
+
ackSentAt: existingSession.ackSentAt ?? null,
|
|
143855
|
+
lastError: null,
|
|
143856
|
+
nextRetryAt: null
|
|
143857
|
+
});
|
|
142924
143858
|
} else {
|
|
142925
|
-
|
|
142926
|
-
const owner = existingSession ? buildBootstrapAttemptOwner(existingSession) : null;
|
|
142927
|
-
if (existingSession && owner) {
|
|
142928
|
-
await persistOwnedBootstrapCheckpoint(workspaceDir, owner, {
|
|
142929
|
-
rawIdea: request.rawIdea,
|
|
142930
|
-
stackHint: request.stackHint ?? void 0,
|
|
142931
|
-
sourceRoute,
|
|
142932
|
-
status: "clarifying",
|
|
142933
|
-
bootstrapStep: existingSession.bootstrapStep ?? "awaiting_pipeline",
|
|
142934
|
-
pendingClarification: "name",
|
|
142935
|
-
language: lang,
|
|
142936
|
-
ackSentAt: existingSession.ackSentAt ?? null,
|
|
142937
|
-
lastError: null,
|
|
142938
|
-
nextRetryAt: null
|
|
142939
|
-
});
|
|
142940
|
-
} else {
|
|
142941
|
-
await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142942
|
-
conversationId,
|
|
142943
|
-
rawIdea: request.rawIdea,
|
|
142944
|
-
stackHint: request.stackHint ?? void 0,
|
|
142945
|
-
status: "clarifying",
|
|
142946
|
-
pendingClarification: "name",
|
|
142947
|
-
language: lang
|
|
142948
|
-
});
|
|
142949
|
-
}
|
|
142950
|
-
await sendTelegramText(
|
|
142951
|
-
ctx,
|
|
143859
|
+
await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142952
143860
|
conversationId,
|
|
142953
|
-
|
|
142954
|
-
|
|
142955
|
-
|
|
142956
|
-
|
|
142957
|
-
|
|
142958
|
-
);
|
|
142959
|
-
return;
|
|
143861
|
+
rawIdea: request.rawIdea,
|
|
143862
|
+
stackHint: request.stackHint ?? void 0,
|
|
143863
|
+
status: "clarifying",
|
|
143864
|
+
pendingClarification: "name",
|
|
143865
|
+
language: lang
|
|
143866
|
+
});
|
|
142960
143867
|
}
|
|
143868
|
+
await sendTelegramText(
|
|
143869
|
+
ctx,
|
|
143870
|
+
conversationId,
|
|
143871
|
+
buildClarificationMessage(
|
|
143872
|
+
{ rawIdea: request.rawIdea, projectName: void 0, stackHint: request.stackHint ?? void 0 },
|
|
143873
|
+
"name",
|
|
143874
|
+
lang
|
|
143875
|
+
)
|
|
143876
|
+
);
|
|
143877
|
+
return;
|
|
142961
143878
|
}
|
|
142962
143879
|
}
|
|
142963
143880
|
const stepCtx = {
|
|
@@ -142988,10 +143905,11 @@ async function continueBootstrap(ctx, conversationId, workspaceDir, request, sou
|
|
|
142988
143905
|
config: ctx.config,
|
|
142989
143906
|
pluginConfig: ctx.pluginConfig
|
|
142990
143907
|
};
|
|
143908
|
+
const stackHint = request.stackHint ?? null;
|
|
142991
143909
|
const payload = {
|
|
142992
|
-
session_id:
|
|
143910
|
+
session_id: randomUUID5(),
|
|
142993
143911
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
142994
|
-
step: "
|
|
143912
|
+
step: "telegram-bootstrap",
|
|
142995
143913
|
raw_idea: request.rawIdea,
|
|
142996
143914
|
answers: {},
|
|
142997
143915
|
metadata: {
|
|
@@ -143089,6 +144007,8 @@ Erro: ${result.error ?? "erro desconhecido"}`
|
|
|
143089
144007
|
const projectChannelId = result.payload.metadata.channel_id ?? telegramConfig.projectsForumChatId;
|
|
143090
144008
|
const messageThreadId = result.payload.metadata.message_thread_id;
|
|
143091
144009
|
const projectSlug = result.payload.metadata.project_slug ?? result.payload.scaffold?.project_slug ?? null;
|
|
144010
|
+
const createdIssue = result.payload.issues?.[0] ?? null;
|
|
144011
|
+
const triage = result.payload.triage ?? null;
|
|
143092
144012
|
if (projectChannelId && messageThreadId) {
|
|
143093
144013
|
const projectRoute = {
|
|
143094
144014
|
channel: "telegram",
|
|
@@ -143103,6 +144023,10 @@ Erro: ${result.error ?? "erro desconhecido"}`
|
|
|
143103
144023
|
bootstrapStep: "project_registered",
|
|
143104
144024
|
projectName: resolvedProjectName,
|
|
143105
144025
|
projectSlug: projectSlug ?? void 0,
|
|
144026
|
+
issueId: createdIssue?.number ?? null,
|
|
144027
|
+
issueUrl: createdIssue?.url ?? null,
|
|
144028
|
+
triageReadyForDispatch: triage?.ready_for_dispatch ?? null,
|
|
144029
|
+
triageErrors: triage?.errors ?? null,
|
|
143106
144030
|
projectRegisteredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
143107
144031
|
projectChannelId: String(projectChannelId),
|
|
143108
144032
|
messageThreadId,
|
|
@@ -143118,6 +144042,10 @@ Erro: ${result.error ?? "erro desconhecido"}`
|
|
|
143118
144042
|
status: "dispatching",
|
|
143119
144043
|
projectName: resolvedProjectName,
|
|
143120
144044
|
projectSlug: projectSlug ?? void 0,
|
|
144045
|
+
issueId: createdIssue?.number ?? null,
|
|
144046
|
+
issueUrl: createdIssue?.url ?? null,
|
|
144047
|
+
triageReadyForDispatch: triage?.ready_for_dispatch ?? null,
|
|
144048
|
+
triageErrors: triage?.errors ?? null,
|
|
143121
144049
|
projectRegisteredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
143122
144050
|
projectChannelId: String(projectChannelId),
|
|
143123
144051
|
messageThreadId,
|
|
@@ -143133,6 +144061,10 @@ Erro: ${result.error ?? "erro desconhecido"}`
|
|
|
143133
144061
|
...registeredSession,
|
|
143134
144062
|
projectName: resolvedProjectName,
|
|
143135
144063
|
projectSlug: projectSlug ?? registeredSession.projectSlug ?? void 0,
|
|
144064
|
+
issueId: createdIssue?.number ?? registeredSession.issueId ?? null,
|
|
144065
|
+
issueUrl: createdIssue?.url ?? registeredSession.issueUrl ?? null,
|
|
144066
|
+
triageReadyForDispatch: triage?.ready_for_dispatch ?? registeredSession.triageReadyForDispatch ?? null,
|
|
144067
|
+
triageErrors: triage?.errors ?? registeredSession.triageErrors ?? null,
|
|
143136
144068
|
projectRegisteredAt: registeredSession.projectRegisteredAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
143137
144069
|
language: currentSession?.language ?? "pt"
|
|
143138
144070
|
});
|
|
@@ -144727,7 +145659,7 @@ async function runPrDiscoveryPass(params) {
|
|
|
144727
145659
|
// lib/services/heartbeat/genesis-health.ts
|
|
144728
145660
|
init_logger();
|
|
144729
145661
|
init_constants();
|
|
144730
|
-
import
|
|
145662
|
+
import fs38 from "node:fs/promises";
|
|
144731
145663
|
import path38 from "node:path";
|
|
144732
145664
|
var STALE_THRESHOLD_MS = 5 * 6e4;
|
|
144733
145665
|
var STALE_STATUSES = /* @__PURE__ */ new Set(["pending_classify", "classifying"]);
|
|
@@ -144742,7 +145674,7 @@ async function checkGenesisHealth(workspaceDir) {
|
|
|
144742
145674
|
const sessionsDir2 = path38.join(workspaceDir, DATA_DIR, "bootstrap-sessions");
|
|
144743
145675
|
let files;
|
|
144744
145676
|
try {
|
|
144745
|
-
files = await
|
|
145677
|
+
files = await fs38.readdir(sessionsDir2);
|
|
144746
145678
|
} catch {
|
|
144747
145679
|
return;
|
|
144748
145680
|
}
|
|
@@ -144750,7 +145682,7 @@ async function checkGenesisHealth(workspaceDir) {
|
|
|
144750
145682
|
for (const file2 of files) {
|
|
144751
145683
|
if (!file2.endsWith(".json")) continue;
|
|
144752
145684
|
try {
|
|
144753
|
-
const raw = await
|
|
145685
|
+
const raw = await fs38.readFile(path38.join(sessionsDir2, file2), "utf-8");
|
|
144754
145686
|
const data = JSON.parse(raw);
|
|
144755
145687
|
const id = data.conversationId ?? file2.replace(/\.json$/, "");
|
|
144756
145688
|
const updatedAt = typeof data.updatedAt === "string" ? new Date(data.updatedAt).getTime() : Number(data.updatedAt ?? 0);
|
|
@@ -145374,7 +146306,7 @@ async function writeModelsToWorkflow(workspacePath, models) {
|
|
|
145374
146306
|
const workflowPath = path39.join(workspacePath, DATA_DIR, "workflow.yaml");
|
|
145375
146307
|
let content = "";
|
|
145376
146308
|
try {
|
|
145377
|
-
content = await
|
|
146309
|
+
content = await fs39.readFile(workflowPath, "utf-8");
|
|
145378
146310
|
} catch {
|
|
145379
146311
|
}
|
|
145380
146312
|
const doc = content ? import_yaml3.default.parseDocument(content) : new import_yaml3.default.Document({});
|
|
@@ -145390,7 +146322,7 @@ async function writeModelsToWorkflow(workspacePath, models) {
|
|
|
145390
146322
|
roleNode.set("models", doc.createNode(levels));
|
|
145391
146323
|
}
|
|
145392
146324
|
}
|
|
145393
|
-
await
|
|
146325
|
+
await fs39.writeFile(workflowPath, doc.toString({ lineWidth: 120 }), "utf-8");
|
|
145394
146326
|
}
|
|
145395
146327
|
|
|
145396
146328
|
// lib/tools/admin/setup.ts
|
|
@@ -145514,7 +146446,7 @@ ${written.map((f3) => ` ${f3}`).join("\n")}` : "All files already exist \u2014
|
|
|
145514
146446
|
|
|
145515
146447
|
// lib/setup/onboarding.ts
|
|
145516
146448
|
init_roles();
|
|
145517
|
-
import
|
|
146449
|
+
import fs40 from "node:fs/promises";
|
|
145518
146450
|
import path40 from "node:path";
|
|
145519
146451
|
function isPluginConfigured(pluginConfig) {
|
|
145520
146452
|
return !!pluginConfig && Object.keys(pluginConfig).length > 0;
|
|
@@ -145522,7 +146454,7 @@ function isPluginConfigured(pluginConfig) {
|
|
|
145522
146454
|
async function hasWorkspaceFiles(workspaceDir) {
|
|
145523
146455
|
if (!workspaceDir) return false;
|
|
145524
146456
|
try {
|
|
145525
|
-
const content = await
|
|
146457
|
+
const content = await fs40.readFile(
|
|
145526
146458
|
path40.join(workspaceDir, "AGENTS.md"),
|
|
145527
146459
|
"utf-8"
|
|
145528
146460
|
);
|
|
@@ -146338,7 +147270,7 @@ These completely replace (not merge with) the workspace-level prompts for that r
|
|
|
146338
147270
|
}
|
|
146339
147271
|
|
|
146340
147272
|
// lib/tools/admin/config.ts
|
|
146341
|
-
import
|
|
147273
|
+
import fs41 from "node:fs/promises";
|
|
146342
147274
|
import path41 from "node:path";
|
|
146343
147275
|
init_workspace();
|
|
146344
147276
|
init_templates();
|
|
@@ -146443,7 +147375,7 @@ async function handleDiff(workspacePath) {
|
|
|
146443
147375
|
summary: "No workflow.yaml found in workspace \u2014 using package defaults."
|
|
146444
147376
|
});
|
|
146445
147377
|
}
|
|
146446
|
-
const current = await
|
|
147378
|
+
const current = await fs41.readFile(workflowPath, "utf-8");
|
|
146447
147379
|
const template = WORKFLOW_YAML_TEMPLATE;
|
|
146448
147380
|
if (current.trim() === template.trim()) {
|
|
146449
147381
|
return jsonResult({
|
|
@@ -146603,6 +147535,8 @@ function extractExplicitProjectName(text) {
|
|
|
146603
147535
|
if (!text) return null;
|
|
146604
147536
|
const fieldMatch = text.match(/(?:^|[\n.,;!?]\s*)(?:project name|repo name|repository name|nome do projeto)\s*:\s*([a-z0-9][a-z0-9-]{1,63})\b/i);
|
|
146605
147537
|
if (fieldMatch?.[1]) return fieldMatch[1].trim().toLowerCase();
|
|
147538
|
+
const naturalLanguageMatch = text.match(/\b(?:please\s+)?(?:use|set|keep)\s+(?:the\s+)?(?:project name|repo name|repository name|nome do projeto)\s+(?:as\s+)?[`"'“”‘’]?([a-z0-9][a-z0-9-]{1,63})[`"'“”‘’]?(?=$|[\s.,!?;:])/i);
|
|
147539
|
+
if (naturalLanguageMatch?.[1]) return naturalLanguageMatch[1].trim().toLowerCase();
|
|
146606
147540
|
const inlineMatch = text.match(/\b(?:called|named|chamado)\s+[`"'“”‘’]?([a-z0-9][a-z0-9-]{1,63})[`"'“”‘’]?(?=$|[\s.,!?;:])/i);
|
|
146607
147541
|
if (inlineMatch?.[1]) return inlineMatch[1].trim().toLowerCase();
|
|
146608
147542
|
return null;
|
|
@@ -147117,7 +148051,7 @@ function getPendingQuestions(payload, answers = payload.answers ?? {}) {
|
|
|
147117
148051
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
147118
148052
|
init_constants();
|
|
147119
148053
|
init_migrate_layout();
|
|
147120
|
-
import
|
|
148054
|
+
import fs42 from "node:fs/promises";
|
|
147121
148055
|
import path42 from "node:path";
|
|
147122
148056
|
init_roles();
|
|
147123
148057
|
async function runDoctor(opts) {
|
|
@@ -147162,14 +148096,14 @@ async function runDoctor(opts) {
|
|
|
147162
148096
|
if (check2.name.startsWith("file:")) {
|
|
147163
148097
|
const recheckPath = check2.name.replace("file:", "");
|
|
147164
148098
|
try {
|
|
147165
|
-
await
|
|
148099
|
+
await fs42.access(recheckPath);
|
|
147166
148100
|
recheckOk = true;
|
|
147167
148101
|
} catch {
|
|
147168
148102
|
}
|
|
147169
148103
|
} else if (check2.name.startsWith("dir:")) {
|
|
147170
148104
|
const recheckPath = check2.name.replace("dir:", "");
|
|
147171
148105
|
try {
|
|
147172
|
-
const stat2 = await
|
|
148106
|
+
const stat2 = await fs42.stat(recheckPath);
|
|
147173
148107
|
recheckOk = stat2.isDirectory();
|
|
147174
148108
|
} catch {
|
|
147175
148109
|
}
|
|
@@ -147198,7 +148132,7 @@ async function runDoctor(opts) {
|
|
|
147198
148132
|
}
|
|
147199
148133
|
async function checkDirExists(dirPath, label) {
|
|
147200
148134
|
try {
|
|
147201
|
-
const stat2 = await
|
|
148135
|
+
const stat2 = await fs42.stat(dirPath);
|
|
147202
148136
|
if (stat2.isDirectory()) {
|
|
147203
148137
|
return { name: `dir:${dirPath}`, severity: "ok", message: `${label} exists` };
|
|
147204
148138
|
}
|
|
@@ -147209,7 +148143,7 @@ async function checkDirExists(dirPath, label) {
|
|
|
147209
148143
|
}
|
|
147210
148144
|
async function checkFileExists(filePath, label) {
|
|
147211
148145
|
try {
|
|
147212
|
-
await
|
|
148146
|
+
await fs42.access(filePath);
|
|
147213
148147
|
return { name: `file:${filePath}`, severity: "ok", message: `${label} exists` };
|
|
147214
148148
|
} catch {
|
|
147215
148149
|
return { name: `file:${filePath}`, severity: "error", message: `${label} missing` };
|
|
@@ -147218,7 +148152,7 @@ async function checkFileExists(filePath, label) {
|
|
|
147218
148152
|
async function checkWorkflowYaml(dataDir) {
|
|
147219
148153
|
const filePath = path42.join(dataDir, "workflow.yaml");
|
|
147220
148154
|
try {
|
|
147221
|
-
const content = await
|
|
148155
|
+
const content = await fs42.readFile(filePath, "utf-8");
|
|
147222
148156
|
const parsed = import_yaml4.default.parse(content);
|
|
147223
148157
|
if (!parsed || typeof parsed !== "object") {
|
|
147224
148158
|
return { name: "yaml:workflow", severity: "error", message: "workflow.yaml is empty or not an object" };
|
|
@@ -147237,7 +148171,7 @@ async function checkWorkflowYaml(dataDir) {
|
|
|
147237
148171
|
async function checkProjectsJson(dataDir) {
|
|
147238
148172
|
const filePath = path42.join(dataDir, "projects.json");
|
|
147239
148173
|
try {
|
|
147240
|
-
const content = await
|
|
148174
|
+
const content = await fs42.readFile(filePath, "utf-8");
|
|
147241
148175
|
const parsed = JSON.parse(content);
|
|
147242
148176
|
if (!parsed || typeof parsed !== "object") {
|
|
147243
148177
|
return { name: "json:projects", severity: "error", message: "projects.json is not an object" };
|
|
@@ -147314,7 +148248,7 @@ async function checkProjects(dataDir) {
|
|
|
147314
148248
|
const filePath = path42.join(dataDir, "projects.json");
|
|
147315
148249
|
let data;
|
|
147316
148250
|
try {
|
|
147317
|
-
const content = await
|
|
148251
|
+
const content = await fs42.readFile(filePath, "utf-8");
|
|
147318
148252
|
data = JSON.parse(content);
|
|
147319
148253
|
} catch {
|
|
147320
148254
|
return results;
|
|
@@ -147354,7 +148288,7 @@ async function checkProjects(dataDir) {
|
|
|
147354
148288
|
if (project.repo && path42.isAbsolute(project.repo)) {
|
|
147355
148289
|
const qaPath = path42.join(project.repo, "scripts", "qa.sh");
|
|
147356
148290
|
try {
|
|
147357
|
-
await
|
|
148291
|
+
await fs42.access(qaPath);
|
|
147358
148292
|
} catch {
|
|
147359
148293
|
results.push({
|
|
147360
148294
|
name: `project:${slug}:qa`,
|
|
@@ -147478,7 +148412,7 @@ function formatMetrics(metrics2) {
|
|
|
147478
148412
|
|
|
147479
148413
|
// lib/setup/security-doctor.ts
|
|
147480
148414
|
init_constants();
|
|
147481
|
-
import
|
|
148415
|
+
import fs43 from "node:fs/promises";
|
|
147482
148416
|
import path43 from "node:path";
|
|
147483
148417
|
var LEGACY_EXTENSION_PATH_PATTERNS = [
|
|
147484
148418
|
"/extensions/secureclaw/",
|
|
@@ -147503,7 +148437,7 @@ async function runSecurityDoctor(openclawHome) {
|
|
|
147503
148437
|
const checklistPath = path43.join(workspacePath, DATA_DIR, "prompts", "security-checklist.md");
|
|
147504
148438
|
let config2 = null;
|
|
147505
148439
|
try {
|
|
147506
|
-
config2 = JSON.parse(await
|
|
148440
|
+
config2 = JSON.parse(await fs43.readFile(configPath, "utf-8"));
|
|
147507
148441
|
checks.push({
|
|
147508
148442
|
name: "config:openclaw-json",
|
|
147509
148443
|
severity: "ok",
|
|
@@ -147570,7 +148504,7 @@ async function runSecurityDoctor(openclawHome) {
|
|
|
147570
148504
|
message: genesisBinding ? "Genesis agent owns the channel-wide Telegram DM binding" : "Genesis agent does not own a channel-wide Telegram binding"
|
|
147571
148505
|
});
|
|
147572
148506
|
try {
|
|
147573
|
-
await
|
|
148507
|
+
await fs43.access(checklistPath);
|
|
147574
148508
|
checks.push({
|
|
147575
148509
|
name: "workspace:security-checklist",
|
|
147576
148510
|
severity: "ok",
|
|
@@ -147584,7 +148518,7 @@ async function runSecurityDoctor(openclawHome) {
|
|
|
147584
148518
|
});
|
|
147585
148519
|
}
|
|
147586
148520
|
try {
|
|
147587
|
-
const jobsJson = JSON.parse(await
|
|
148521
|
+
const jobsJson = JSON.parse(await fs43.readFile(jobsPath, "utf-8"));
|
|
147588
148522
|
const jobs = jobsJson.jobs ?? [];
|
|
147589
148523
|
const legacyJobFindings = jobs.filter((job) => job.enabled !== false).flatMap((job) => {
|
|
147590
148524
|
const text = `${job.payload?.text ?? ""} ${job.payload?.message ?? ""}`;
|
|
@@ -148724,7 +149658,7 @@ function registerGitHubWebhookRoute(api, ctx) {
|
|
|
148724
149658
|
// lib/setup/gateway-lifecycle-hook.ts
|
|
148725
149659
|
init_constants();
|
|
148726
149660
|
init_audit();
|
|
148727
|
-
import * as
|
|
149661
|
+
import * as fs44 from "node:fs/promises";
|
|
148728
149662
|
import * as path45 from "node:path";
|
|
148729
149663
|
function registerGatewayLifecycleHook(api, ctx) {
|
|
148730
149664
|
const workspaceDir = resolveWorkspaceDir(ctx.config);
|
|
@@ -148733,7 +149667,7 @@ function registerGatewayLifecycleHook(api, ctx) {
|
|
|
148733
149667
|
const bootTime = Date.now();
|
|
148734
149668
|
const dataPath = path45.join(workspaceDir, DATA_DIR);
|
|
148735
149669
|
try {
|
|
148736
|
-
await
|
|
149670
|
+
await fs44.access(dataPath);
|
|
148737
149671
|
} catch {
|
|
148738
149672
|
await log(workspaceDir, "gateway_start_warning", {
|
|
148739
149673
|
message: `Workspace data directory missing: ${dataPath}`
|
|
@@ -148743,7 +149677,7 @@ function registerGatewayLifecycleHook(api, ctx) {
|
|
|
148743
149677
|
}
|
|
148744
149678
|
const projectsPath2 = path45.join(dataPath, "projects.json");
|
|
148745
149679
|
try {
|
|
148746
|
-
const raw = await
|
|
149680
|
+
const raw = await fs44.readFile(projectsPath2, "utf-8");
|
|
148747
149681
|
JSON.parse(raw);
|
|
148748
149682
|
} catch (err) {
|
|
148749
149683
|
await log(workspaceDir, "gateway_start_warning", {
|
|
@@ -149025,7 +149959,7 @@ function registerSubagentLifecycleHook(api, ctx) {
|
|
|
149025
149959
|
init_constants();
|
|
149026
149960
|
init_registry();
|
|
149027
149961
|
init_roles();
|
|
149028
|
-
import * as
|
|
149962
|
+
import * as fs45 from "node:fs";
|
|
149029
149963
|
import * as path46 from "node:path";
|
|
149030
149964
|
var ESCALATION_MAP = {
|
|
149031
149965
|
junior: "medior",
|
|
@@ -149052,7 +149986,7 @@ function registerModelResolveHook(api, ctx) {
|
|
|
149052
149986
|
if (!level) return;
|
|
149053
149987
|
try {
|
|
149054
149988
|
const projectsPath2 = path46.join(workspaceDir, DATA_DIR, "projects.json");
|
|
149055
|
-
const raw = await
|
|
149989
|
+
const raw = await fs45.promises.readFile(projectsPath2, "utf-8");
|
|
149056
149990
|
const projects = JSON.parse(raw);
|
|
149057
149991
|
const project = Object.values(projects).find((p) => p.slug === projectName);
|
|
149058
149992
|
if (!project?.workers?.[role]?.levels?.[level]) return;
|
|
@@ -149121,6 +150055,9 @@ Do not delegate implementation, testing, review, or planning to another coding a
|
|
|
149121
150055
|
Do not use nested coding agents.
|
|
149122
150056
|
Do not use planning or meta-skills such as brainstorming, writing-plans, or coding-agent.
|
|
149123
150057
|
Do not spawn, supervise, or instruct another agent to do the work for you.
|
|
150058
|
+
Do not weaken, replace, or bypass the canonical scripts/qa.sh contract just to make the task pass.
|
|
150059
|
+
Preserve the canonical QA gates: lint, types, security, tests, and coverage.
|
|
150060
|
+
If QA fails, fix the product code or project setup instead of rewriting the QA contract into ad-hoc scenario checks.
|
|
149124
150061
|
If you cannot proceed directly in the assigned worktree, end with your role's canonical blocked result line.
|
|
149125
150062
|
`;
|
|
149126
150063
|
var REVIEWER_EXECUTION_CONTRACT_CONTEXT = `## Execution Contract
|