@ametyst/cli 0.3.7 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1278 -1215
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -23203,13 +23203,13 @@ var init_getCallError = __esm({
|
|
|
23203
23203
|
|
|
23204
23204
|
// node_modules/.pnpm/viem@2.38.6_typescript@5.2.2_zod@4.3.6/node_modules/viem/_esm/utils/promise/withResolvers.js
|
|
23205
23205
|
function withResolvers() {
|
|
23206
|
-
let
|
|
23206
|
+
let resolve3 = () => void 0;
|
|
23207
23207
|
let reject = () => void 0;
|
|
23208
23208
|
const promise = new Promise((resolve_, reject_) => {
|
|
23209
|
-
|
|
23209
|
+
resolve3 = resolve_;
|
|
23210
23210
|
reject = reject_;
|
|
23211
23211
|
});
|
|
23212
|
-
return { promise, resolve, reject };
|
|
23212
|
+
return { promise, resolve: resolve3, reject };
|
|
23213
23213
|
}
|
|
23214
23214
|
var init_withResolvers = __esm({
|
|
23215
23215
|
"node_modules/.pnpm/viem@2.38.6_typescript@5.2.2_zod@4.3.6/node_modules/viem/_esm/utils/promise/withResolvers.js"() {
|
|
@@ -23230,8 +23230,8 @@ function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait2 = 0, sort
|
|
|
23230
23230
|
if (sort && Array.isArray(data))
|
|
23231
23231
|
data.sort(sort);
|
|
23232
23232
|
for (let i = 0; i < scheduler.length; i++) {
|
|
23233
|
-
const { resolve } = scheduler[i];
|
|
23234
|
-
|
|
23233
|
+
const { resolve: resolve3 } = scheduler[i];
|
|
23234
|
+
resolve3?.([data[i], data]);
|
|
23235
23235
|
}
|
|
23236
23236
|
}).catch((err) => {
|
|
23237
23237
|
for (let i = 0; i < scheduler.length; i++) {
|
|
@@ -23247,16 +23247,16 @@ function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait2 = 0, sort
|
|
|
23247
23247
|
return {
|
|
23248
23248
|
flush,
|
|
23249
23249
|
async schedule(args) {
|
|
23250
|
-
const { promise, resolve, reject } = withResolvers();
|
|
23250
|
+
const { promise, resolve: resolve3, reject } = withResolvers();
|
|
23251
23251
|
const split2 = shouldSplitBatch?.([...getBatchedArgs(), args]);
|
|
23252
23252
|
if (split2)
|
|
23253
23253
|
exec();
|
|
23254
23254
|
const hasActiveScheduler = getScheduler().length > 0;
|
|
23255
23255
|
if (hasActiveScheduler) {
|
|
23256
|
-
setScheduler({ args, resolve, reject });
|
|
23256
|
+
setScheduler({ args, resolve: resolve3, reject });
|
|
23257
23257
|
return promise;
|
|
23258
23258
|
}
|
|
23259
|
-
setScheduler({ args, resolve, reject });
|
|
23259
|
+
setScheduler({ args, resolve: resolve3, reject });
|
|
23260
23260
|
setTimeout(exec, wait2);
|
|
23261
23261
|
return promise;
|
|
23262
23262
|
}
|
|
@@ -24831,7 +24831,7 @@ var init_calls = __esm({
|
|
|
24831
24831
|
|
|
24832
24832
|
// node_modules/.pnpm/viem@2.38.6_typescript@5.2.2_zod@4.3.6/node_modules/viem/_esm/utils/promise/withRetry.js
|
|
24833
24833
|
function withRetry(fn, { delay: delay_ = 100, retryCount = 2, shouldRetry: shouldRetry2 = () => true } = {}) {
|
|
24834
|
-
return new Promise((
|
|
24834
|
+
return new Promise((resolve3, reject) => {
|
|
24835
24835
|
const attemptRetry = async ({ count = 0 } = {}) => {
|
|
24836
24836
|
const retry = async ({ error }) => {
|
|
24837
24837
|
const delay = typeof delay_ === "function" ? delay_({ count, error }) : delay_;
|
|
@@ -24841,7 +24841,7 @@ function withRetry(fn, { delay: delay_ = 100, retryCount = 2, shouldRetry: shoul
|
|
|
24841
24841
|
};
|
|
24842
24842
|
try {
|
|
24843
24843
|
const data = await fn();
|
|
24844
|
-
|
|
24844
|
+
resolve3(data);
|
|
24845
24845
|
} catch (err) {
|
|
24846
24846
|
if (count < retryCount && await shouldRetry2({ count, error: err }))
|
|
24847
24847
|
return retry({ error: err });
|
|
@@ -24963,7 +24963,7 @@ async function sendCalls(client, parameters) {
|
|
|
24963
24963
|
});
|
|
24964
24964
|
promises.push(promise);
|
|
24965
24965
|
if (experimental_fallbackDelay > 0)
|
|
24966
|
-
await new Promise((
|
|
24966
|
+
await new Promise((resolve3) => setTimeout(resolve3, experimental_fallbackDelay));
|
|
24967
24967
|
}
|
|
24968
24968
|
const results = await Promise.allSettled(promises);
|
|
24969
24969
|
if (results.every((r) => r.status === "rejected"))
|
|
@@ -25098,9 +25098,9 @@ async function waitForCallsStatus(client, parameters) {
|
|
|
25098
25098
|
throwOnFailure = false
|
|
25099
25099
|
} = parameters;
|
|
25100
25100
|
const observerId = stringify(["waitForCallsStatus", client.uid, id]);
|
|
25101
|
-
const { promise, resolve, reject } = withResolvers();
|
|
25101
|
+
const { promise, resolve: resolve3, reject } = withResolvers();
|
|
25102
25102
|
let timer;
|
|
25103
|
-
const unobserve = observe(observerId, { resolve, reject }, (emit2) => {
|
|
25103
|
+
const unobserve = observe(observerId, { resolve: resolve3, reject }, (emit2) => {
|
|
25104
25104
|
const unpoll = poll(async () => {
|
|
25105
25105
|
const done = (fn) => {
|
|
25106
25106
|
clearTimeout(timer);
|
|
@@ -25511,13 +25511,13 @@ async function isImageUri(uri) {
|
|
|
25511
25511
|
}
|
|
25512
25512
|
if (!Object.hasOwn(globalThis, "Image"))
|
|
25513
25513
|
return false;
|
|
25514
|
-
return new Promise((
|
|
25514
|
+
return new Promise((resolve3) => {
|
|
25515
25515
|
const img = new Image();
|
|
25516
25516
|
img.onload = () => {
|
|
25517
|
-
|
|
25517
|
+
resolve3(true);
|
|
25518
25518
|
};
|
|
25519
25519
|
img.onerror = () => {
|
|
25520
|
-
|
|
25520
|
+
resolve3(false);
|
|
25521
25521
|
};
|
|
25522
25522
|
img.src = uri;
|
|
25523
25523
|
});
|
|
@@ -27351,7 +27351,7 @@ var init_nonceManager = __esm({
|
|
|
27351
27351
|
|
|
27352
27352
|
// node_modules/.pnpm/viem@2.38.6_typescript@5.2.2_zod@4.3.6/node_modules/viem/_esm/utils/promise/withTimeout.js
|
|
27353
27353
|
function withTimeout(fn, { errorInstance = new Error("timed out"), timeout, signal }) {
|
|
27354
|
-
return new Promise((
|
|
27354
|
+
return new Promise((resolve3, reject) => {
|
|
27355
27355
|
;
|
|
27356
27356
|
(async () => {
|
|
27357
27357
|
let timeoutId;
|
|
@@ -27366,7 +27366,7 @@ function withTimeout(fn, { errorInstance = new Error("timed out"), timeout, sign
|
|
|
27366
27366
|
}
|
|
27367
27367
|
}, timeout);
|
|
27368
27368
|
}
|
|
27369
|
-
|
|
27369
|
+
resolve3(await fn({ signal: controller?.signal || null }));
|
|
27370
27370
|
} catch (err) {
|
|
27371
27371
|
if (err?.name === "AbortError")
|
|
27372
27372
|
reject(errorInstance);
|
|
@@ -29757,7 +29757,7 @@ var require_extension = __commonJS({
|
|
|
29757
29757
|
if (dest[name] === void 0) dest[name] = [elem];
|
|
29758
29758
|
else dest[name].push(elem);
|
|
29759
29759
|
}
|
|
29760
|
-
function
|
|
29760
|
+
function parse5(header) {
|
|
29761
29761
|
const offers = /* @__PURE__ */ Object.create(null);
|
|
29762
29762
|
let params = /* @__PURE__ */ Object.create(null);
|
|
29763
29763
|
let mustUnescape = false;
|
|
@@ -29897,7 +29897,7 @@ var require_extension = __commonJS({
|
|
|
29897
29897
|
}).join(", ");
|
|
29898
29898
|
}).join(", ");
|
|
29899
29899
|
}
|
|
29900
|
-
module.exports = { format, parse:
|
|
29900
|
+
module.exports = { format, parse: parse5 };
|
|
29901
29901
|
}
|
|
29902
29902
|
});
|
|
29903
29903
|
|
|
@@ -29932,7 +29932,7 @@ var require_websocket = __commonJS({
|
|
|
29932
29932
|
var {
|
|
29933
29933
|
EventTarget: { addEventListener: addEventListener2, removeEventListener: removeEventListener2 }
|
|
29934
29934
|
} = require_event_target();
|
|
29935
|
-
var { format, parse:
|
|
29935
|
+
var { format, parse: parse5 } = require_extension();
|
|
29936
29936
|
var { toBuffer: toBuffer2 } = require_buffer_util();
|
|
29937
29937
|
var kAborted = /* @__PURE__ */ Symbol("kAborted");
|
|
29938
29938
|
var protocolVersions = [8, 13];
|
|
@@ -30609,7 +30609,7 @@ var require_websocket = __commonJS({
|
|
|
30609
30609
|
}
|
|
30610
30610
|
let extensions;
|
|
30611
30611
|
try {
|
|
30612
|
-
extensions =
|
|
30612
|
+
extensions = parse5(secWebSocketExtensions);
|
|
30613
30613
|
} catch (err) {
|
|
30614
30614
|
const message = "Invalid Sec-WebSocket-Extensions header";
|
|
30615
30615
|
abortHandshake(websocket, socket, message);
|
|
@@ -30903,7 +30903,7 @@ var require_subprotocol = __commonJS({
|
|
|
30903
30903
|
"use strict";
|
|
30904
30904
|
init_esm_shims();
|
|
30905
30905
|
var { tokenChars } = require_validation();
|
|
30906
|
-
function
|
|
30906
|
+
function parse5(header) {
|
|
30907
30907
|
const protocols = /* @__PURE__ */ new Set();
|
|
30908
30908
|
let start = -1;
|
|
30909
30909
|
let end = -1;
|
|
@@ -30939,7 +30939,7 @@ var require_subprotocol = __commonJS({
|
|
|
30939
30939
|
protocols.add(protocol4);
|
|
30940
30940
|
return protocols;
|
|
30941
30941
|
}
|
|
30942
|
-
module.exports = { parse:
|
|
30942
|
+
module.exports = { parse: parse5 };
|
|
30943
30943
|
}
|
|
30944
30944
|
});
|
|
30945
30945
|
|
|
@@ -31447,10 +31447,10 @@ async function getWebSocketRpcClient(url2, options = {}) {
|
|
|
31447
31447
|
socket.addEventListener("error", onError);
|
|
31448
31448
|
socket.addEventListener("open", onOpen);
|
|
31449
31449
|
if (socket.readyState === WebSocket4.CONNECTING) {
|
|
31450
|
-
await new Promise((
|
|
31450
|
+
await new Promise((resolve3, reject) => {
|
|
31451
31451
|
if (!socket)
|
|
31452
31452
|
return;
|
|
31453
|
-
socket.onopen =
|
|
31453
|
+
socket.onopen = resolve3;
|
|
31454
31454
|
socket.onerror = reject;
|
|
31455
31455
|
});
|
|
31456
31456
|
}
|
|
@@ -35738,13 +35738,13 @@ async function waitForTransactionReceipt(client, parameters) {
|
|
|
35738
35738
|
let retrying = false;
|
|
35739
35739
|
let _unobserve;
|
|
35740
35740
|
let _unwatch;
|
|
35741
|
-
const { promise, resolve, reject } = withResolvers();
|
|
35741
|
+
const { promise, resolve: resolve3, reject } = withResolvers();
|
|
35742
35742
|
const timer = timeout ? setTimeout(() => {
|
|
35743
35743
|
_unwatch?.();
|
|
35744
35744
|
_unobserve?.();
|
|
35745
35745
|
reject(new WaitForTransactionReceiptTimeoutError({ hash: hash3 }));
|
|
35746
35746
|
}, timeout) : void 0;
|
|
35747
|
-
_unobserve = observe(observerId, { onReplaced, resolve, reject }, async (emit2) => {
|
|
35747
|
+
_unobserve = observe(observerId, { onReplaced, resolve: resolve3, reject }, async (emit2) => {
|
|
35748
35748
|
receipt = await getAction(client, getTransactionReceipt, "getTransactionReceipt")({ hash: hash3 }).catch(() => void 0);
|
|
35749
35749
|
if (receipt && confirmations <= 1) {
|
|
35750
35750
|
clearTimeout(timer);
|
|
@@ -38095,7 +38095,7 @@ function webSocket(url2, config = {}) {
|
|
|
38095
38095
|
},
|
|
38096
38096
|
async subscribe({ params, onData, onError }) {
|
|
38097
38097
|
const rpcClient = await getWebSocketRpcClient(url_, wsRpcClientOpts);
|
|
38098
|
-
const { result: subscriptionId } = await new Promise((
|
|
38098
|
+
const { result: subscriptionId } = await new Promise((resolve3, reject) => rpcClient.request({
|
|
38099
38099
|
body: {
|
|
38100
38100
|
method: "eth_subscribe",
|
|
38101
38101
|
params
|
|
@@ -38112,7 +38112,7 @@ function webSocket(url2, config = {}) {
|
|
|
38112
38112
|
return;
|
|
38113
38113
|
}
|
|
38114
38114
|
if (typeof response.id === "number") {
|
|
38115
|
-
|
|
38115
|
+
resolve3(response);
|
|
38116
38116
|
return;
|
|
38117
38117
|
}
|
|
38118
38118
|
if (response.method !== "eth_subscription")
|
|
@@ -38123,12 +38123,12 @@ function webSocket(url2, config = {}) {
|
|
|
38123
38123
|
return {
|
|
38124
38124
|
subscriptionId,
|
|
38125
38125
|
async unsubscribe() {
|
|
38126
|
-
return new Promise((
|
|
38126
|
+
return new Promise((resolve3) => rpcClient.request({
|
|
38127
38127
|
body: {
|
|
38128
38128
|
method: "eth_unsubscribe",
|
|
38129
38129
|
params: [subscriptionId]
|
|
38130
38130
|
},
|
|
38131
|
-
onResponse:
|
|
38131
|
+
onResponse: resolve3
|
|
38132
38132
|
}));
|
|
38133
38133
|
}
|
|
38134
38134
|
};
|
|
@@ -66783,8 +66783,8 @@ function waitForUserOperationReceipt(client, parameters) {
|
|
|
66783
66783
|
client.uid,
|
|
66784
66784
|
hash3
|
|
66785
66785
|
]);
|
|
66786
|
-
return new Promise((
|
|
66787
|
-
const unobserve = observe(observerId, { resolve, reject }, (emit2) => {
|
|
66786
|
+
return new Promise((resolve3, reject) => {
|
|
66787
|
+
const unobserve = observe(observerId, { resolve: resolve3, reject }, (emit2) => {
|
|
66788
66788
|
const done = (fn) => {
|
|
66789
66789
|
unpoll();
|
|
66790
66790
|
fn();
|
|
@@ -67779,7 +67779,7 @@ var require_parse = __commonJS({
|
|
|
67779
67779
|
"use strict";
|
|
67780
67780
|
init_esm_shims();
|
|
67781
67781
|
var SemVer = require_semver();
|
|
67782
|
-
var
|
|
67782
|
+
var parse5 = (version5, options, throwErrors = false) => {
|
|
67783
67783
|
if (version5 instanceof SemVer) {
|
|
67784
67784
|
return version5;
|
|
67785
67785
|
}
|
|
@@ -67792,7 +67792,7 @@ var require_parse = __commonJS({
|
|
|
67792
67792
|
throw er;
|
|
67793
67793
|
}
|
|
67794
67794
|
};
|
|
67795
|
-
module.exports =
|
|
67795
|
+
module.exports = parse5;
|
|
67796
67796
|
}
|
|
67797
67797
|
});
|
|
67798
67798
|
|
|
@@ -67801,9 +67801,9 @@ var require_valid = __commonJS({
|
|
|
67801
67801
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/valid.js"(exports, module) {
|
|
67802
67802
|
"use strict";
|
|
67803
67803
|
init_esm_shims();
|
|
67804
|
-
var
|
|
67804
|
+
var parse5 = require_parse();
|
|
67805
67805
|
var valid = (version5, options) => {
|
|
67806
|
-
const v =
|
|
67806
|
+
const v = parse5(version5, options);
|
|
67807
67807
|
return v ? v.version : null;
|
|
67808
67808
|
};
|
|
67809
67809
|
module.exports = valid;
|
|
@@ -67815,9 +67815,9 @@ var require_clean = __commonJS({
|
|
|
67815
67815
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/clean.js"(exports, module) {
|
|
67816
67816
|
"use strict";
|
|
67817
67817
|
init_esm_shims();
|
|
67818
|
-
var
|
|
67818
|
+
var parse5 = require_parse();
|
|
67819
67819
|
var clean2 = (version5, options) => {
|
|
67820
|
-
const s =
|
|
67820
|
+
const s = parse5(version5.trim().replace(/^[=v]+/, ""), options);
|
|
67821
67821
|
return s ? s.version : null;
|
|
67822
67822
|
};
|
|
67823
67823
|
module.exports = clean2;
|
|
@@ -67854,10 +67854,10 @@ var require_diff = __commonJS({
|
|
|
67854
67854
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/diff.js"(exports, module) {
|
|
67855
67855
|
"use strict";
|
|
67856
67856
|
init_esm_shims();
|
|
67857
|
-
var
|
|
67857
|
+
var parse5 = require_parse();
|
|
67858
67858
|
var diff = (version1, version22) => {
|
|
67859
|
-
const v1 =
|
|
67860
|
-
const v2 =
|
|
67859
|
+
const v1 = parse5(version1, null, true);
|
|
67860
|
+
const v2 = parse5(version22, null, true);
|
|
67861
67861
|
const comparison = v1.compare(v2);
|
|
67862
67862
|
if (comparison === 0) {
|
|
67863
67863
|
return null;
|
|
@@ -67932,9 +67932,9 @@ var require_prerelease = __commonJS({
|
|
|
67932
67932
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/prerelease.js"(exports, module) {
|
|
67933
67933
|
"use strict";
|
|
67934
67934
|
init_esm_shims();
|
|
67935
|
-
var
|
|
67935
|
+
var parse5 = require_parse();
|
|
67936
67936
|
var prerelease = (version5, options) => {
|
|
67937
|
-
const parsed =
|
|
67937
|
+
const parsed = parse5(version5, options);
|
|
67938
67938
|
return parsed && parsed.prerelease.length ? parsed.prerelease : null;
|
|
67939
67939
|
};
|
|
67940
67940
|
module.exports = prerelease;
|
|
@@ -68134,7 +68134,7 @@ var require_coerce = __commonJS({
|
|
|
68134
68134
|
"use strict";
|
|
68135
68135
|
init_esm_shims();
|
|
68136
68136
|
var SemVer = require_semver();
|
|
68137
|
-
var
|
|
68137
|
+
var parse5 = require_parse();
|
|
68138
68138
|
var { safeRe: re2, t } = require_re();
|
|
68139
68139
|
var coerce = (version5, options) => {
|
|
68140
68140
|
if (version5 instanceof SemVer) {
|
|
@@ -68169,7 +68169,7 @@ var require_coerce = __commonJS({
|
|
|
68169
68169
|
const patch = match[4] || "0";
|
|
68170
68170
|
const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : "";
|
|
68171
68171
|
const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
68172
|
-
return
|
|
68172
|
+
return parse5(`${major}.${minor}.${patch}${prerelease}${build}`, options);
|
|
68173
68173
|
};
|
|
68174
68174
|
module.exports = coerce;
|
|
68175
68175
|
}
|
|
@@ -68180,11 +68180,11 @@ var require_truncate = __commonJS({
|
|
|
68180
68180
|
"node_modules/.pnpm/semver@7.8.1/node_modules/semver/functions/truncate.js"(exports, module) {
|
|
68181
68181
|
"use strict";
|
|
68182
68182
|
init_esm_shims();
|
|
68183
|
-
var
|
|
68184
|
-
var
|
|
68183
|
+
var parse5 = require_parse();
|
|
68184
|
+
var constants5 = require_constants2();
|
|
68185
68185
|
var SemVer = require_semver();
|
|
68186
68186
|
var truncate = (version5, truncation, options) => {
|
|
68187
|
-
if (!
|
|
68187
|
+
if (!constants5.RELEASE_TYPES.includes(truncation)) {
|
|
68188
68188
|
return null;
|
|
68189
68189
|
}
|
|
68190
68190
|
const clonedVersion = cloneInputVersion(version5, options);
|
|
@@ -68192,7 +68192,7 @@ var require_truncate = __commonJS({
|
|
|
68192
68192
|
};
|
|
68193
68193
|
var cloneInputVersion = (version5, options) => {
|
|
68194
68194
|
const versionStringToParse = version5 instanceof SemVer ? version5.version : version5;
|
|
68195
|
-
return
|
|
68195
|
+
return parse5(versionStringToParse, options);
|
|
68196
68196
|
};
|
|
68197
68197
|
var doTruncation = (version5, truncation) => {
|
|
68198
68198
|
if (isPrerelease(truncation)) {
|
|
@@ -69244,10 +69244,10 @@ var require_semver2 = __commonJS({
|
|
|
69244
69244
|
"use strict";
|
|
69245
69245
|
init_esm_shims();
|
|
69246
69246
|
var internalRe = require_re();
|
|
69247
|
-
var
|
|
69247
|
+
var constants5 = require_constants2();
|
|
69248
69248
|
var SemVer = require_semver();
|
|
69249
69249
|
var identifiers = require_identifiers();
|
|
69250
|
-
var
|
|
69250
|
+
var parse5 = require_parse();
|
|
69251
69251
|
var valid = require_valid();
|
|
69252
69252
|
var clean2 = require_clean();
|
|
69253
69253
|
var inc = require_inc();
|
|
@@ -69286,7 +69286,7 @@ var require_semver2 = __commonJS({
|
|
|
69286
69286
|
var simplifyRange = require_simplify();
|
|
69287
69287
|
var subset = require_subset();
|
|
69288
69288
|
module.exports = {
|
|
69289
|
-
parse:
|
|
69289
|
+
parse: parse5,
|
|
69290
69290
|
valid,
|
|
69291
69291
|
clean: clean2,
|
|
69292
69292
|
inc,
|
|
@@ -69328,8 +69328,8 @@ var require_semver2 = __commonJS({
|
|
|
69328
69328
|
re: internalRe.re,
|
|
69329
69329
|
src: internalRe.src,
|
|
69330
69330
|
tokens: internalRe.t,
|
|
69331
|
-
SEMVER_SPEC_VERSION:
|
|
69332
|
-
RELEASE_TYPES:
|
|
69331
|
+
SEMVER_SPEC_VERSION: constants5.SEMVER_SPEC_VERSION,
|
|
69332
|
+
RELEASE_TYPES: constants5.RELEASE_TYPES,
|
|
69333
69333
|
compareIdentifiers: identifiers.compareIdentifiers,
|
|
69334
69334
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
69335
69335
|
};
|
|
@@ -77915,7 +77915,7 @@ function bufferToUTF8String(value2) {
|
|
|
77915
77915
|
function browserSupportsWebAuthnAutofill() {
|
|
77916
77916
|
const globalPublicKeyCredential = window.PublicKeyCredential;
|
|
77917
77917
|
if (globalPublicKeyCredential.isConditionalMediationAvailable === void 0) {
|
|
77918
|
-
return new Promise((
|
|
77918
|
+
return new Promise((resolve3) => resolve3(false));
|
|
77919
77919
|
}
|
|
77920
77920
|
return globalPublicKeyCredential.isConditionalMediationAvailable();
|
|
77921
77921
|
}
|
|
@@ -78019,7 +78019,7 @@ async function startAuthentication(requestOptionsJSON, useBrowserAutofill = fals
|
|
|
78019
78019
|
}
|
|
78020
78020
|
function platformAuthenticatorIsAvailable() {
|
|
78021
78021
|
if (!browserSupportsWebAuthn()) {
|
|
78022
|
-
return new Promise((
|
|
78022
|
+
return new Promise((resolve3) => resolve3(false));
|
|
78023
78023
|
}
|
|
78024
78024
|
return PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
|
78025
78025
|
}
|
|
@@ -78405,7 +78405,7 @@ function bufferToUTF8String2(value2) {
|
|
|
78405
78405
|
function browserSupportsWebAuthnAutofill2() {
|
|
78406
78406
|
const globalPublicKeyCredential = window.PublicKeyCredential;
|
|
78407
78407
|
if (globalPublicKeyCredential.isConditionalMediationAvailable === void 0) {
|
|
78408
|
-
return new Promise((
|
|
78408
|
+
return new Promise((resolve3) => resolve3(false));
|
|
78409
78409
|
}
|
|
78410
78410
|
return globalPublicKeyCredential.isConditionalMediationAvailable();
|
|
78411
78411
|
}
|
|
@@ -78509,7 +78509,7 @@ async function startAuthentication2(requestOptionsJSON, useBrowserAutofill = fal
|
|
|
78509
78509
|
}
|
|
78510
78510
|
function platformAuthenticatorIsAvailable2() {
|
|
78511
78511
|
if (!browserSupportsWebAuthn2()) {
|
|
78512
|
-
return new Promise((
|
|
78512
|
+
return new Promise((resolve3) => resolve3(false));
|
|
78513
78513
|
}
|
|
78514
78514
|
return PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
|
78515
78515
|
}
|
|
@@ -78896,18 +78896,18 @@ var init_initialization = __esm({
|
|
|
78896
78896
|
async createBaseContracts() {
|
|
78897
78897
|
const eoaAccount = await this.actions.createEoaAccount();
|
|
78898
78898
|
const kernelClient = await this.actions.createKernelClientWithEoa(eoaAccount);
|
|
78899
|
-
await new Promise((
|
|
78899
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78900
78900
|
const recoveryEscrowFactoryAddress = await this.actions.deployContractAndGetAddress(kernelClient, RecoveryEscrowFactoryAbi, RecoveryEscrowFactoryBytecode, []);
|
|
78901
|
-
await new Promise((
|
|
78901
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78902
78902
|
const mockTokenAddress = await this.actions.deployContractAndGetAddress(kernelClient, MockTokenAbi, MockTokenBytecode, [
|
|
78903
78903
|
"MockToken",
|
|
78904
78904
|
"MTK",
|
|
78905
78905
|
BigInt(1e6) * BigInt(10 ** 6),
|
|
78906
78906
|
6
|
|
78907
78907
|
]);
|
|
78908
|
-
await new Promise((
|
|
78908
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78909
78909
|
const paymentManagersRegistryAddress = await this.actions.deployContractAndGetAddress(kernelClient, PaymentManagersRegistryAbi, PaymentManagersRegistryBytecode, []);
|
|
78910
|
-
await new Promise((
|
|
78910
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78911
78911
|
return {
|
|
78912
78912
|
recoveryEscrowFactoryAddress,
|
|
78913
78913
|
mockTokenAddress,
|
|
@@ -78918,9 +78918,9 @@ var init_initialization = __esm({
|
|
|
78918
78918
|
async initializePolicies() {
|
|
78919
78919
|
const eoaAccount = await this.actions.createEoaAccount();
|
|
78920
78920
|
const kernelClient = await this.actions.createKernelClientWithEoa(eoaAccount);
|
|
78921
|
-
await new Promise((
|
|
78921
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78922
78922
|
const timestampPolicyAddress = await this.actions.deployContractAndGetAddress(kernelClient, TimestampPolicyAbi, TimestampPolicyBytecode, []);
|
|
78923
|
-
await new Promise((
|
|
78923
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78924
78924
|
return {
|
|
78925
78925
|
timestampPolicyAddress
|
|
78926
78926
|
};
|
|
@@ -78929,7 +78929,7 @@ var init_initialization = __esm({
|
|
|
78929
78929
|
const newPrivateKey = generatePrivateKey();
|
|
78930
78930
|
const eoaAccount = privateKeyToAccount(newPrivateKey);
|
|
78931
78931
|
const kernelClient = await this.actions.createKernelClientWithEoa(eoaAccount);
|
|
78932
|
-
await new Promise((
|
|
78932
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78933
78933
|
return { address: eoaAccount.address, privateKey: newPrivateKey, smartAccount: kernelClient.account.address };
|
|
78934
78934
|
}
|
|
78935
78935
|
async addMoneyToTestAccount(testAccount, mockTokenAddress) {
|
|
@@ -78961,13 +78961,13 @@ var init_initialization = __esm({
|
|
|
78961
78961
|
const txReceipt = await publicClient.waitForTransactionReceipt({
|
|
78962
78962
|
hash: userOpReceipt.receipt.transactionHash
|
|
78963
78963
|
});
|
|
78964
|
-
await new Promise((
|
|
78964
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78965
78965
|
return true;
|
|
78966
78966
|
}
|
|
78967
78967
|
async sendMoneyToTestAccount(testAccount, mockTokenAddress, amount, recipientAddress) {
|
|
78968
78968
|
const eoaAccount = privateKeyToAccount(testAccount.privateKey);
|
|
78969
78969
|
const kernelClient = await this.actions.createKernelClientWithEoa(eoaAccount);
|
|
78970
|
-
await new Promise((
|
|
78970
|
+
await new Promise((resolve3) => setTimeout(resolve3, 5e3));
|
|
78971
78971
|
const publicClient = createPublicClient({
|
|
78972
78972
|
chain: CHAINS.BASE_SEPOLIA,
|
|
78973
78973
|
transport: http(this.bundlerPaymasterUrl)
|
|
@@ -78994,7 +78994,7 @@ var init_initialization = __esm({
|
|
|
78994
78994
|
const txReceipt = await publicClient.waitForTransactionReceipt({
|
|
78995
78995
|
hash: userOpReceipt.receipt.transactionHash
|
|
78996
78996
|
});
|
|
78997
|
-
await new Promise((
|
|
78997
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
78998
78998
|
return true;
|
|
78999
78999
|
}
|
|
79000
79000
|
async createPolicyFreeSessionKey(kernelClient, kernelAccountAddress, paymentManagerAddress) {
|
|
@@ -79017,7 +79017,7 @@ var init_initialization = __esm({
|
|
|
79017
79017
|
policies: [rootPolicy]
|
|
79018
79018
|
});
|
|
79019
79019
|
const configurePermissions = await this.permissions.configurePermissions(kernelClient, publicClient, sessionPermission, kernelAccountAddress, paymentManagerAddress);
|
|
79020
|
-
await new Promise((
|
|
79020
|
+
await new Promise((resolve3) => setTimeout(resolve3, 5e3));
|
|
79021
79021
|
return { sessionKeyAddress: sessionKeySigner.account.address, sessionKeyPrivateKey: sessionPrivateKey };
|
|
79022
79022
|
}
|
|
79023
79023
|
};
|
|
@@ -79753,13 +79753,13 @@ var init_financial_accounts = __esm({
|
|
|
79753
79753
|
await this.installExecutor(kernelClient, publicClient, kernelAccountAddress, paymentManagerAddress);
|
|
79754
79754
|
console.error("[SIGNUP 8] installExecutor DONE");
|
|
79755
79755
|
console.error("[SIGNUP 9] waiting 10s for nonce propagation...");
|
|
79756
|
-
await new Promise((
|
|
79756
|
+
await new Promise((resolve3) => setTimeout(resolve3, 1e4));
|
|
79757
79757
|
onProgress?.("configure_permissions");
|
|
79758
79758
|
console.error("[SIGNUP 10] calling permissions.configurePermissions (PASSKEY SIGN #2 expected)...");
|
|
79759
79759
|
const configurePermissions = await this.permissions.configurePermissions(kernelClient, publicClient, sessionPermission, kernelAccountAddress, paymentManagerAddress);
|
|
79760
79760
|
console.error("[SIGNUP 10b] configurePermissions DONE", configurePermissions);
|
|
79761
79761
|
console.error("[SIGNUP 11] waiting 5s...");
|
|
79762
|
-
await new Promise((
|
|
79762
|
+
await new Promise((resolve3) => setTimeout(resolve3, 5e3));
|
|
79763
79763
|
}
|
|
79764
79764
|
const sessionKernelAccountConfig = {
|
|
79765
79765
|
address: kernelAccountAddress,
|
|
@@ -81898,7 +81898,7 @@ var init_permissions = __esm({
|
|
|
81898
81898
|
const permissionInstallReceipt = await publicClient.waitForTransactionReceipt({
|
|
81899
81899
|
hash: permissionInstallUserOpReceipt.receipt.transactionHash
|
|
81900
81900
|
});
|
|
81901
|
-
await new Promise((
|
|
81901
|
+
await new Promise((resolve3) => setTimeout(resolve3, 5e3));
|
|
81902
81902
|
const grantAccessUserOpHash = await kernelClient.sendUserOperation({
|
|
81903
81903
|
callData: delegateCall,
|
|
81904
81904
|
callGasLimit: 2000000n
|
|
@@ -81957,7 +81957,7 @@ var init_permissions = __esm({
|
|
|
81957
81957
|
let lastTxHash = batchUserOpReceipt.receipt.transactionHash;
|
|
81958
81958
|
for (const input of installInputs) {
|
|
81959
81959
|
if (grantDelayMs > 0) {
|
|
81960
|
-
await new Promise((
|
|
81960
|
+
await new Promise((resolve3) => setTimeout(resolve3, grantDelayMs));
|
|
81961
81961
|
}
|
|
81962
81962
|
const grantAccessFunctionData = buildGrantAccessFunctionData(input.identifier);
|
|
81963
81963
|
const delegateCall = await encodeCallData2([
|
|
@@ -82011,7 +82011,7 @@ var init_permissions = __esm({
|
|
|
82011
82011
|
await publicClient.waitForTransactionReceipt({
|
|
82012
82012
|
hash: revokeAccessUserOpReceipt.receipt.transactionHash
|
|
82013
82013
|
});
|
|
82014
|
-
await new Promise((
|
|
82014
|
+
await new Promise((resolve3) => setTimeout(resolve3, 5e3));
|
|
82015
82015
|
const deinitData = await permissionPlugin.getEnableData(kernelAccountAddress);
|
|
82016
82016
|
const uninstallFunctionData = encodeFunctionData({
|
|
82017
82017
|
abi: KernelV3_3AccountAbi,
|
|
@@ -82428,7 +82428,7 @@ var init_virtual_wallets_managers = __esm({
|
|
|
82428
82428
|
policyValidUntil = built.policyValidUntil;
|
|
82429
82429
|
spendingLimitWire = built.spendingLimitWire;
|
|
82430
82430
|
const configurePermissions = await this.permissions.configurePermissions(kernelClient, publicClient, built.sessionPermission, kernelAccountAddress, paymentManagerAddress);
|
|
82431
|
-
await new Promise((
|
|
82431
|
+
await new Promise((resolve3) => setTimeout(resolve3, 5e3));
|
|
82432
82432
|
}
|
|
82433
82433
|
}
|
|
82434
82434
|
const response = await this.buyerAPIs.walletApproval(jwtToken, {
|
|
@@ -82819,7 +82819,7 @@ var init_bundler_retry = __esm({
|
|
|
82819
82819
|
factor: 2,
|
|
82820
82820
|
maxMs: 8e3
|
|
82821
82821
|
};
|
|
82822
|
-
defaultSleep = (ms) => new Promise((
|
|
82822
|
+
defaultSleep = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
82823
82823
|
}
|
|
82824
82824
|
});
|
|
82825
82825
|
|
|
@@ -86276,7 +86276,7 @@ var init_transactions = __esm({
|
|
|
86276
86276
|
async waitBeforeDeferredPermitRetry(attempt) {
|
|
86277
86277
|
const delayMs = _transactions.DEFERRED_PERMIT_RETRY_DELAYS_MS[attempt] ?? 0;
|
|
86278
86278
|
if (delayMs > 0)
|
|
86279
|
-
await new Promise((
|
|
86279
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
86280
86280
|
}
|
|
86281
86281
|
async cancelAuthorization(kernelClient, paymentManagerAddress, wrappedHash) {
|
|
86282
86282
|
console.error("[spend] cancelAuthorization called with:", {
|
|
@@ -90107,11 +90107,11 @@ var require_codegen = __commonJS({
|
|
|
90107
90107
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
90108
90108
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
90109
90109
|
}
|
|
90110
|
-
optimizeNames(names,
|
|
90110
|
+
optimizeNames(names, constants5) {
|
|
90111
90111
|
if (!names[this.name.str])
|
|
90112
90112
|
return;
|
|
90113
90113
|
if (this.rhs)
|
|
90114
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
90114
|
+
this.rhs = optimizeExpr(this.rhs, names, constants5);
|
|
90115
90115
|
return this;
|
|
90116
90116
|
}
|
|
90117
90117
|
get names() {
|
|
@@ -90128,10 +90128,10 @@ var require_codegen = __commonJS({
|
|
|
90128
90128
|
render({ _n }) {
|
|
90129
90129
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
90130
90130
|
}
|
|
90131
|
-
optimizeNames(names,
|
|
90131
|
+
optimizeNames(names, constants5) {
|
|
90132
90132
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
90133
90133
|
return;
|
|
90134
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
90134
|
+
this.rhs = optimizeExpr(this.rhs, names, constants5);
|
|
90135
90135
|
return this;
|
|
90136
90136
|
}
|
|
90137
90137
|
get names() {
|
|
@@ -90192,8 +90192,8 @@ var require_codegen = __commonJS({
|
|
|
90192
90192
|
optimizeNodes() {
|
|
90193
90193
|
return `${this.code}` ? this : void 0;
|
|
90194
90194
|
}
|
|
90195
|
-
optimizeNames(names,
|
|
90196
|
-
this.code = optimizeExpr(this.code, names,
|
|
90195
|
+
optimizeNames(names, constants5) {
|
|
90196
|
+
this.code = optimizeExpr(this.code, names, constants5);
|
|
90197
90197
|
return this;
|
|
90198
90198
|
}
|
|
90199
90199
|
get names() {
|
|
@@ -90222,12 +90222,12 @@ var require_codegen = __commonJS({
|
|
|
90222
90222
|
}
|
|
90223
90223
|
return nodes.length > 0 ? this : void 0;
|
|
90224
90224
|
}
|
|
90225
|
-
optimizeNames(names,
|
|
90225
|
+
optimizeNames(names, constants5) {
|
|
90226
90226
|
const { nodes } = this;
|
|
90227
90227
|
let i = nodes.length;
|
|
90228
90228
|
while (i--) {
|
|
90229
90229
|
const n = nodes[i];
|
|
90230
|
-
if (n.optimizeNames(names,
|
|
90230
|
+
if (n.optimizeNames(names, constants5))
|
|
90231
90231
|
continue;
|
|
90232
90232
|
subtractNames(names, n.names);
|
|
90233
90233
|
nodes.splice(i, 1);
|
|
@@ -90280,12 +90280,12 @@ var require_codegen = __commonJS({
|
|
|
90280
90280
|
return void 0;
|
|
90281
90281
|
return this;
|
|
90282
90282
|
}
|
|
90283
|
-
optimizeNames(names,
|
|
90283
|
+
optimizeNames(names, constants5) {
|
|
90284
90284
|
var _a;
|
|
90285
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
90286
|
-
if (!(super.optimizeNames(names,
|
|
90285
|
+
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants5);
|
|
90286
|
+
if (!(super.optimizeNames(names, constants5) || this.else))
|
|
90287
90287
|
return;
|
|
90288
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
90288
|
+
this.condition = optimizeExpr(this.condition, names, constants5);
|
|
90289
90289
|
return this;
|
|
90290
90290
|
}
|
|
90291
90291
|
get names() {
|
|
@@ -90308,10 +90308,10 @@ var require_codegen = __commonJS({
|
|
|
90308
90308
|
render(opts) {
|
|
90309
90309
|
return `for(${this.iteration})` + super.render(opts);
|
|
90310
90310
|
}
|
|
90311
|
-
optimizeNames(names,
|
|
90312
|
-
if (!super.optimizeNames(names,
|
|
90311
|
+
optimizeNames(names, constants5) {
|
|
90312
|
+
if (!super.optimizeNames(names, constants5))
|
|
90313
90313
|
return;
|
|
90314
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
90314
|
+
this.iteration = optimizeExpr(this.iteration, names, constants5);
|
|
90315
90315
|
return this;
|
|
90316
90316
|
}
|
|
90317
90317
|
get names() {
|
|
@@ -90347,10 +90347,10 @@ var require_codegen = __commonJS({
|
|
|
90347
90347
|
render(opts) {
|
|
90348
90348
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
90349
90349
|
}
|
|
90350
|
-
optimizeNames(names,
|
|
90351
|
-
if (!super.optimizeNames(names,
|
|
90350
|
+
optimizeNames(names, constants5) {
|
|
90351
|
+
if (!super.optimizeNames(names, constants5))
|
|
90352
90352
|
return;
|
|
90353
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
90353
|
+
this.iterable = optimizeExpr(this.iterable, names, constants5);
|
|
90354
90354
|
return this;
|
|
90355
90355
|
}
|
|
90356
90356
|
get names() {
|
|
@@ -90392,11 +90392,11 @@ var require_codegen = __commonJS({
|
|
|
90392
90392
|
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
|
|
90393
90393
|
return this;
|
|
90394
90394
|
}
|
|
90395
|
-
optimizeNames(names,
|
|
90395
|
+
optimizeNames(names, constants5) {
|
|
90396
90396
|
var _a, _b;
|
|
90397
|
-
super.optimizeNames(names,
|
|
90398
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
90399
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
90397
|
+
super.optimizeNames(names, constants5);
|
|
90398
|
+
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants5);
|
|
90399
|
+
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants5);
|
|
90400
90400
|
return this;
|
|
90401
90401
|
}
|
|
90402
90402
|
get names() {
|
|
@@ -90697,7 +90697,7 @@ var require_codegen = __commonJS({
|
|
|
90697
90697
|
function addExprNames(names, from14) {
|
|
90698
90698
|
return from14 instanceof code_1._CodeOrName ? addNames(names, from14.names) : names;
|
|
90699
90699
|
}
|
|
90700
|
-
function optimizeExpr(expr, names,
|
|
90700
|
+
function optimizeExpr(expr, names, constants5) {
|
|
90701
90701
|
if (expr instanceof code_1.Name)
|
|
90702
90702
|
return replaceName(expr);
|
|
90703
90703
|
if (!canOptimize(expr))
|
|
@@ -90712,14 +90712,14 @@ var require_codegen = __commonJS({
|
|
|
90712
90712
|
return items;
|
|
90713
90713
|
}, []));
|
|
90714
90714
|
function replaceName(n) {
|
|
90715
|
-
const c =
|
|
90715
|
+
const c = constants5[n.str];
|
|
90716
90716
|
if (c === void 0 || names[n.str] !== 1)
|
|
90717
90717
|
return n;
|
|
90718
90718
|
delete names[n.str];
|
|
90719
90719
|
return c;
|
|
90720
90720
|
}
|
|
90721
90721
|
function canOptimize(e) {
|
|
90722
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
90722
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants5[c.str] !== void 0);
|
|
90723
90723
|
}
|
|
90724
90724
|
}
|
|
90725
90725
|
function subtractNames(names, from14) {
|
|
@@ -92699,7 +92699,7 @@ var require_compile = __commonJS({
|
|
|
92699
92699
|
const schOrFunc = root2.refs[ref];
|
|
92700
92700
|
if (schOrFunc)
|
|
92701
92701
|
return schOrFunc;
|
|
92702
|
-
let _sch =
|
|
92702
|
+
let _sch = resolve3.call(this, root2, ref);
|
|
92703
92703
|
if (_sch === void 0) {
|
|
92704
92704
|
const schema = (_a = root2.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
92705
92705
|
const { schemaId } = this.opts;
|
|
@@ -92726,7 +92726,7 @@ var require_compile = __commonJS({
|
|
|
92726
92726
|
function sameSchemaEnv(s1, s2) {
|
|
92727
92727
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
92728
92728
|
}
|
|
92729
|
-
function
|
|
92729
|
+
function resolve3(root2, ref) {
|
|
92730
92730
|
let sch;
|
|
92731
92731
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
92732
92732
|
ref = sch;
|
|
@@ -93356,21 +93356,21 @@ var require_fast_uri = __commonJS({
|
|
|
93356
93356
|
normalizeString(uri, options);
|
|
93357
93357
|
} else if (typeof uri === "object") {
|
|
93358
93358
|
uri = /** @type {T} */
|
|
93359
|
-
|
|
93359
|
+
parse5(serialize(uri, options), options);
|
|
93360
93360
|
}
|
|
93361
93361
|
return uri;
|
|
93362
93362
|
}
|
|
93363
|
-
function
|
|
93363
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
93364
93364
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
93365
|
-
const resolved = resolveComponent(
|
|
93365
|
+
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
93366
93366
|
schemelessOptions.skipEscape = true;
|
|
93367
93367
|
return serialize(resolved, schemelessOptions);
|
|
93368
93368
|
}
|
|
93369
93369
|
function resolveComponent(base2, relative, options, skipNormalization) {
|
|
93370
93370
|
const target = {};
|
|
93371
93371
|
if (!skipNormalization) {
|
|
93372
|
-
base2 =
|
|
93373
|
-
relative =
|
|
93372
|
+
base2 = parse5(serialize(base2, options), options);
|
|
93373
|
+
relative = parse5(serialize(relative, options), options);
|
|
93374
93374
|
}
|
|
93375
93375
|
options = options || {};
|
|
93376
93376
|
if (!options.tolerant && relative.scheme) {
|
|
@@ -93593,7 +93593,7 @@ var require_fast_uri = __commonJS({
|
|
|
93593
93593
|
}
|
|
93594
93594
|
return { parsed, malformedAuthorityOrPort };
|
|
93595
93595
|
}
|
|
93596
|
-
function
|
|
93596
|
+
function parse5(uri, opts) {
|
|
93597
93597
|
return parseWithStatus(uri, opts).parsed;
|
|
93598
93598
|
}
|
|
93599
93599
|
function normalizeString(uri, opts) {
|
|
@@ -93618,11 +93618,11 @@ var require_fast_uri = __commonJS({
|
|
|
93618
93618
|
var fastUri = {
|
|
93619
93619
|
SCHEMES,
|
|
93620
93620
|
normalize,
|
|
93621
|
-
resolve,
|
|
93621
|
+
resolve: resolve3,
|
|
93622
93622
|
resolveComponent,
|
|
93623
93623
|
equal,
|
|
93624
93624
|
serialize,
|
|
93625
|
-
parse:
|
|
93625
|
+
parse: parse5
|
|
93626
93626
|
};
|
|
93627
93627
|
module.exports = fastUri;
|
|
93628
93628
|
module.exports.default = fastUri;
|
|
@@ -97837,8 +97837,8 @@ function paymentMiddleware(routes, server2, paywallConfig, paywall, syncFacilita
|
|
|
97837
97837
|
let bufferedCalls = [];
|
|
97838
97838
|
let settled = false;
|
|
97839
97839
|
let endCalled;
|
|
97840
|
-
const endPromise = new Promise((
|
|
97841
|
-
endCalled =
|
|
97840
|
+
const endPromise = new Promise((resolve3) => {
|
|
97841
|
+
endCalled = resolve3;
|
|
97842
97842
|
});
|
|
97843
97843
|
res.writeHead = function(...args) {
|
|
97844
97844
|
if (!settled) {
|
|
@@ -99974,7 +99974,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
99974
99974
|
init_esm_shims();
|
|
99975
99975
|
var fs = __require("fs");
|
|
99976
99976
|
var Url = __require("url");
|
|
99977
|
-
var
|
|
99977
|
+
var spawn3 = __require("child_process").spawn;
|
|
99978
99978
|
module.exports = XMLHttpRequest3;
|
|
99979
99979
|
XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
|
|
99980
99980
|
function XMLHttpRequest3(opts) {
|
|
@@ -100270,7 +100270,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
100270
100270
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
100271
100271
|
fs.writeFileSync(syncFile, "", "utf8");
|
|
100272
100272
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
100273
|
-
var syncProc =
|
|
100273
|
+
var syncProc = spawn3(process.argv[0], ["-e", execString]);
|
|
100274
100274
|
var statusText;
|
|
100275
100275
|
while (fs.existsSync(syncFile)) {
|
|
100276
100276
|
}
|
|
@@ -100907,7 +100907,7 @@ var require_ms = __commonJS({
|
|
|
100907
100907
|
options = options || {};
|
|
100908
100908
|
var type = typeof val;
|
|
100909
100909
|
if (type === "string" && val.length > 0) {
|
|
100910
|
-
return
|
|
100910
|
+
return parse5(val);
|
|
100911
100911
|
} else if (type === "number" && isFinite(val)) {
|
|
100912
100912
|
return options.long ? fmtLong(val) : fmtShort(val);
|
|
100913
100913
|
}
|
|
@@ -100915,7 +100915,7 @@ var require_ms = __commonJS({
|
|
|
100915
100915
|
"val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
|
|
100916
100916
|
);
|
|
100917
100917
|
};
|
|
100918
|
-
function
|
|
100918
|
+
function parse5(str2) {
|
|
100919
100919
|
str2 = String(str2);
|
|
100920
100920
|
if (str2.length > 100) {
|
|
100921
100921
|
return;
|
|
@@ -104457,9 +104457,9 @@ var init_socket3 = __esm({
|
|
|
104457
104457
|
* @return a Promise that will be fulfilled when the server acknowledges the event
|
|
104458
104458
|
*/
|
|
104459
104459
|
emitWithAck(ev, ...args) {
|
|
104460
|
-
return new Promise((
|
|
104460
|
+
return new Promise((resolve3, reject) => {
|
|
104461
104461
|
const fn = (arg1, arg2) => {
|
|
104462
|
-
return arg1 ? reject(arg1) :
|
|
104462
|
+
return arg1 ? reject(arg1) : resolve3(arg2);
|
|
104463
104463
|
};
|
|
104464
104464
|
fn.withError = true;
|
|
104465
104465
|
args.push(fn);
|
|
@@ -105532,7 +105532,7 @@ var init_eventSubscriber = __esm({
|
|
|
105532
105532
|
this.apiKey = apiKey;
|
|
105533
105533
|
}
|
|
105534
105534
|
async connect() {
|
|
105535
|
-
return new Promise((
|
|
105535
|
+
return new Promise((resolve3, reject) => {
|
|
105536
105536
|
this.socket = lookup(`${this.apiUrl}/events`, {
|
|
105537
105537
|
auth: { apiKey: this.apiKey },
|
|
105538
105538
|
transports: ["websocket"],
|
|
@@ -105541,7 +105541,7 @@ var init_eventSubscriber = __esm({
|
|
|
105541
105541
|
this.socket.on("connect", () => {
|
|
105542
105542
|
this.isConnected = true;
|
|
105543
105543
|
console.error("\u2705 Connected to Ametyst Events WebSocket");
|
|
105544
|
-
|
|
105544
|
+
resolve3();
|
|
105545
105545
|
});
|
|
105546
105546
|
this.socket.on("connectionStatus", (status) => {
|
|
105547
105547
|
console.error("\u{1F4E1} Blockchain connection status:", status);
|
|
@@ -106359,33 +106359,82 @@ var init_dist = __esm({
|
|
|
106359
106359
|
});
|
|
106360
106360
|
|
|
106361
106361
|
// src/config/paths.ts
|
|
106362
|
-
import { existsSync, mkdirSync } from "fs";
|
|
106362
|
+
import { accessSync, constants, existsSync, mkdirSync } from "fs";
|
|
106363
106363
|
import { homedir } from "os";
|
|
106364
|
-
import { join } from "path";
|
|
106364
|
+
import { basename, join, parse as parse3, resolve } from "path";
|
|
106365
106365
|
function ensureDirectories() {
|
|
106366
106366
|
if (!existsSync(AMETYST_DIR)) mkdirSync(AMETYST_DIR, { mode: 448 });
|
|
106367
106367
|
if (!existsSync(WALLETS_DIR)) mkdirSync(WALLETS_DIR, { mode: 448 });
|
|
106368
106368
|
}
|
|
106369
|
-
function
|
|
106370
|
-
|
|
106369
|
+
function isFilesystemRoot(dir) {
|
|
106370
|
+
const abs = resolve(dir);
|
|
106371
|
+
return parse3(abs).root === abs;
|
|
106371
106372
|
}
|
|
106372
|
-
function
|
|
106373
|
+
function whyUnusable(dir, deps) {
|
|
106374
|
+
if (isFilesystemRoot(dir)) return "it is the filesystem root, not a project folder";
|
|
106375
|
+
if (!deps.existsSync(dir)) return "it does not exist";
|
|
106376
|
+
try {
|
|
106377
|
+
deps.accessSync(dir, constants.W_OK);
|
|
106378
|
+
} catch (err) {
|
|
106379
|
+
const code = err?.code;
|
|
106380
|
+
return `it is not writable${code ? ` (${code})` : ""}`;
|
|
106381
|
+
}
|
|
106382
|
+
return void 0;
|
|
106383
|
+
}
|
|
106384
|
+
function resolveRunRoot(explicitDir, deps = {}) {
|
|
106385
|
+
const probes = { existsSync: deps.existsSync ?? existsSync, accessSync: deps.accessSync ?? accessSync };
|
|
106386
|
+
const rejected = [];
|
|
106387
|
+
const explicit = typeof explicitDir === "string" ? explicitDir.trim() : "";
|
|
106388
|
+
if (explicit) {
|
|
106389
|
+
const abs = resolve(explicit);
|
|
106390
|
+
const why = whyUnusable(abs, probes);
|
|
106391
|
+
if (!why) return { root: abs, reason: "explicit" };
|
|
106392
|
+
rejected.push({ dir: abs, why });
|
|
106393
|
+
}
|
|
106394
|
+
let cwd;
|
|
106395
|
+
try {
|
|
106396
|
+
cwd = (deps.cwd ?? (() => process.cwd()))();
|
|
106397
|
+
} catch (err) {
|
|
106398
|
+
rejected.push({ dir: "<cwd>", why: `it cannot be read (${err instanceof Error ? err.message : String(err)})` });
|
|
106399
|
+
}
|
|
106400
|
+
if (cwd !== void 0) {
|
|
106401
|
+
const why = whyUnusable(cwd, probes);
|
|
106402
|
+
if (!why) return { root: cwd, reason: "cwd", ...rejected.length ? { rejected } : {} };
|
|
106403
|
+
rejected.push({ dir: cwd, why });
|
|
106404
|
+
}
|
|
106405
|
+
const home = deps.homedir ? deps.homedir() : void 0;
|
|
106406
|
+
const fallback2 = home === void 0 ? join(AMETYST_DIR, "runs") : join(home, `.ametyst${ENV_SUFFIX}`, "runs");
|
|
106407
|
+
(deps.mkdirSync ?? mkdirSync)(fallback2, { recursive: true, mode: 448 });
|
|
106408
|
+
return { root: fallback2, reason: "fallback", rejected };
|
|
106409
|
+
}
|
|
106410
|
+
function tasksRoot(runRoot) {
|
|
106411
|
+
return join(runRoot ?? resolveRunRoot().root, `.ametyst${ENV_SUFFIX}`, RUNS_SEGMENT);
|
|
106412
|
+
}
|
|
106413
|
+
function runDir(slug, runRoot) {
|
|
106373
106414
|
const safe = String(slug).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
|
|
106374
|
-
if (!safe) throw new Error(`invalid
|
|
106375
|
-
return join(
|
|
106415
|
+
if (!safe) throw new Error(`invalid task slug: ${JSON.stringify(slug)}`);
|
|
106416
|
+
return join(tasksRoot(runRoot), safe);
|
|
106376
106417
|
}
|
|
106377
|
-
function
|
|
106378
|
-
|
|
106418
|
+
function legacyRunFolderHint(slug, runRoot, deps = {}) {
|
|
106419
|
+
const exists = deps.existsSync ?? existsSync;
|
|
106420
|
+
const next = runDir(slug, runRoot);
|
|
106421
|
+
const ametystDir = join(runRoot, `.ametyst${ENV_SUFFIX}`);
|
|
106422
|
+
const legacy = join(ametystDir, LEGACY_RUNS_SEGMENT, basename(next));
|
|
106423
|
+
if (exists(next) || !exists(legacy)) return void 0;
|
|
106424
|
+
return `Task ${slug}: found a run folder from an older cli at ${legacy} and nothing yet at ${next} \u2014 move it (\`mv ${join(ametystDir, LEGACY_RUNS_SEGMENT)} ${join(ametystDir, RUNS_SEGMENT)}\`, or \`mv ${legacy} ${next}\` when ${join(ametystDir, RUNS_SEGMENT)} already exists) to keep its .state/ ledger and logs; continuing with ${next}.`;
|
|
106379
106425
|
}
|
|
106380
|
-
function
|
|
106426
|
+
function runFiresRoot(slug, runRoot) {
|
|
106427
|
+
return join(runDir(slug, runRoot), "fires");
|
|
106428
|
+
}
|
|
106429
|
+
function runFireDir(slug, fireId, runRoot) {
|
|
106381
106430
|
const safe = String(fireId).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
|
|
106382
106431
|
if (!safe) throw new Error(`invalid fire id: ${JSON.stringify(fireId)}`);
|
|
106383
|
-
return join(
|
|
106432
|
+
return join(runFiresRoot(slug, runRoot), safe);
|
|
106384
106433
|
}
|
|
106385
|
-
function refusedConstraintsPath(slug, fireId) {
|
|
106434
|
+
function refusedConstraintsPath(slug, fireId, runRoot) {
|
|
106386
106435
|
const safe = String(fireId).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
|
|
106387
106436
|
if (!safe) throw new Error(`invalid fire id: ${JSON.stringify(fireId)}`);
|
|
106388
|
-
return join(
|
|
106437
|
+
return join(runDir(slug, runRoot), ".state", `constraints-refused-${safe}.md`);
|
|
106389
106438
|
}
|
|
106390
106439
|
function skillsRoot(target = "claude", global2 = false) {
|
|
106391
106440
|
return join(global2 ? homedir() : process.cwd(), SKILLS_DIR_BY_TARGET[target], "skills");
|
|
@@ -106395,7 +106444,7 @@ function skillDir(slug, target = "claude", global2 = false) {
|
|
|
106395
106444
|
if (!safe) throw new Error(`invalid skill slug: ${JSON.stringify(slug)}`);
|
|
106396
106445
|
return join(skillsRoot(target, global2), safe);
|
|
106397
106446
|
}
|
|
106398
|
-
var ENV_SUFFIX, AMETYST_DIR, CONFIG_PATH, CREDENTIALS_ENC_PATH, WALLETS_DIR, VAULT_PATH, SKILLS_DIR_BY_TARGET;
|
|
106447
|
+
var ENV_SUFFIX, AMETYST_DIR, CONFIG_PATH, CREDENTIALS_ENC_PATH, WALLETS_DIR, VAULT_PATH, RUNS_SEGMENT, LEGACY_RUNS_SEGMENT, SKILLS_DIR_BY_TARGET;
|
|
106399
106448
|
var init_paths = __esm({
|
|
106400
106449
|
"src/config/paths.ts"() {
|
|
106401
106450
|
"use strict";
|
|
@@ -106406,6 +106455,8 @@ var init_paths = __esm({
|
|
|
106406
106455
|
CREDENTIALS_ENC_PATH = join(AMETYST_DIR, "credentials.enc");
|
|
106407
106456
|
WALLETS_DIR = join(AMETYST_DIR, "wallets");
|
|
106408
106457
|
VAULT_PATH = join(AMETYST_DIR, "wallet.json");
|
|
106458
|
+
RUNS_SEGMENT = "tasks";
|
|
106459
|
+
LEGACY_RUNS_SEGMENT = "loops";
|
|
106409
106460
|
SKILLS_DIR_BY_TARGET = {
|
|
106410
106461
|
claude: ".claude",
|
|
106411
106462
|
codex: ".codex"
|
|
@@ -107040,7 +107091,7 @@ var require_text = __commonJS({
|
|
|
107040
107091
|
"node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/dist/elements/text.js"(exports, module) {
|
|
107041
107092
|
"use strict";
|
|
107042
107093
|
init_esm_shims();
|
|
107043
|
-
function asyncGeneratorStep(gen2,
|
|
107094
|
+
function asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, key, arg) {
|
|
107044
107095
|
try {
|
|
107045
107096
|
var info = gen2[key](arg);
|
|
107046
107097
|
var value2 = info.value;
|
|
@@ -107049,7 +107100,7 @@ var require_text = __commonJS({
|
|
|
107049
107100
|
return;
|
|
107050
107101
|
}
|
|
107051
107102
|
if (info.done) {
|
|
107052
|
-
|
|
107103
|
+
resolve3(value2);
|
|
107053
107104
|
} else {
|
|
107054
107105
|
Promise.resolve(value2).then(_next, _throw);
|
|
107055
107106
|
}
|
|
@@ -107057,13 +107108,13 @@ var require_text = __commonJS({
|
|
|
107057
107108
|
function _asyncToGenerator(fn) {
|
|
107058
107109
|
return function() {
|
|
107059
107110
|
var self2 = this, args = arguments;
|
|
107060
|
-
return new Promise(function(
|
|
107111
|
+
return new Promise(function(resolve3, reject) {
|
|
107061
107112
|
var gen2 = fn.apply(self2, args);
|
|
107062
107113
|
function _next(value2) {
|
|
107063
|
-
asyncGeneratorStep(gen2,
|
|
107114
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "next", value2);
|
|
107064
107115
|
}
|
|
107065
107116
|
function _throw(err) {
|
|
107066
|
-
asyncGeneratorStep(gen2,
|
|
107117
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "throw", err);
|
|
107067
107118
|
}
|
|
107068
107119
|
_next(void 0);
|
|
107069
107120
|
});
|
|
@@ -107792,7 +107843,7 @@ var require_date = __commonJS({
|
|
|
107792
107843
|
"node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/dist/elements/date.js"(exports, module) {
|
|
107793
107844
|
"use strict";
|
|
107794
107845
|
init_esm_shims();
|
|
107795
|
-
function asyncGeneratorStep(gen2,
|
|
107846
|
+
function asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, key, arg) {
|
|
107796
107847
|
try {
|
|
107797
107848
|
var info = gen2[key](arg);
|
|
107798
107849
|
var value2 = info.value;
|
|
@@ -107801,7 +107852,7 @@ var require_date = __commonJS({
|
|
|
107801
107852
|
return;
|
|
107802
107853
|
}
|
|
107803
107854
|
if (info.done) {
|
|
107804
|
-
|
|
107855
|
+
resolve3(value2);
|
|
107805
107856
|
} else {
|
|
107806
107857
|
Promise.resolve(value2).then(_next, _throw);
|
|
107807
107858
|
}
|
|
@@ -107809,13 +107860,13 @@ var require_date = __commonJS({
|
|
|
107809
107860
|
function _asyncToGenerator(fn) {
|
|
107810
107861
|
return function() {
|
|
107811
107862
|
var self2 = this, args = arguments;
|
|
107812
|
-
return new Promise(function(
|
|
107863
|
+
return new Promise(function(resolve3, reject) {
|
|
107813
107864
|
var gen2 = fn.apply(self2, args);
|
|
107814
107865
|
function _next(value2) {
|
|
107815
|
-
asyncGeneratorStep(gen2,
|
|
107866
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "next", value2);
|
|
107816
107867
|
}
|
|
107817
107868
|
function _throw(err) {
|
|
107818
|
-
asyncGeneratorStep(gen2,
|
|
107869
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "throw", err);
|
|
107819
107870
|
}
|
|
107820
107871
|
_next(void 0);
|
|
107821
107872
|
});
|
|
@@ -108019,7 +108070,7 @@ var require_number = __commonJS({
|
|
|
108019
108070
|
"node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/dist/elements/number.js"(exports, module) {
|
|
108020
108071
|
"use strict";
|
|
108021
108072
|
init_esm_shims();
|
|
108022
|
-
function asyncGeneratorStep(gen2,
|
|
108073
|
+
function asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, key, arg) {
|
|
108023
108074
|
try {
|
|
108024
108075
|
var info = gen2[key](arg);
|
|
108025
108076
|
var value2 = info.value;
|
|
@@ -108028,7 +108079,7 @@ var require_number = __commonJS({
|
|
|
108028
108079
|
return;
|
|
108029
108080
|
}
|
|
108030
108081
|
if (info.done) {
|
|
108031
|
-
|
|
108082
|
+
resolve3(value2);
|
|
108032
108083
|
} else {
|
|
108033
108084
|
Promise.resolve(value2).then(_next, _throw);
|
|
108034
108085
|
}
|
|
@@ -108036,13 +108087,13 @@ var require_number = __commonJS({
|
|
|
108036
108087
|
function _asyncToGenerator(fn) {
|
|
108037
108088
|
return function() {
|
|
108038
108089
|
var self2 = this, args = arguments;
|
|
108039
|
-
return new Promise(function(
|
|
108090
|
+
return new Promise(function(resolve3, reject) {
|
|
108040
108091
|
var gen2 = fn.apply(self2, args);
|
|
108041
108092
|
function _next(value2) {
|
|
108042
|
-
asyncGeneratorStep(gen2,
|
|
108093
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "next", value2);
|
|
108043
108094
|
}
|
|
108044
108095
|
function _throw(err) {
|
|
108045
|
-
asyncGeneratorStep(gen2,
|
|
108096
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "throw", err);
|
|
108046
108097
|
}
|
|
108047
108098
|
_next(void 0);
|
|
108048
108099
|
});
|
|
@@ -108467,7 +108518,7 @@ var require_autocomplete = __commonJS({
|
|
|
108467
108518
|
"node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/dist/elements/autocomplete.js"(exports, module) {
|
|
108468
108519
|
"use strict";
|
|
108469
108520
|
init_esm_shims();
|
|
108470
|
-
function asyncGeneratorStep(gen2,
|
|
108521
|
+
function asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, key, arg) {
|
|
108471
108522
|
try {
|
|
108472
108523
|
var info = gen2[key](arg);
|
|
108473
108524
|
var value2 = info.value;
|
|
@@ -108476,7 +108527,7 @@ var require_autocomplete = __commonJS({
|
|
|
108476
108527
|
return;
|
|
108477
108528
|
}
|
|
108478
108529
|
if (info.done) {
|
|
108479
|
-
|
|
108530
|
+
resolve3(value2);
|
|
108480
108531
|
} else {
|
|
108481
108532
|
Promise.resolve(value2).then(_next, _throw);
|
|
108482
108533
|
}
|
|
@@ -108484,13 +108535,13 @@ var require_autocomplete = __commonJS({
|
|
|
108484
108535
|
function _asyncToGenerator(fn) {
|
|
108485
108536
|
return function() {
|
|
108486
108537
|
var self2 = this, args = arguments;
|
|
108487
|
-
return new Promise(function(
|
|
108538
|
+
return new Promise(function(resolve3, reject) {
|
|
108488
108539
|
var gen2 = fn.apply(self2, args);
|
|
108489
108540
|
function _next(value2) {
|
|
108490
|
-
asyncGeneratorStep(gen2,
|
|
108541
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "next", value2);
|
|
108491
108542
|
}
|
|
108492
108543
|
function _throw(err) {
|
|
108493
|
-
asyncGeneratorStep(gen2,
|
|
108544
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "throw", err);
|
|
108494
108545
|
}
|
|
108495
108546
|
_next(void 0);
|
|
108496
108547
|
});
|
|
@@ -109126,7 +109177,7 @@ var require_dist = __commonJS({
|
|
|
109126
109177
|
for (var i = 0, arr22 = new Array(len); i < len; i++) arr22[i] = arr2[i];
|
|
109127
109178
|
return arr22;
|
|
109128
109179
|
}
|
|
109129
|
-
function asyncGeneratorStep(gen2,
|
|
109180
|
+
function asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, key, arg) {
|
|
109130
109181
|
try {
|
|
109131
109182
|
var info = gen2[key](arg);
|
|
109132
109183
|
var value2 = info.value;
|
|
@@ -109135,7 +109186,7 @@ var require_dist = __commonJS({
|
|
|
109135
109186
|
return;
|
|
109136
109187
|
}
|
|
109137
109188
|
if (info.done) {
|
|
109138
|
-
|
|
109189
|
+
resolve3(value2);
|
|
109139
109190
|
} else {
|
|
109140
109191
|
Promise.resolve(value2).then(_next, _throw);
|
|
109141
109192
|
}
|
|
@@ -109143,13 +109194,13 @@ var require_dist = __commonJS({
|
|
|
109143
109194
|
function _asyncToGenerator(fn) {
|
|
109144
109195
|
return function() {
|
|
109145
109196
|
var self2 = this, args = arguments;
|
|
109146
|
-
return new Promise(function(
|
|
109197
|
+
return new Promise(function(resolve3, reject) {
|
|
109147
109198
|
var gen2 = fn.apply(self2, args);
|
|
109148
109199
|
function _next(value2) {
|
|
109149
|
-
asyncGeneratorStep(gen2,
|
|
109200
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "next", value2);
|
|
109150
109201
|
}
|
|
109151
109202
|
function _throw(err) {
|
|
109152
|
-
asyncGeneratorStep(gen2,
|
|
109203
|
+
asyncGeneratorStep(gen2, resolve3, reject, _next, _throw, "throw", err);
|
|
109153
109204
|
}
|
|
109154
109205
|
_next(void 0);
|
|
109155
109206
|
});
|
|
@@ -111804,7 +111855,7 @@ import {
|
|
|
111804
111855
|
writeSync
|
|
111805
111856
|
} from "fs";
|
|
111806
111857
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
111807
|
-
import { basename as
|
|
111858
|
+
import { basename as basename4, dirname as dirname5, join as join5 } from "path";
|
|
111808
111859
|
function createWallet(passphrase) {
|
|
111809
111860
|
return native.createWallet(passphrase);
|
|
111810
111861
|
}
|
|
@@ -111846,7 +111897,7 @@ function listWallets() {
|
|
|
111846
111897
|
return readdirSync3(WALLETS_DIR).filter((file) => file.endsWith(".json")).map((file) => join5(WALLETS_DIR, file));
|
|
111847
111898
|
}
|
|
111848
111899
|
function deleteWallet(walletPath) {
|
|
111849
|
-
const name =
|
|
111900
|
+
const name = basename4(walletPath);
|
|
111850
111901
|
if (!name.endsWith(".json")) {
|
|
111851
111902
|
throw new Error("Refusing to delete non-wallet file");
|
|
111852
111903
|
}
|
|
@@ -111883,7 +111934,7 @@ function writeVaultFileAtomic(contents) {
|
|
|
111883
111934
|
}
|
|
111884
111935
|
function writeSecretFileAtomic(path2, contents) {
|
|
111885
111936
|
const dir = dirname5(path2);
|
|
111886
|
-
const tmp = join5(dir, `.${
|
|
111937
|
+
const tmp = join5(dir, `.${basename4(path2)}.tmp-${process.pid}-${randomBytes3(8).toString("hex")}`);
|
|
111887
111938
|
let fd;
|
|
111888
111939
|
try {
|
|
111889
111940
|
fd = openSync(tmp, "wx", 384);
|
|
@@ -112436,7 +112487,7 @@ var init_version5 = __esm({
|
|
|
112436
112487
|
"src/version.ts"() {
|
|
112437
112488
|
"use strict";
|
|
112438
112489
|
init_esm_shims();
|
|
112439
|
-
CLI_VERSION = true ? "0.3.
|
|
112490
|
+
CLI_VERSION = true ? "0.3.11" : "0.0.0-dev";
|
|
112440
112491
|
}
|
|
112441
112492
|
});
|
|
112442
112493
|
|
|
@@ -113777,8 +113828,8 @@ function sqliteWasmDriver(db, raw) {
|
|
|
113777
113828
|
init: () => Promise.resolve(),
|
|
113778
113829
|
acquireConnection: async () => {
|
|
113779
113830
|
while (inUse) await inUse;
|
|
113780
|
-
inUse = new Promise((
|
|
113781
|
-
release =
|
|
113831
|
+
inUse = new Promise((resolve3) => {
|
|
113832
|
+
release = resolve3;
|
|
113782
113833
|
});
|
|
113783
113834
|
return connection;
|
|
113784
113835
|
},
|
|
@@ -113792,10 +113843,10 @@ function sqliteWasmDriver(db, raw) {
|
|
|
113792
113843
|
await conn.executeQuery(raw("rollback"));
|
|
113793
113844
|
},
|
|
113794
113845
|
releaseConnection: () => {
|
|
113795
|
-
const
|
|
113846
|
+
const resolve3 = release;
|
|
113796
113847
|
inUse = void 0;
|
|
113797
113848
|
release = void 0;
|
|
113798
|
-
|
|
113849
|
+
resolve3?.();
|
|
113799
113850
|
return Promise.resolve();
|
|
113800
113851
|
},
|
|
113801
113852
|
destroy: () => {
|
|
@@ -113910,8 +113961,8 @@ var init_engine_store = __esm({
|
|
|
113910
113961
|
SCHEMA_VERSION = "1.0.0";
|
|
113911
113962
|
SCHEMA_NAMESPACE = "ametyst_connections";
|
|
113912
113963
|
IN_MEMORY_DB = ":memory:";
|
|
113913
|
-
sleep = (ms) => new Promise((
|
|
113914
|
-
setTimeout(
|
|
113964
|
+
sleep = (ms) => new Promise((resolve3) => {
|
|
113965
|
+
setTimeout(resolve3, ms);
|
|
113915
113966
|
});
|
|
113916
113967
|
monotonicNowMs = () => performance.now();
|
|
113917
113968
|
CAUSE_CHAIN_MAX_DEPTH = 8;
|
|
@@ -113920,7 +113971,7 @@ var init_engine_store = __esm({
|
|
|
113920
113971
|
BUSY_PRIMARY_ERRNO = /* @__PURE__ */ new Set([5, 6]);
|
|
113921
113972
|
isBusyErrno = (errno) => Number.isInteger(errno) && BUSY_PRIMARY_ERRNO.has(errno & 255);
|
|
113922
113973
|
CORRUPT_STORE_ADVICE = `It holds no credentials \u2014 every secret is in your OS keychain \u2014 so deleting that file loses nothing but the engine's own bookkeeping, and it is rebuilt on the next 'ametyst connections' command.`;
|
|
113923
|
-
lockedStoreAdvice = (budgetMs) => `Another process is holding it \u2014 a second 'ametyst' command, or a
|
|
113974
|
+
lockedStoreAdvice = (budgetMs) => `Another process is holding it \u2014 a second 'ametyst' command, or a scheduled task firing alongside this one. Nothing is damaged and nothing was lost: SQLite is doing its job and serializing the two. This was already retried for up to ${budgetMs}ms and the lock did not clear, so the other process is still working; run the command again once it finishes. Do NOT delete the database to clear this \u2014 that would throw away working state to fix a lock that goes away by itself.`;
|
|
113924
113975
|
ConnectionsStoreUnavailableError = class extends Error {
|
|
113925
113976
|
constructor(path2, cause, advice = CORRUPT_STORE_ADVICE) {
|
|
113926
113977
|
super(
|
|
@@ -114202,8 +114253,8 @@ import { createServer as createServer4 } from "http";
|
|
|
114202
114253
|
async function startOAuthCallbackListener(options = {}) {
|
|
114203
114254
|
let settle = null;
|
|
114204
114255
|
let fail2 = null;
|
|
114205
|
-
const received = new Promise((
|
|
114206
|
-
settle =
|
|
114256
|
+
const received = new Promise((resolve3, reject) => {
|
|
114257
|
+
settle = resolve3;
|
|
114207
114258
|
fail2 = reject;
|
|
114208
114259
|
});
|
|
114209
114260
|
const server2 = createServer4((req, res) => {
|
|
@@ -114224,12 +114275,12 @@ async function startOAuthCallbackListener(options = {}) {
|
|
|
114224
114275
|
else if (!code) fail2?.(new Error("The provider's callback carried no authorization code."));
|
|
114225
114276
|
else settle?.({ code, state });
|
|
114226
114277
|
});
|
|
114227
|
-
await new Promise((
|
|
114278
|
+
await new Promise((resolve3) => server2.listen(0, "127.0.0.1", resolve3));
|
|
114228
114279
|
const address = server2.address();
|
|
114229
114280
|
const port = typeof address === "object" && address ? address.port : 0;
|
|
114230
|
-
const close = () => new Promise((
|
|
114281
|
+
const close = () => new Promise((resolve3) => {
|
|
114231
114282
|
server2.closeAllConnections?.();
|
|
114232
|
-
server2.close(() =>
|
|
114283
|
+
server2.close(() => resolve3());
|
|
114233
114284
|
});
|
|
114234
114285
|
return {
|
|
114235
114286
|
redirectUri: `http://127.0.0.1:${port}${OAUTH_CALLBACK_PATH2}`,
|
|
@@ -114317,7 +114368,7 @@ init_credentials();
|
|
|
114317
114368
|
// src/config/host-registry.ts
|
|
114318
114369
|
init_esm_shims();
|
|
114319
114370
|
init_resolve();
|
|
114320
|
-
import { accessSync, constants, existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync5 } from "fs";
|
|
114371
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync5 } from "fs";
|
|
114321
114372
|
import { homedir as homedir4 } from "os";
|
|
114322
114373
|
import { delimiter, dirname as dirname4, isAbsolute, join as join4 } from "path";
|
|
114323
114374
|
|
|
@@ -114333,7 +114384,7 @@ import {
|
|
|
114333
114384
|
writeFileSync as writeFileSync3
|
|
114334
114385
|
} from "fs";
|
|
114335
114386
|
import { homedir as homedir2 } from "os";
|
|
114336
|
-
import { basename, dirname as dirname2, join as join2 } from "path";
|
|
114387
|
+
import { basename as basename2, dirname as dirname2, join as join2 } from "path";
|
|
114337
114388
|
var CLAUDE_CODE_CONFIG_PATH = join2(homedir2(), ".claude.json");
|
|
114338
114389
|
var AMETYST_MCP_COMMAND = process.argv[1] || "ametyst";
|
|
114339
114390
|
var AMETYST_MCP_NAME = false ? "ametyst-staging" : "ametyst";
|
|
@@ -114356,7 +114407,7 @@ function backupIfExists() {
|
|
|
114356
114407
|
const backupPath = `${CLAUDE_CODE_CONFIG_PATH}.backup-${ts}`;
|
|
114357
114408
|
copyFileSync(CLAUDE_CODE_CONFIG_PATH, backupPath);
|
|
114358
114409
|
const dir = dirname2(CLAUDE_CODE_CONFIG_PATH);
|
|
114359
|
-
const base2 =
|
|
114410
|
+
const base2 = basename2(CLAUDE_CODE_CONFIG_PATH);
|
|
114360
114411
|
const backups = readdirSync(dir).filter((f) => f.startsWith(`${base2}.backup-`)).sort();
|
|
114361
114412
|
while (backups.length > MAX_BACKUPS) {
|
|
114362
114413
|
const oldest = backups.shift();
|
|
@@ -114451,7 +114502,7 @@ import {
|
|
|
114451
114502
|
writeFileSync as writeFileSync4
|
|
114452
114503
|
} from "fs";
|
|
114453
114504
|
import { homedir as homedir3 } from "os";
|
|
114454
|
-
import { basename as
|
|
114505
|
+
import { basename as basename3, dirname as dirname3, join as join3 } from "path";
|
|
114455
114506
|
var CODEX_CONFIG_PATH = join3(homedir3(), ".codex", "config.toml");
|
|
114456
114507
|
var MAX_BACKUPS2 = 3;
|
|
114457
114508
|
var TRAILING = "\\s*(#.*)?$";
|
|
@@ -114469,7 +114520,7 @@ function backupIfExists2() {
|
|
|
114469
114520
|
} catch {
|
|
114470
114521
|
}
|
|
114471
114522
|
const dir = dirname3(CODEX_CONFIG_PATH);
|
|
114472
|
-
const base2 =
|
|
114523
|
+
const base2 = basename3(CODEX_CONFIG_PATH);
|
|
114473
114524
|
const backups = readdirSync2(dir).filter((f) => f.startsWith(`${base2}.backup-`)).sort();
|
|
114474
114525
|
while (backups.length > MAX_BACKUPS2) {
|
|
114475
114526
|
const oldest = backups.shift();
|
|
@@ -114576,7 +114627,7 @@ function removeAmetystCodexEntry() {
|
|
|
114576
114627
|
}
|
|
114577
114628
|
function purgeKeyBearingBackups() {
|
|
114578
114629
|
const dir = dirname3(CODEX_CONFIG_PATH);
|
|
114579
|
-
const base2 =
|
|
114630
|
+
const base2 = basename3(CODEX_CONFIG_PATH);
|
|
114580
114631
|
let names;
|
|
114581
114632
|
try {
|
|
114582
114633
|
names = readdirSync2(dir).filter((f) => f.startsWith(`${base2}.backup-`));
|
|
@@ -114662,7 +114713,7 @@ function findBinary(name, env = process.env) {
|
|
|
114662
114713
|
const candidate = join4(dir, name);
|
|
114663
114714
|
try {
|
|
114664
114715
|
if (!statSync(candidate).isFile()) continue;
|
|
114665
|
-
|
|
114716
|
+
accessSync2(candidate, constants2.X_OK);
|
|
114666
114717
|
return candidate;
|
|
114667
114718
|
} catch {
|
|
114668
114719
|
}
|
|
@@ -115295,8 +115346,8 @@ async function readJson(url2, apiKey) {
|
|
|
115295
115346
|
function withSourceTimeout(work) {
|
|
115296
115347
|
return Promise.race([
|
|
115297
115348
|
work,
|
|
115298
|
-
new Promise((
|
|
115299
|
-
setTimeout(() =>
|
|
115349
|
+
new Promise((resolve3) => {
|
|
115350
|
+
setTimeout(() => resolve3(null), SOURCE_TIMEOUT_MS).unref();
|
|
115300
115351
|
})
|
|
115301
115352
|
]);
|
|
115302
115353
|
}
|
|
@@ -116176,7 +116227,7 @@ function esc(value2) {
|
|
|
116176
116227
|
return value2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
116177
116228
|
}
|
|
116178
116229
|
function readBody(req) {
|
|
116179
|
-
return new Promise((
|
|
116230
|
+
return new Promise((resolve3) => {
|
|
116180
116231
|
let data = "";
|
|
116181
116232
|
let over = false;
|
|
116182
116233
|
req.setEncoding("utf-8");
|
|
@@ -116186,14 +116237,14 @@ function readBody(req) {
|
|
|
116186
116237
|
if (data.length > MAX_BODY_BYTES) {
|
|
116187
116238
|
over = true;
|
|
116188
116239
|
data = "";
|
|
116189
|
-
|
|
116240
|
+
resolve3({ body: "", tooLarge: true });
|
|
116190
116241
|
}
|
|
116191
116242
|
});
|
|
116192
116243
|
req.on("end", () => {
|
|
116193
|
-
if (!over)
|
|
116244
|
+
if (!over) resolve3({ body: data, tooLarge: false });
|
|
116194
116245
|
});
|
|
116195
116246
|
req.on("error", () => {
|
|
116196
|
-
if (!over)
|
|
116247
|
+
if (!over) resolve3({ body: "", tooLarge: false });
|
|
116197
116248
|
});
|
|
116198
116249
|
});
|
|
116199
116250
|
}
|
|
@@ -116331,18 +116382,18 @@ async function startUnlockListener(deps) {
|
|
|
116331
116382
|
expiryTimer = void 0;
|
|
116332
116383
|
}
|
|
116333
116384
|
resolveDone(state);
|
|
116334
|
-
return new Promise((
|
|
116385
|
+
return new Promise((resolve3) => {
|
|
116335
116386
|
try {
|
|
116336
|
-
server2.close(() =>
|
|
116387
|
+
server2.close(() => resolve3());
|
|
116337
116388
|
setTimeout(() => {
|
|
116338
116389
|
try {
|
|
116339
116390
|
server2.closeAllConnections?.();
|
|
116340
116391
|
} catch {
|
|
116341
116392
|
}
|
|
116342
|
-
|
|
116393
|
+
resolve3();
|
|
116343
116394
|
}, 50).unref?.();
|
|
116344
116395
|
} catch {
|
|
116345
|
-
|
|
116396
|
+
resolve3();
|
|
116346
116397
|
}
|
|
116347
116398
|
});
|
|
116348
116399
|
};
|
|
@@ -116510,12 +116561,12 @@ async function startUnlockListener(deps) {
|
|
|
116510
116561
|
}
|
|
116511
116562
|
});
|
|
116512
116563
|
});
|
|
116513
|
-
const listened = await new Promise((
|
|
116564
|
+
const listened = await new Promise((resolve3) => {
|
|
116514
116565
|
let settled = false;
|
|
116515
116566
|
const settle = (value2) => {
|
|
116516
116567
|
if (settled) return;
|
|
116517
116568
|
settled = true;
|
|
116518
|
-
|
|
116569
|
+
resolve3(value2);
|
|
116519
116570
|
};
|
|
116520
116571
|
try {
|
|
116521
116572
|
server2.once("error", (err) => {
|
|
@@ -116569,7 +116620,7 @@ async function startUnlockListener(deps) {
|
|
|
116569
116620
|
// src/mcp-server/system-prompt.ts
|
|
116570
116621
|
init_esm_shims();
|
|
116571
116622
|
|
|
116572
|
-
// src/
|
|
116623
|
+
// src/tasks/memory-model.ts
|
|
116573
116624
|
init_esm_shims();
|
|
116574
116625
|
init_dist();
|
|
116575
116626
|
var TASK_MEMORY_MODEL_SECTION = `TASK MEMORY MODEL (the one description \u2014 every taskMemory* tool points here):
|
|
@@ -116832,12 +116883,12 @@ your wallet first** \u2014 discover it, don't dead-end on "I can't access that".
|
|
|
116832
116883
|
## Tasks are payable too
|
|
116833
116884
|
|
|
116834
116885
|
Beyond raw merchants, your workspace has reusable **tasks** \u2014 multi-step
|
|
116835
|
-
procedures that may spend on merchants as they run. A task is
|
|
116836
|
-
|
|
116837
|
-
|
|
116886
|
+
procedures that may spend on merchants as they run. A task is one card
|
|
116887
|
+
whether it is a one-shot procedure or a scheduled job with its own memory,
|
|
116888
|
+
and one set of tools covers all of them.
|
|
116838
116889
|
|
|
116839
116890
|
- **\`getTask({ intent })\`** then **\`runTask\`** \u2014 find and run one, whether
|
|
116840
|
-
it is a one-shot procedure or
|
|
116891
|
+
it is a one-shot procedure or a scheduled job with its own memory.
|
|
116841
116892
|
- **\`createTask\`** \u2014 author or modify one.
|
|
116842
116893
|
|
|
116843
116894
|
These are *your* capabilities the same way the merchants are: available through
|
|
@@ -117017,9 +117068,9 @@ async function buildMerchantErrorHelp(opts) {
|
|
|
117017
117068
|
let timer;
|
|
117018
117069
|
const fetched = await Promise.race([
|
|
117019
117070
|
opts.fetchInstructions(provider),
|
|
117020
|
-
new Promise((
|
|
117071
|
+
new Promise((resolve3) => {
|
|
117021
117072
|
timer = setTimeout(
|
|
117022
|
-
() =>
|
|
117073
|
+
() => resolve3({ status: "nok", error: `instructions fetch timed out after ${timeoutMs}ms` }),
|
|
117023
117074
|
timeoutMs
|
|
117024
117075
|
);
|
|
117025
117076
|
})
|
|
@@ -117084,15 +117135,13 @@ function shouldAutoShare(toolCallCount2, isDesignPartner) {
|
|
|
117084
117135
|
init_esm_shims();
|
|
117085
117136
|
function nudgeFlagForCounts(counts) {
|
|
117086
117137
|
if (!counts) return null;
|
|
117087
|
-
if (!Number.isFinite(counts.
|
|
117088
|
-
if (counts.
|
|
117089
|
-
if (counts.
|
|
117090
|
-
if (counts.loops === 0) return "loop";
|
|
117138
|
+
if (!Number.isFinite(counts.tasks)) return null;
|
|
117139
|
+
if (counts.tasks < 0) return null;
|
|
117140
|
+
if (counts.tasks === 0) return "task";
|
|
117091
117141
|
return null;
|
|
117092
117142
|
}
|
|
117093
117143
|
var NUDGE_TEXT = {
|
|
117094
|
-
|
|
117095
|
-
loop: "\u2139\uFE0F Ametyst \u2014 this workspace has skills but no loop yet. Ask the user: would they like to turn one of their skills into a loop? A loop runs on a schedule and keeps its own memory between runs, so a skill they fire by hand every day runs itself instead. If they say yes, walk them through `createTask`. If they say no, drop it and carry on \u2014 do not ask again in this turn. This note is advisory: finish the tool result above first, it is unrelated."
|
|
117144
|
+
task: "\u2139\uFE0F Ametyst \u2014 this workspace has not created any task yet. Ask the user: would they like to create their first task now? A task packages a workflow they repeat into one reusable command \u2014 run by hand or on a schedule, with its own memory between runs \u2014 instead of re-explaining it every session. If they say yes, walk them through `createTask`. If they say no, drop it and carry on \u2014 do not ask again in this turn. This note is advisory: finish the tool result above first, it is unrelated."
|
|
117096
117145
|
};
|
|
117097
117146
|
function nudgeTextFor(flag) {
|
|
117098
117147
|
return Object.prototype.hasOwnProperty.call(NUDGE_TEXT, flag) ? NUDGE_TEXT[flag] : null;
|
|
@@ -117311,12 +117360,12 @@ init_paths();
|
|
|
117311
117360
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
117312
117361
|
import { join as join9 } from "path";
|
|
117313
117362
|
|
|
117314
|
-
// src/
|
|
117363
|
+
// src/tasks/state-docs.ts
|
|
117315
117364
|
init_esm_shims();
|
|
117316
117365
|
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
117317
117366
|
import { join as join8 } from "path";
|
|
117318
117367
|
|
|
117319
|
-
// src/
|
|
117368
|
+
// src/tasks/shipback.ts
|
|
117320
117369
|
init_esm_shims();
|
|
117321
117370
|
function scanHeadings(text) {
|
|
117322
117371
|
return scanDoc(text).items;
|
|
@@ -117439,7 +117488,7 @@ function verifyLanded(afterWrite, expectedBlocks) {
|
|
|
117439
117488
|
return expectedBlocks.filter((t) => !have.has(t));
|
|
117440
117489
|
}
|
|
117441
117490
|
|
|
117442
|
-
// src/
|
|
117491
|
+
// src/tasks/memory-manifest.ts
|
|
117443
117492
|
init_esm_shims();
|
|
117444
117493
|
var MEMORY_MANIFEST_MAX_DOCS = 64;
|
|
117445
117494
|
var MEMORY_MANIFEST_MAX_RECORDS = 16;
|
|
@@ -117595,7 +117644,7 @@ function formatMemoryManifest(manifest) {
|
|
|
117595
117644
|
return lines.length ? lines.join("\n") : "(declared empty: no docs, no records)";
|
|
117596
117645
|
}
|
|
117597
117646
|
|
|
117598
|
-
// src/
|
|
117647
|
+
// src/tasks/state-docs.ts
|
|
117599
117648
|
var RESERVED_FIRE_FILENAMES = [
|
|
117600
117649
|
"SKILL.md",
|
|
117601
117650
|
"VISION.md",
|
|
@@ -117820,8 +117869,8 @@ var TASK_DEFINITION_FILES = [
|
|
|
117820
117869
|
["dashboardHtml", "dashboard.html", "dashboardHtml"],
|
|
117821
117870
|
["dashboardManifest", "dashboard.manifest.json", "dashboardManifest"]
|
|
117822
117871
|
];
|
|
117823
|
-
function materializeTask(task, runId) {
|
|
117824
|
-
const dir =
|
|
117872
|
+
function materializeTask(task, runId, runRoot) {
|
|
117873
|
+
const dir = runFireDir(task.slug, runId, runRoot);
|
|
117825
117874
|
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
117826
117875
|
const files = {};
|
|
117827
117876
|
const skipped = [];
|
|
@@ -117882,17 +117931,55 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
|
|
|
117882
117931
|
// src/mcp-server/index.ts
|
|
117883
117932
|
import { existsSync as existsSync13 } from "fs";
|
|
117884
117933
|
|
|
117885
|
-
// src/
|
|
117934
|
+
// src/tasks/dashboard.ts
|
|
117886
117935
|
init_esm_shims();
|
|
117887
117936
|
import { createServer as createServer2 } from "http";
|
|
117888
117937
|
import * as realFs from "fs";
|
|
117889
117938
|
import { spawn as realSpawn } from "child_process";
|
|
117890
117939
|
import { join as join10 } from "path";
|
|
117940
|
+
|
|
117941
|
+
// src/tasks/env.ts
|
|
117942
|
+
init_esm_shims();
|
|
117943
|
+
var TASK_ENV = Object.freeze({
|
|
117944
|
+
DASHBOARD_PORT: "AMETYST_TASK_DASHBOARD_PORT",
|
|
117945
|
+
MAX_BUDGET_USD: "AMETYST_TASK_MAX_BUDGET_USD",
|
|
117946
|
+
MAX_CONCURRENT_FIRES: "AMETYST_TASK_MAX_CONCURRENT_FIRES",
|
|
117947
|
+
GIT_AUTHOR_NAME: "AMETYST_TASK_GIT_AUTHOR_NAME",
|
|
117948
|
+
GIT_AUTHOR_EMAIL: "AMETYST_TASK_GIT_AUTHOR_EMAIL",
|
|
117949
|
+
SLUG: "AMETYST_TASK_SLUG"
|
|
117950
|
+
});
|
|
117951
|
+
var LEGACY_TASK_ENV = Object.freeze({
|
|
117952
|
+
DASHBOARD_PORT: "AMETYST_LOOP_DASHBOARD_PORT",
|
|
117953
|
+
MAX_BUDGET_USD: "AMETYST_LOOP_MAX_BUDGET_USD",
|
|
117954
|
+
MAX_CONCURRENT_FIRES: "AMETYST_LOOP_MAX_CONCURRENT_FIRES",
|
|
117955
|
+
GIT_AUTHOR_NAME: "AMETYST_LOOP_GIT_AUTHOR_NAME",
|
|
117956
|
+
GIT_AUTHOR_EMAIL: "AMETYST_LOOP_GIT_AUTHOR_EMAIL",
|
|
117957
|
+
SLUG: "AMETYST_LOOP_SLUG"
|
|
117958
|
+
});
|
|
117959
|
+
var warnedLegacyNames = /* @__PURE__ */ new Set();
|
|
117960
|
+
function legacyTaskEnvWarning(key) {
|
|
117961
|
+
return `${LEGACY_TASK_ENV[key]} is deprecated \u2014 rename it to ${TASK_ENV[key]}. Read as ${TASK_ENV[key]} for now; the old name stops being honoured in a later release.`;
|
|
117962
|
+
}
|
|
117963
|
+
function readTaskEnv(key, env = process.env, deps = {}) {
|
|
117964
|
+
const fresh = env[TASK_ENV[key]];
|
|
117965
|
+
if (fresh !== void 0) return fresh;
|
|
117966
|
+
const legacy = env[LEGACY_TASK_ENV[key]];
|
|
117967
|
+
if (legacy === void 0) return void 0;
|
|
117968
|
+
const legacyName = LEGACY_TASK_ENV[key];
|
|
117969
|
+
if (!warnedLegacyNames.has(legacyName)) {
|
|
117970
|
+
warnedLegacyNames.add(legacyName);
|
|
117971
|
+
(deps.warn ?? ((line) => console.warn(line)))(legacyTaskEnvWarning(key));
|
|
117972
|
+
}
|
|
117973
|
+
return legacy;
|
|
117974
|
+
}
|
|
117975
|
+
|
|
117976
|
+
// src/tasks/dashboard.ts
|
|
117891
117977
|
var DEFAULT_PORT = 4477;
|
|
117892
|
-
var DASHBOARD_PORT_ENV =
|
|
117978
|
+
var DASHBOARD_PORT_ENV = TASK_ENV.DASHBOARD_PORT;
|
|
117893
117979
|
var DASHBOARD_NO_OPEN_ENV = "AMETYST_DASHBOARD_NO_OPEN";
|
|
117894
117980
|
var NO_DASHBOARD_MESSAGE = "no dashboard on this task \u2014 ask your agent to create one";
|
|
117895
117981
|
var MAX_PORT_RETRIES = 20;
|
|
117982
|
+
var DASHBOARD_DOC_CACHE_MS = 5e3;
|
|
117896
117983
|
var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
117897
117984
|
function parseManifestFiles(manifest) {
|
|
117898
117985
|
if (typeof manifest !== "string" || !manifest.trim()) return [];
|
|
@@ -117906,7 +117993,7 @@ function parseManifestFiles(manifest) {
|
|
|
117906
117993
|
return list2.filter((f) => typeof f === "string" && SAFE_NAME.test(f));
|
|
117907
117994
|
}
|
|
117908
117995
|
function dashboardPort(env = process.env) {
|
|
117909
|
-
const raw = Number(env
|
|
117996
|
+
const raw = Number(readTaskEnv("DASHBOARD_PORT", env));
|
|
117910
117997
|
return Number.isInteger(raw) && raw > 0 && raw < 65536 ? raw : DEFAULT_PORT;
|
|
117911
117998
|
}
|
|
117912
117999
|
function openDashboardInBrowser(url2, deps = {}) {
|
|
@@ -117917,9 +118004,9 @@ function openDashboardInBrowser(url2, deps = {}) {
|
|
|
117917
118004
|
const platform = deps.platform ?? process.platform;
|
|
117918
118005
|
const cmd = platform === "darwin" ? "open" : platform === "linux" ? "xdg-open" : null;
|
|
117919
118006
|
if (!cmd) return false;
|
|
117920
|
-
const
|
|
118007
|
+
const spawn3 = deps.spawn ?? realSpawn;
|
|
117921
118008
|
try {
|
|
117922
|
-
const child =
|
|
118009
|
+
const child = spawn3(cmd, [url2], { stdio: "ignore", detached: true });
|
|
117923
118010
|
child.once?.("error", () => {
|
|
117924
118011
|
});
|
|
117925
118012
|
child.unref?.();
|
|
@@ -117929,14 +118016,32 @@ function openDashboardInBrowser(url2, deps = {}) {
|
|
|
117929
118016
|
}
|
|
117930
118017
|
}
|
|
117931
118018
|
function startDashboardServer(args) {
|
|
117932
|
-
const html = args.
|
|
118019
|
+
const html = args.task.dashboardHtml;
|
|
117933
118020
|
if (typeof html !== "string" || !html) return Promise.resolve(null);
|
|
117934
118021
|
const fs = args.deps?.fs ?? realFs;
|
|
117935
118022
|
const log = args.deps?.log ?? ((line) => console.error(line));
|
|
117936
118023
|
const make = args.deps?.createServer ?? createServer2;
|
|
118024
|
+
const clock = args.deps?.now ?? Date.now;
|
|
117937
118025
|
const basePort = args.port ?? dashboardPort();
|
|
117938
|
-
const files = parseManifestFiles(args.
|
|
117939
|
-
const
|
|
118026
|
+
const files = parseManifestFiles(args.task.dashboardManifest);
|
|
118027
|
+
const readDoc = args.readDoc;
|
|
118028
|
+
const docCache = /* @__PURE__ */ new Map();
|
|
118029
|
+
const resolveDoc = async (name) => {
|
|
118030
|
+
if (!readDoc) return null;
|
|
118031
|
+
const now = clock();
|
|
118032
|
+
const hit = docCache.get(name);
|
|
118033
|
+
if (hit && now - hit.at < DASHBOARD_DOC_CACHE_MS) return hit.value;
|
|
118034
|
+
let value2 = null;
|
|
118035
|
+
try {
|
|
118036
|
+
const got = await readDoc(name);
|
|
118037
|
+
value2 = got && typeof got.content === "string" ? got : null;
|
|
118038
|
+
} catch {
|
|
118039
|
+
value2 = null;
|
|
118040
|
+
}
|
|
118041
|
+
docCache.set(name, { at: now, value: value2 });
|
|
118042
|
+
return value2;
|
|
118043
|
+
};
|
|
118044
|
+
const serve = async (req, res) => {
|
|
117940
118045
|
try {
|
|
117941
118046
|
const url2 = (req.url ?? "/").split("?")[0];
|
|
117942
118047
|
if (req.method === "GET" && url2 === "/") {
|
|
@@ -117946,27 +118051,36 @@ function startDashboardServer(args) {
|
|
|
117946
118051
|
}
|
|
117947
118052
|
if (req.method === "GET" && url2 === "/data") {
|
|
117948
118053
|
const data = {};
|
|
118054
|
+
let newestUpdatedAt;
|
|
117949
118055
|
for (const name of files) {
|
|
118056
|
+
const doc = await resolveDoc(name);
|
|
118057
|
+
if (doc) {
|
|
118058
|
+
data[name] = doc.content;
|
|
118059
|
+
if (doc.updatedAt && (!newestUpdatedAt || Date.parse(doc.updatedAt) > Date.parse(newestUpdatedAt))) {
|
|
118060
|
+
newestUpdatedAt = doc.updatedAt;
|
|
118061
|
+
}
|
|
118062
|
+
continue;
|
|
118063
|
+
}
|
|
117950
118064
|
try {
|
|
117951
|
-
const p = join10(args.
|
|
118065
|
+
const p = join10(args.runDir, name);
|
|
117952
118066
|
if (fs.existsSync(p)) data[name] = fs.readFileSync(p, "utf-8");
|
|
117953
118067
|
} catch {
|
|
117954
118068
|
}
|
|
117955
118069
|
}
|
|
117956
118070
|
const state = {};
|
|
117957
118071
|
try {
|
|
117958
|
-
const p = join10(args.
|
|
118072
|
+
const p = join10(args.runDir, ".state", "fires.jsonl");
|
|
117959
118073
|
if (fs.existsSync(p)) state["fires.jsonl"] = fs.readFileSync(p, "utf-8");
|
|
117960
118074
|
} catch {
|
|
117961
118075
|
}
|
|
117962
118076
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
117963
118077
|
res.end(
|
|
117964
118078
|
JSON.stringify({
|
|
117965
|
-
loop: args.
|
|
118079
|
+
loop: args.task.slug,
|
|
117966
118080
|
files: data,
|
|
117967
118081
|
state,
|
|
117968
118082
|
mode: "live",
|
|
117969
|
-
asOf: (/* @__PURE__ */ new Date()).toISOString()
|
|
118083
|
+
asOf: newestUpdatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
117970
118084
|
})
|
|
117971
118085
|
);
|
|
117972
118086
|
return;
|
|
@@ -117981,18 +118095,27 @@ function startDashboardServer(args) {
|
|
|
117981
118095
|
}
|
|
117982
118096
|
}
|
|
117983
118097
|
};
|
|
117984
|
-
const
|
|
118098
|
+
const handler = (req, res) => {
|
|
118099
|
+
void serve(req, res).catch(() => {
|
|
118100
|
+
try {
|
|
118101
|
+
res.writeHead(500);
|
|
118102
|
+
res.end();
|
|
118103
|
+
} catch {
|
|
118104
|
+
}
|
|
118105
|
+
});
|
|
118106
|
+
};
|
|
118107
|
+
const bind = (port) => new Promise((resolve3) => {
|
|
117985
118108
|
const server2 = make(handler);
|
|
117986
118109
|
server2.once("error", (err) => {
|
|
117987
118110
|
try {
|
|
117988
118111
|
server2.close();
|
|
117989
118112
|
} catch {
|
|
117990
118113
|
}
|
|
117991
|
-
|
|
118114
|
+
resolve3({ ok: false, err });
|
|
117992
118115
|
});
|
|
117993
118116
|
server2.listen(port, "127.0.0.1", () => {
|
|
117994
118117
|
server2.unref();
|
|
117995
|
-
|
|
118118
|
+
resolve3({
|
|
117996
118119
|
ok: true,
|
|
117997
118120
|
handle: {
|
|
117998
118121
|
server: server2,
|
|
@@ -118013,350 +118136,57 @@ function startDashboardServer(args) {
|
|
|
118013
118136
|
for (let port = basePort; port <= lastPort; port++) {
|
|
118014
118137
|
const attempt = await bind(port);
|
|
118015
118138
|
if (attempt.ok) {
|
|
118016
|
-
log(`
|
|
118139
|
+
log(` task dashboard: http://localhost:${attempt.handle.port}`);
|
|
118017
118140
|
return attempt.handle;
|
|
118018
118141
|
}
|
|
118019
118142
|
if (attempt.err.code !== "EADDRINUSE") {
|
|
118020
118143
|
log(
|
|
118021
|
-
` (
|
|
118144
|
+
` (task dashboard not started on :${port} \u2014 ${attempt.err.code ?? attempt.err.message}; run continues)`
|
|
118022
118145
|
);
|
|
118023
118146
|
return null;
|
|
118024
118147
|
}
|
|
118025
118148
|
}
|
|
118026
|
-
log(` (
|
|
118149
|
+
log(` (task dashboard not started \u2014 :${basePort}-${lastPort} all in use; run continues)`);
|
|
118027
118150
|
return null;
|
|
118028
118151
|
})();
|
|
118029
118152
|
}
|
|
118030
118153
|
|
|
118031
|
-
// src/
|
|
118154
|
+
// src/tasks/dashboard-docs.ts
|
|
118032
118155
|
init_esm_shims();
|
|
118033
|
-
|
|
118034
|
-
|
|
118035
|
-
|
|
118036
|
-
|
|
118037
|
-
|
|
118038
|
-
|
|
118039
|
-
|
|
118040
|
-
|
|
118041
|
-
}
|
|
118042
|
-
|
|
118043
|
-
|
|
118044
|
-
|
|
118045
|
-
|
|
118046
|
-
} catch {
|
|
118047
|
-
return String(raw);
|
|
118048
|
-
}
|
|
118049
|
-
}
|
|
118050
|
-
function classifyRunModeArgument(raw) {
|
|
118051
|
-
if (raw === void 0 || raw === null) return { kind: "omitted" };
|
|
118052
|
-
if (typeof raw === "string" && raw.trim() === "") return { kind: "omitted" };
|
|
118053
|
-
const mode2 = normalizeRunMode(raw);
|
|
118054
|
-
if (mode2) return { kind: "valid", mode: mode2 };
|
|
118055
|
-
return { kind: "invalid", provided: describeProvided(raw) };
|
|
118056
|
-
}
|
|
118057
|
-
function unquoteScalar(raw) {
|
|
118058
|
-
let v = raw.trim();
|
|
118059
|
-
const comment = v.match(/(?:^|\s)#.*$/);
|
|
118060
|
-
if (comment) v = v.slice(0, comment.index === 0 ? 0 : comment.index).trim();
|
|
118061
|
-
if (v.length >= 2 && (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))) {
|
|
118062
|
-
v = v.slice(1, -1).trim();
|
|
118063
|
-
}
|
|
118064
|
-
return v;
|
|
118065
|
-
}
|
|
118066
|
-
function parseDefaultRunMode(body) {
|
|
118067
|
-
if (typeof body !== "string" || !body) return void 0;
|
|
118068
|
-
const head = body.replace(/^\uFEFF/, "").slice(0, FRONTMATTER_SCAN_LIMIT);
|
|
118069
|
-
const opener = head.match(/^---[ \t]*\r?\n/);
|
|
118070
|
-
if (!opener) return void 0;
|
|
118071
|
-
const rest = head.slice(opener[0].length);
|
|
118072
|
-
const closer = rest.search(/^(?:---|\.\.\.)[ \t]*(?:\r?\n|$)/m);
|
|
118073
|
-
if (closer < 0) return void 0;
|
|
118074
|
-
const block = rest.slice(0, closer);
|
|
118075
|
-
const hit = block.match(/^defaultRunMode[ \t]*:[ \t]*(.*)$/m);
|
|
118076
|
-
if (!hit) return void 0;
|
|
118077
|
-
return normalizeRunMode(unquoteScalar(hit[1] ?? ""));
|
|
118078
|
-
}
|
|
118079
|
-
function resolveRunMode(explicit, body) {
|
|
118080
|
-
const arg = classifyRunModeArgument(explicit);
|
|
118081
|
-
if (arg.kind === "invalid") {
|
|
118082
|
-
return { ok: false, error: "invalid_mode", provided: arg.provided, accepted: ACCEPTED_RUN_MODES };
|
|
118083
|
-
}
|
|
118084
|
-
if (arg.kind === "valid") return { ok: true, mode: arg.mode, source: "explicit" };
|
|
118085
|
-
const fromBody = parseDefaultRunMode(body);
|
|
118086
|
-
if (fromBody) return { ok: true, mode: fromBody, source: "frontmatter" };
|
|
118087
|
-
return { ok: true, mode: "in-chat", source: "default" };
|
|
118088
|
-
}
|
|
118089
|
-
|
|
118090
|
-
// src/loops/estimate.ts
|
|
118091
|
-
init_esm_shims();
|
|
118092
|
-
function estimateBlastRadius(loop2) {
|
|
118093
|
-
const g = loop2.graphJson ?? {};
|
|
118094
|
-
const nodes = Array.isArray(g.nodes) ? g.nodes : [];
|
|
118095
|
-
const steps = nodes.length;
|
|
118096
|
-
const paidSteps = nodes.filter(
|
|
118097
|
-
(n) => n?.type === "spend" || n?.data?.paid === true || n?.paid === true
|
|
118098
|
-
).length;
|
|
118099
|
-
const costHints = nodes.map((n) => Number(n?.data?.estCostEur ?? n?.estCostEur)).filter((x) => !Number.isNaN(x));
|
|
118100
|
-
const estCostEur = costHints.length ? costHints.reduce((a, b) => a + b, 0) : null;
|
|
118101
|
-
return { steps, paidSteps, estCostEur };
|
|
118102
|
-
}
|
|
118103
|
-
|
|
118104
|
-
// src/loops/dashboard-template.ts
|
|
118105
|
-
init_esm_shims();
|
|
118106
|
-
var DEFAULT_DASHBOARD_FILES = [
|
|
118107
|
-
"VISION.md",
|
|
118108
|
-
"CONSTRAINTS.md",
|
|
118109
|
-
"QUEUE.md",
|
|
118110
|
-
"STATUS.md",
|
|
118111
|
-
"README.md",
|
|
118112
|
-
"rounds.jsonl"
|
|
118113
|
-
];
|
|
118114
|
-
function parseProcessSpec(manifest) {
|
|
118115
|
-
if (typeof manifest !== "string" || !manifest.trim()) return null;
|
|
118116
|
-
let parsed;
|
|
118117
|
-
try {
|
|
118118
|
-
parsed = JSON.parse(manifest);
|
|
118119
|
-
} catch {
|
|
118120
|
-
return null;
|
|
118121
|
-
}
|
|
118122
|
-
const raw = parsed?.processSpec;
|
|
118123
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
118124
|
-
const spec = raw;
|
|
118125
|
-
const stages = (Array.isArray(spec.stages) ? spec.stages : []).map((s) => {
|
|
118126
|
-
if (typeof s === "string" && s.trim()) return { label: s.trim() };
|
|
118127
|
-
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
118128
|
-
const o = s;
|
|
118129
|
-
if (typeof o.label === "string" && o.label.trim()) {
|
|
118130
|
-
return {
|
|
118131
|
-
label: o.label.trim(),
|
|
118132
|
-
...typeof o.id === "string" && o.id.trim() ? { id: o.id.trim() } : {},
|
|
118133
|
-
...typeof o.detail === "string" && o.detail.trim() ? { detail: o.detail.trim() } : {}
|
|
118134
|
-
};
|
|
118156
|
+
function memoryDocResolver(sdk, apiKey, slug, manifest) {
|
|
118157
|
+
return async (name) => {
|
|
118158
|
+
const key = docKeyForFilename(name);
|
|
118159
|
+
if (!key) return null;
|
|
118160
|
+
const declared = declaredDocScope(manifest, key);
|
|
118161
|
+
const scopes = declared ? [declared] : ["member", "shared"];
|
|
118162
|
+
for (const scope of scopes) {
|
|
118163
|
+
try {
|
|
118164
|
+
const res = await sdk.loops.memory.getDoc(apiKey, slug, key, { scope });
|
|
118165
|
+
if (res.status === "ok") {
|
|
118166
|
+
return { content: res.doc.content ?? "", ...res.doc.updatedAt ? { updatedAt: res.doc.updatedAt } : {} };
|
|
118167
|
+
}
|
|
118168
|
+
} catch {
|
|
118135
118169
|
}
|
|
118136
118170
|
}
|
|
118137
118171
|
return null;
|
|
118138
|
-
}).filter((s) => s !== null);
|
|
118139
|
-
const strings = (v) => (Array.isArray(v) ? v : []).filter((x) => typeof x === "string" && x.trim() !== "");
|
|
118140
|
-
const out = {
|
|
118141
|
-
stages,
|
|
118142
|
-
inputs: strings(spec.inputs),
|
|
118143
|
-
outputs: strings(spec.outputs),
|
|
118144
|
-
...typeof spec.title === "string" && spec.title.trim() ? { title: spec.title.trim() } : {}
|
|
118145
118172
|
};
|
|
118146
|
-
return stages.length || out.inputs.length || out.outputs.length ? out : null;
|
|
118147
|
-
}
|
|
118148
|
-
function defaultDashboardManifest() {
|
|
118149
|
-
return JSON.stringify({ files: [...DEFAULT_DASHBOARD_FILES] });
|
|
118150
118173
|
}
|
|
118151
|
-
function
|
|
118152
|
-
|
|
118174
|
+
function docKeyForFilename(name) {
|
|
118175
|
+
if (typeof name !== "string" || name.length === 0 || name.startsWith(".")) return void 0;
|
|
118176
|
+
if (name.includes("/") || name.includes("\\")) return void 0;
|
|
118177
|
+
const key = name.endsWith(".md") ? name.slice(0, -".md".length) : name;
|
|
118178
|
+
return key.length > 0 ? key : void 0;
|
|
118153
118179
|
}
|
|
118154
|
-
function escapeHtml(s) {
|
|
118155
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
118156
|
-
}
|
|
118157
|
-
function renderLoopDashboardTemplate(args) {
|
|
118158
|
-
const title = escapeHtml(args.processSpec?.title ?? args.slug);
|
|
118159
|
-
const seed = embedJson({
|
|
118160
|
-
slug: args.slug,
|
|
118161
|
-
descriptionShort: args.descriptionShort ?? "",
|
|
118162
|
-
processSpec: args.processSpec ?? null
|
|
118163
|
-
});
|
|
118164
|
-
return `<!doctype html>
|
|
118165
|
-
<html lang="en">
|
|
118166
|
-
<head>
|
|
118167
|
-
<meta charset="utf-8">
|
|
118168
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
118169
|
-
<title>${title} \u2014 loop dashboard</title>
|
|
118170
|
-
<style>
|
|
118171
|
-
:root { --bg:#f7f7fb; --panel:#fff; --border:#e3e3ee; --muted:#6b6b80; --accent:#5b5bd6; --ok:#1a7f37; --bad:#b42318; }
|
|
118172
|
-
* { box-sizing: border-box; }
|
|
118173
|
-
body { margin:0; font:14px/1.5 -apple-system, "Segoe UI", Roboto, sans-serif; background:var(--bg); color:#1d1d2b; padding:24px; }
|
|
118174
|
-
h1 { font-size:20px; margin:0 0 4px; }
|
|
118175
|
-
h2 { font-size:14px; margin:24px 0 8px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); }
|
|
118176
|
-
.sub { color:var(--muted); margin:0 0 16px; }
|
|
118177
|
-
.panel { background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:16px; }
|
|
118178
|
-
.map { display:flex; flex-wrap:wrap; align-items:stretch; gap:8px; }
|
|
118179
|
-
.stage { background:var(--panel); border:1px solid var(--border); border-left:3px solid var(--accent); border-radius:10px; padding:10px 14px; min-width:140px; flex:1; }
|
|
118180
|
-
.stage .label { font-weight:600; }
|
|
118181
|
-
.stage .detail { color:var(--muted); font-size:12px; margin-top:2px; }
|
|
118182
|
-
.arrow { align-self:center; color:var(--muted); }
|
|
118183
|
-
.io { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
|
|
118184
|
-
ul { margin:6px 0 0; padding-left:18px; }
|
|
118185
|
-
table { width:100%; border-collapse:collapse; background:var(--panel); border:1px solid var(--border); border-radius:12px; overflow:hidden; }
|
|
118186
|
-
th, td { text-align:left; padding:8px 12px; border-top:1px solid var(--border); font-size:13px; vertical-align:top; }
|
|
118187
|
-
thead th { border-top:none; background:var(--bg); color:var(--muted); font-weight:600; }
|
|
118188
|
-
.ok { color:var(--ok); } .bad { color:var(--bad); }
|
|
118189
|
-
details { background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:10px 14px; margin-bottom:8px; }
|
|
118190
|
-
summary { cursor:pointer; font-weight:600; }
|
|
118191
|
-
pre { overflow-x:auto; font-size:12px; background:var(--bg); border-radius:8px; padding:10px; }
|
|
118192
|
-
.empty { color:var(--muted); font-style:italic; }
|
|
118193
|
-
#live { font-size:12px; color:var(--muted); float:right; }
|
|
118194
|
-
</style>
|
|
118195
|
-
</head>
|
|
118196
|
-
<body>
|
|
118197
|
-
<span id="live">loading\u2026</span>
|
|
118198
|
-
<h1 id="title"></h1>
|
|
118199
|
-
<p class="sub" id="desc"></p>
|
|
118200
|
-
|
|
118201
|
-
<h2>Process</h2>
|
|
118202
|
-
<div id="map" class="map panel"></div>
|
|
118203
|
-
|
|
118204
|
-
<h2>Inputs & outputs</h2>
|
|
118205
|
-
<div class="io">
|
|
118206
|
-
<div class="panel"><strong>Inputs</strong><ul id="inputs"></ul></div>
|
|
118207
|
-
<div class="panel"><strong>Outputs</strong><ul id="outputs"></ul></div>
|
|
118208
|
-
</div>
|
|
118209
|
-
|
|
118210
|
-
<h2>Rounds</h2>
|
|
118211
|
-
<div id="rounds"></div>
|
|
118212
|
-
|
|
118213
|
-
<h2>Files</h2>
|
|
118214
|
-
<div id="files"></div>
|
|
118215
|
-
|
|
118216
|
-
<script type="application/json" id="loop-seed">${seed}</script>
|
|
118217
|
-
<script>
|
|
118218
|
-
(function () {
|
|
118219
|
-
"use strict";
|
|
118220
|
-
var seed = JSON.parse(document.getElementById("loop-seed").textContent);
|
|
118221
|
-
document.getElementById("title").textContent = (seed.processSpec && seed.processSpec.title) || seed.slug;
|
|
118222
|
-
document.getElementById("desc").textContent = seed.descriptionShort || "";
|
|
118223
|
-
document.title = seed.slug + " \u2014 loop dashboard";
|
|
118224
|
-
|
|
118225
|
-
function el(tag, cls, text) {
|
|
118226
|
-
var e = document.createElement(tag);
|
|
118227
|
-
if (cls) e.className = cls;
|
|
118228
|
-
if (text !== undefined) e.textContent = text;
|
|
118229
|
-
return e;
|
|
118230
|
-
}
|
|
118231
|
-
|
|
118232
|
-
// \u2500\u2500 Process map + IO from the embedded spec (static \u2014 rendered once). \u2500\u2500
|
|
118233
|
-
var map = document.getElementById("map");
|
|
118234
|
-
var spec = seed.processSpec;
|
|
118235
|
-
if (spec && spec.stages && spec.stages.length) {
|
|
118236
|
-
spec.stages.forEach(function (s, i) {
|
|
118237
|
-
if (i > 0) map.appendChild(el("div", "arrow", "\\u2192"));
|
|
118238
|
-
var box = el("div", "stage");
|
|
118239
|
-
box.appendChild(el("div", "label", s.label));
|
|
118240
|
-
if (s.detail) box.appendChild(el("div", "detail", s.detail));
|
|
118241
|
-
map.appendChild(box);
|
|
118242
|
-
});
|
|
118243
|
-
} else {
|
|
118244
|
-
map.appendChild(el("span", "empty", "No process spec on this loop \\u2014 live files and rounds below."));
|
|
118245
|
-
}
|
|
118246
|
-
function fillList(id, items) {
|
|
118247
|
-
var ul = document.getElementById(id);
|
|
118248
|
-
ul.textContent = "";
|
|
118249
|
-
if (!items || !items.length) { ul.appendChild(el("li", "empty", "\\u2014")); return; }
|
|
118250
|
-
items.forEach(function (x) { ul.appendChild(el("li", null, x)); });
|
|
118251
|
-
}
|
|
118252
|
-
fillList("inputs", spec && spec.inputs);
|
|
118253
|
-
fillList("outputs", spec && spec.outputs);
|
|
118254
|
-
|
|
118255
|
-
// \u2500\u2500 Live data: rounds + files, refreshed from GET /data. \u2500\u2500
|
|
118256
|
-
function parseJsonl(text) {
|
|
118257
|
-
var rows = [];
|
|
118258
|
-
(text || "").split("\\n").forEach(function (line) {
|
|
118259
|
-
line = line.trim();
|
|
118260
|
-
if (!line) return;
|
|
118261
|
-
try { rows.push(JSON.parse(line)); } catch (e) { /* skip malformed line */ }
|
|
118262
|
-
});
|
|
118263
|
-
return rows;
|
|
118264
|
-
}
|
|
118265
|
-
|
|
118266
|
-
function renderRounds(fires, rounds) {
|
|
118267
|
-
var host = document.getElementById("rounds");
|
|
118268
|
-
host.textContent = "";
|
|
118269
|
-
if (!fires.length && !rounds.length) {
|
|
118270
|
-
var p = el("div", "panel"); p.appendChild(el("span", "empty", "No rounds yet \\u2014 this fills in as the loop runs."));
|
|
118271
|
-
host.appendChild(p);
|
|
118272
|
-
return;
|
|
118273
|
-
}
|
|
118274
|
-
var table = document.createElement("table");
|
|
118275
|
-
var thead = document.createElement("thead");
|
|
118276
|
-
var hr = document.createElement("tr");
|
|
118277
|
-
["when", "duration", "turns", "tokens", "exit", "round detail (rounds.jsonl)"].forEach(function (h) {
|
|
118278
|
-
hr.appendChild(el("th", null, h));
|
|
118279
|
-
});
|
|
118280
|
-
thead.appendChild(hr); table.appendChild(thead);
|
|
118281
|
-
var tbody = document.createElement("tbody");
|
|
118282
|
-
var n = Math.max(fires.length, rounds.length);
|
|
118283
|
-
for (var i = n - 1; i >= 0; i--) { // newest first
|
|
118284
|
-
var f = fires[i] || {};
|
|
118285
|
-
var r = rounds[i];
|
|
118286
|
-
var tr = document.createElement("tr");
|
|
118287
|
-
tr.appendChild(el("td", null, f.ts || (r && r.ts) || "\\u2014"));
|
|
118288
|
-
tr.appendChild(el("td", null, f.duration_s != null ? f.duration_s + "s" : "\\u2014"));
|
|
118289
|
-
tr.appendChild(el("td", null, f.turns != null ? String(f.turns) : "\\u2014"));
|
|
118290
|
-
tr.appendChild(el("td", null, f.tokens_total != null ? Number(f.tokens_total).toLocaleString() : "\\u2014"));
|
|
118291
|
-
tr.appendChild(el("td", f.exit === 0 ? "ok" : f.exit != null ? "bad" : null, f.exit != null ? String(f.exit) : "\\u2014"));
|
|
118292
|
-
var detail = el("td");
|
|
118293
|
-
if (f.launch_failed) { detail.textContent = "launch failed \\u2014 " + (f.reason || "unknown reason"); }
|
|
118294
|
-
else if (r) { var pre = document.createElement("pre"); pre.textContent = JSON.stringify(r, null, 1); detail.appendChild(pre); }
|
|
118295
|
-
else detail.textContent = "\\u2014";
|
|
118296
|
-
tr.appendChild(detail);
|
|
118297
|
-
tbody.appendChild(tr);
|
|
118298
|
-
}
|
|
118299
|
-
table.appendChild(tbody);
|
|
118300
|
-
host.appendChild(table);
|
|
118301
|
-
}
|
|
118302
118180
|
|
|
118303
|
-
|
|
118304
|
-
|
|
118305
|
-
|
|
118306
|
-
var names = Object.keys(files || {}).filter(function (n) { return n !== "rounds.jsonl"; });
|
|
118307
|
-
if (!names.length) { var p = el("div", "panel"); p.appendChild(el("span", "empty", "No files surfaced by the manifest.")); host.appendChild(p); return; }
|
|
118308
|
-
names.forEach(function (name) {
|
|
118309
|
-
var d = document.createElement("details");
|
|
118310
|
-
d.appendChild(el("summary", null, name));
|
|
118311
|
-
var pre = document.createElement("pre");
|
|
118312
|
-
pre.textContent = files[name];
|
|
118313
|
-
d.appendChild(pre);
|
|
118314
|
-
host.appendChild(d);
|
|
118315
|
-
});
|
|
118316
|
-
}
|
|
118317
|
-
|
|
118318
|
-
function refresh() {
|
|
118319
|
-
fetch("/data").then(function (res) { return res.json(); }).then(function (data) {
|
|
118320
|
-
var fires = parseJsonl(data.state && data.state["fires.jsonl"]);
|
|
118321
|
-
var rounds = parseJsonl(data.files && data.files["rounds.jsonl"]);
|
|
118322
|
-
renderRounds(fires, rounds);
|
|
118323
|
-
renderFiles(data.files);
|
|
118324
|
-
document.getElementById("live").textContent = "updated " + new Date().toLocaleTimeString();
|
|
118325
|
-
}).catch(function () {
|
|
118326
|
-
document.getElementById("live").textContent = "server stopped";
|
|
118327
|
-
});
|
|
118328
|
-
}
|
|
118329
|
-
refresh();
|
|
118330
|
-
setInterval(refresh, 5000);
|
|
118331
|
-
})();
|
|
118332
|
-
</script>
|
|
118333
|
-
</body>
|
|
118334
|
-
</html>
|
|
118335
|
-
`;
|
|
118336
|
-
}
|
|
118337
|
-
function injectDefaultDashboard(loop2) {
|
|
118338
|
-
const hasHtml = typeof loop2.dashboardHtml === "string" && loop2.dashboardHtml.trim() !== "";
|
|
118339
|
-
if (hasHtml) return;
|
|
118340
|
-
if (loop2.dashboardManifest && typeof loop2.dashboardManifest === "object") {
|
|
118341
|
-
try {
|
|
118342
|
-
loop2.dashboardManifest = JSON.stringify(loop2.dashboardManifest);
|
|
118343
|
-
} catch {
|
|
118344
|
-
}
|
|
118345
|
-
}
|
|
118346
|
-
const manifest = typeof loop2.dashboardManifest === "string" ? loop2.dashboardManifest : void 0;
|
|
118347
|
-
loop2.dashboardHtml = renderLoopDashboardTemplate({
|
|
118348
|
-
slug: typeof loop2.slug === "string" ? loop2.slug : "loop",
|
|
118349
|
-
descriptionShort: typeof loop2.descriptionShort === "string" ? loop2.descriptionShort : void 0,
|
|
118350
|
-
processSpec: parseProcessSpec(manifest)
|
|
118351
|
-
});
|
|
118352
|
-
if (!manifest || !manifest.trim()) loop2.dashboardManifest = defaultDashboardManifest();
|
|
118353
|
-
}
|
|
118181
|
+
// src/tasks/launch.ts
|
|
118182
|
+
init_esm_shims();
|
|
118183
|
+
import { spawnSync } from "child_process";
|
|
118354
118184
|
|
|
118355
|
-
// src/
|
|
118185
|
+
// src/tasks/memory-verbs.ts
|
|
118356
118186
|
init_esm_shims();
|
|
118357
118187
|
import { readFileSync as readFileSync10 } from "fs";
|
|
118358
118188
|
|
|
118359
|
-
// src/
|
|
118189
|
+
// src/tasks/sdk.ts
|
|
118360
118190
|
init_esm_shims();
|
|
118361
118191
|
init_config();
|
|
118362
118192
|
init_resolve();
|
|
@@ -118386,7 +118216,7 @@ async function getCliSdk() {
|
|
|
118386
118216
|
return { sdk, apiKey };
|
|
118387
118217
|
}
|
|
118388
118218
|
|
|
118389
|
-
// src/
|
|
118219
|
+
// src/tasks/memory-verbs.ts
|
|
118390
118220
|
function emit(json, payload, human) {
|
|
118391
118221
|
if (json) {
|
|
118392
118222
|
console.log(JSON.stringify(payload, null, 2));
|
|
@@ -118674,6 +118504,461 @@ async function taskMemoryArchiveVerb(rawSlug, opts) {
|
|
|
118674
118504
|
);
|
|
118675
118505
|
}
|
|
118676
118506
|
|
|
118507
|
+
// src/tasks/launch.ts
|
|
118508
|
+
var TASK_INPUT_ENV = "AMETYST_TASK_INPUT";
|
|
118509
|
+
var LAUNCH_INPUT_HEADER = "LAUNCH INPUT \u2014 the user's arguments for this run; treat them exactly as if the user had typed them in chat:";
|
|
118510
|
+
function launchInputBlock(input) {
|
|
118511
|
+
if (typeof input !== "string" || input.trim() === "") return "";
|
|
118512
|
+
return `${LAUNCH_INPUT_HEADER}
|
|
118513
|
+
${input}
|
|
118514
|
+
|
|
118515
|
+
`;
|
|
118516
|
+
}
|
|
118517
|
+
function readGlobalGitConfig(key) {
|
|
118518
|
+
try {
|
|
118519
|
+
const r = spawnSync("git", ["config", "--global", "--get", key], { encoding: "utf-8" });
|
|
118520
|
+
const v = r.status === 0 ? r.stdout.trim() : "";
|
|
118521
|
+
return v || void 0;
|
|
118522
|
+
} catch {
|
|
118523
|
+
return void 0;
|
|
118524
|
+
}
|
|
118525
|
+
}
|
|
118526
|
+
function deriveGitIdentityEnv(env = process.env, readGitConfig = readGlobalGitConfig) {
|
|
118527
|
+
const name = readTaskEnv("GIT_AUTHOR_NAME", env)?.trim() || readGitConfig("user.name");
|
|
118528
|
+
const email = readTaskEnv("GIT_AUTHOR_EMAIL", env)?.trim() || readGitConfig("user.email");
|
|
118529
|
+
const out = {};
|
|
118530
|
+
if (name) {
|
|
118531
|
+
out.GIT_AUTHOR_NAME = name;
|
|
118532
|
+
out.GIT_COMMITTER_NAME = name;
|
|
118533
|
+
}
|
|
118534
|
+
if (email) {
|
|
118535
|
+
out.GIT_AUTHOR_EMAIL = email;
|
|
118536
|
+
out.GIT_COMMITTER_EMAIL = email;
|
|
118537
|
+
}
|
|
118538
|
+
return out;
|
|
118539
|
+
}
|
|
118540
|
+
function buildLaunchArgs(dir, opts = {}, slug, deps = {}) {
|
|
118541
|
+
const env = deps.env ?? process.env;
|
|
118542
|
+
const maxBudget = resolveMaxBudgetUsd(opts, env);
|
|
118543
|
+
const docKey = defaultDocKey(opts.memoryManifest);
|
|
118544
|
+
const readDocSentence = typeof docKey === "string" ? `taskMemoryGet({ taskSlug: "${slug}", docKey: "${docKey}" }) for your "${docKey}" document \u2014 the FIRST document this task's memory manifest declares, materialized for you at boot as ${filenameForKey(docKey)} \u2014 then ` : docKey === null ? `THIS TASK DECLARES NO MEMORY DOCUMENT \u2014 do not read one and do not create one; your records ARE its memory. Read ` : `no memory document is named here \u2014 this launcher could not resolve the task's manifest, so do NOT assume one exists. Read `;
|
|
118545
|
+
const writeDocSentence = typeof docKey === "string" ? `, and rewrite your "${docKey}" document with taskMemoryAppend({ taskSlug: "${slug}", docKey: "${docKey}", content: "<the state the next fire needs>" }).` : docKey === null ? `. This task declares no memory document, so there is nothing to rewrite \u2014 do not invent one; the run record and your keyed items are what the next fire reads.` : `. If this task declares a memory document, rewrite it with taskMemoryAppend({ taskSlug: "${slug}", docKey: "<the key its manifest declares>", content: "..." }) \u2014 this launcher could not name it for you, so do not guess a key.`;
|
|
118546
|
+
const prompt = `${launchInputBlock(opts.input)}You are running the Ametyst task${slug ? ` "${slug}"` : ""} headless and unattended.
|
|
118547
|
+
|
|
118548
|
+
${dir} is THIS FIRE'S OWN directory. Other fires of the same task may be running right now, each with its own directory alongside yours \u2014 wherever the task's SKILL says <LOOPDIR> it means exactly ${dir}, never a sibling's directory and never their shared parent. Derive every path the SKILL asks you to create (links, run-state) from ${dir}; never from a path written literally in the SKILL prose.
|
|
118549
|
+
|
|
118550
|
+
Read the task definition files in ${dir}:
|
|
118551
|
+
- SKILL.md \u2014 the driver; follow it.
|
|
118552
|
+
- VISION.md \u2014 the objective / done-condition.
|
|
118553
|
+
- CONSTRAINTS.md \u2014 hard limits; never violate them.
|
|
118554
|
+
- STATUS.md \u2014 your run-state; keep it updated as you progress.
|
|
118555
|
+
|
|
118556
|
+
The QUEUE (the work items) is EXTERNAL \u2014 it is NOT one of these files. SKILL.md tells you WHERE to read the queue from and WHERE to write the results; read your work items from that source.
|
|
118557
|
+
${slug ? `
|
|
118558
|
+
YOUR DURABLE MEMORY survives this fire, and ${dir} does not \u2014 this directory is deleted when you exit, so anything you want the NEXT fire to know must go into memory, not into a file here. Reach it with the taskMemory* MCP tools, always with taskSlug "${slug}" (also exported as AMETYST_TASK_SLUG). Nothing from it was injected into this fire beyond the materialized docs:
|
|
118559
|
+
- FIRST, before you start work, read what you need with taskMemoryGet: ${readDocSentence}one list per record kind you need \u2014 taskMemoryGet({ taskSlug: "${slug}", records: true, kind: "<kind>" }) \u2014 which returns the latest version per key, archived=false by default. If everything is empty this is your first fire \u2014 say so in your run record.
|
|
118560
|
+
- BEFORE YOU EXIT, on every path including a brake: taskMemoryAppend({ taskSlug: "${slug}", kind: "run", content: "<what you did, what you learned, what the next fire should pick up>" })${writeDocSentence}
|
|
118561
|
+
- Items with an identity (a PBI, a test, a merchant) are keyed records of a free kind: taskMemoryAppend({ taskSlug: "${slug}", kind: "<kind>", key: "<id>", content }) writes or versions one; taskMemoryArchive({ taskSlug: "${slug}", key: "<id>", note }) closes it.
|
|
118562
|
+
Memory is quota-bounded per workspace \u2014 an over-limit write is REJECTED and tells you the limit, never silently truncated. If a write is refused, shorten it and write again; do not skip the run record.
|
|
118563
|
+
|
|
118564
|
+
${TASK_MEMORY_MODEL_SECTION}
|
|
118565
|
+
` : ""}
|
|
118566
|
+
Execute the task until VISION is met or the queue is drained. For any step that costs money, use the Ametyst \`spend\` MCP tool (the on-chain policy enforces the budget) \u2014 do NOT invent another payment path.
|
|
118567
|
+
|
|
118568
|
+
When you finish cleanly (VISION met / queue drained), write "status: done" and "queue drained" into ${dir}/STATUS.md. If you must stop early (a brake/constraint was hit or an unrecoverable error occurred), write "brake: <reason>" into ${dir}/STATUS.md and exit. Never exceed the constraints.`;
|
|
118569
|
+
const args = [
|
|
118570
|
+
"-p",
|
|
118571
|
+
prompt,
|
|
118572
|
+
"--dangerously-skip-permissions",
|
|
118573
|
+
// The WRAPPER (`ametyst task run`, see runTask) owns shipping the task's learned
|
|
118574
|
+
// CONSTRAINTS back to Ametyst on exit. The headless agent must NEVER rewrite its own
|
|
118575
|
+
// stored task record, so deny the upsert tool even though `--dangerously-skip-permissions`
|
|
118576
|
+
// otherwise grants every tool. (STATUS/QUEUE stay local run-state and are never shipped.)
|
|
118577
|
+
// ⛔ THE LIST IS DERIVED, NOT TYPED, on both axes — a deny that names the wrong string is
|
|
118578
|
+
// indistinguishable from no deny at all:
|
|
118579
|
+
// - TOOL: `createTask` is what this server exposes now; `createLoop`, its retired alias,
|
|
118580
|
+
// is kept because the fire connects to whichever `ametyst serve` the host's MCP config
|
|
118581
|
+
// points at, which may still be an older build offering it. Denying only the retired
|
|
118582
|
+
// name is what the retirement would otherwise leave behind — an inert deny.
|
|
118583
|
+
// - ENTRY NAME: this line used to hardcode the `ametyst-staging` prefix, but a PROD build
|
|
118584
|
+
// registers as `ametyst` (AMETYST_MCP_NAME), so the guard was silently inert in prod.
|
|
118585
|
+
// AMETYST_MCP_NAMES is the repo's own list of every entry name this CLI family writes.
|
|
118586
|
+
// `--disallowedTools` is variadic and comma-or-space separated, so one arg carries them all.
|
|
118587
|
+
"--disallowedTools",
|
|
118588
|
+
AMETYST_MCP_NAMES.flatMap((n) => [`mcp__${n}__createTask`, `mcp__${n}__createLoop`]).join(","),
|
|
118589
|
+
"--add-dir",
|
|
118590
|
+
dir
|
|
118591
|
+
];
|
|
118592
|
+
if (maxBudget !== void 0) {
|
|
118593
|
+
args.push("--max-budget-usd", String(maxBudget));
|
|
118594
|
+
}
|
|
118595
|
+
if (opts.sessionId) {
|
|
118596
|
+
args.push("--session-id", opts.sessionId);
|
|
118597
|
+
}
|
|
118598
|
+
args.push(
|
|
118599
|
+
// Eager-load MCP tools: with tool search enabled, MCP tools (including Ametyst's) are
|
|
118600
|
+
// deferred behind a ToolSearch step that smaller orchestrator models (e.g. Haiku) never
|
|
118601
|
+
// perform — the run ends its turn without the tools (BUG-15). Eager loading is safe for
|
|
118602
|
+
// all models, so this is universal rather than model-gated.
|
|
118603
|
+
"--settings",
|
|
118604
|
+
'{"env":{"ENABLE_TOOL_SEARCH":"false"}}'
|
|
118605
|
+
);
|
|
118606
|
+
return {
|
|
118607
|
+
cmd: "claude",
|
|
118608
|
+
args,
|
|
118609
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
118610
|
+
env: {
|
|
118611
|
+
...deriveGitIdentityEnv(env, deps.readGitConfig ?? readGlobalGitConfig),
|
|
118612
|
+
// Task memory is addressed by SLUG, and the fire context carries no task
|
|
118613
|
+
// identity today — which is why the taskMemory* tools take an explicit
|
|
118614
|
+
// `taskSlug`. Exporting it here is the convenience half: the prompt names
|
|
118615
|
+
// the slug literally, and this lets any shell step in the task reach the
|
|
118616
|
+
// same value without re-deriving it. Env only, never argv — the launch
|
|
118617
|
+
// argv is pinned byte-for-byte by launch.test.ts, and widening it would
|
|
118618
|
+
// be a change to the command rather than to the child's environment.
|
|
118619
|
+
// Exported under BOTH spellings for this version: a task SKILL written against an
|
|
118620
|
+
// older cli may still read `AMETYST_LOOP_SLUG`, and a fire must not lose its memory
|
|
118621
|
+
// address because the launcher was upgraded underneath it.
|
|
118622
|
+
...slug ? { [TASK_ENV.SLUG]: slug, [LEGACY_TASK_ENV.SLUG]: slug } : {},
|
|
118623
|
+
// The user's `--input` text, env only for the same reason as the slug above. Only when
|
|
118624
|
+
// set: a run without arguments must not export an empty variable the SKILL could mistake
|
|
118625
|
+
// for "the user said nothing" when the truth is "nobody asked".
|
|
118626
|
+
...launchInputBlock(opts.input) ? { [TASK_INPUT_ENV]: opts.input } : {}
|
|
118627
|
+
}
|
|
118628
|
+
};
|
|
118629
|
+
}
|
|
118630
|
+
function resolveMaxBudgetUsd(opts, env = process.env) {
|
|
118631
|
+
if (opts.maxBudgetUsd !== void 0) return opts.maxBudgetUsd;
|
|
118632
|
+
const raw = readTaskEnv("MAX_BUDGET_USD", env);
|
|
118633
|
+
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
118634
|
+
const n = Number(raw);
|
|
118635
|
+
return Number.isFinite(n) ? n : void 0;
|
|
118636
|
+
}
|
|
118637
|
+
|
|
118638
|
+
// src/mcp-server/task-run-mode.ts
|
|
118639
|
+
init_esm_shims();
|
|
118640
|
+
var ACCEPTED_RUN_MODES = ["in-chat", "headless"];
|
|
118641
|
+
var FRONTMATTER_SCAN_LIMIT = 8192;
|
|
118642
|
+
function normalizeRunMode(raw) {
|
|
118643
|
+
if (typeof raw !== "string") return void 0;
|
|
118644
|
+
const v = raw.trim().toLowerCase();
|
|
118645
|
+
if (v === "headless") return "headless";
|
|
118646
|
+
if (v === "in-chat" || v === "inchat" || v === "in_chat") return "in-chat";
|
|
118647
|
+
return void 0;
|
|
118648
|
+
}
|
|
118649
|
+
function describeProvided(raw) {
|
|
118650
|
+
if (typeof raw === "string") return raw.trim();
|
|
118651
|
+
try {
|
|
118652
|
+
return JSON.stringify(raw) ?? String(raw);
|
|
118653
|
+
} catch {
|
|
118654
|
+
return String(raw);
|
|
118655
|
+
}
|
|
118656
|
+
}
|
|
118657
|
+
function classifyRunModeArgument(raw) {
|
|
118658
|
+
if (raw === void 0 || raw === null) return { kind: "omitted" };
|
|
118659
|
+
if (typeof raw === "string" && raw.trim() === "") return { kind: "omitted" };
|
|
118660
|
+
const mode2 = normalizeRunMode(raw);
|
|
118661
|
+
if (mode2) return { kind: "valid", mode: mode2 };
|
|
118662
|
+
return { kind: "invalid", provided: describeProvided(raw) };
|
|
118663
|
+
}
|
|
118664
|
+
function unquoteScalar(raw) {
|
|
118665
|
+
let v = raw.trim();
|
|
118666
|
+
const comment = v.match(/(?:^|\s)#.*$/);
|
|
118667
|
+
if (comment) v = v.slice(0, comment.index === 0 ? 0 : comment.index).trim();
|
|
118668
|
+
if (v.length >= 2 && (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))) {
|
|
118669
|
+
v = v.slice(1, -1).trim();
|
|
118670
|
+
}
|
|
118671
|
+
return v;
|
|
118672
|
+
}
|
|
118673
|
+
function parseDefaultRunMode(body) {
|
|
118674
|
+
if (typeof body !== "string" || !body) return void 0;
|
|
118675
|
+
const head = body.replace(/^\uFEFF/, "").slice(0, FRONTMATTER_SCAN_LIMIT);
|
|
118676
|
+
const opener = head.match(/^---[ \t]*\r?\n/);
|
|
118677
|
+
if (!opener) return void 0;
|
|
118678
|
+
const rest = head.slice(opener[0].length);
|
|
118679
|
+
const closer = rest.search(/^(?:---|\.\.\.)[ \t]*(?:\r?\n|$)/m);
|
|
118680
|
+
if (closer < 0) return void 0;
|
|
118681
|
+
const block = rest.slice(0, closer);
|
|
118682
|
+
const hit = block.match(/^defaultRunMode[ \t]*:[ \t]*(.*)$/m);
|
|
118683
|
+
if (!hit) return void 0;
|
|
118684
|
+
return normalizeRunMode(unquoteScalar(hit[1] ?? ""));
|
|
118685
|
+
}
|
|
118686
|
+
function resolveRunMode(explicit, body) {
|
|
118687
|
+
const arg = classifyRunModeArgument(explicit);
|
|
118688
|
+
if (arg.kind === "invalid") {
|
|
118689
|
+
return { ok: false, error: "invalid_mode", provided: arg.provided, accepted: ACCEPTED_RUN_MODES };
|
|
118690
|
+
}
|
|
118691
|
+
if (arg.kind === "valid") return { ok: true, mode: arg.mode, source: "explicit" };
|
|
118692
|
+
const fromBody = parseDefaultRunMode(body);
|
|
118693
|
+
if (fromBody) return { ok: true, mode: fromBody, source: "frontmatter" };
|
|
118694
|
+
return { ok: true, mode: "in-chat", source: "default" };
|
|
118695
|
+
}
|
|
118696
|
+
|
|
118697
|
+
// src/tasks/estimate.ts
|
|
118698
|
+
init_esm_shims();
|
|
118699
|
+
function estimateBlastRadius(task) {
|
|
118700
|
+
const g = task.graphJson ?? {};
|
|
118701
|
+
const nodes = Array.isArray(g.nodes) ? g.nodes : [];
|
|
118702
|
+
const steps = nodes.length;
|
|
118703
|
+
const paidSteps = nodes.filter(
|
|
118704
|
+
(n) => n?.type === "spend" || n?.data?.paid === true || n?.paid === true
|
|
118705
|
+
).length;
|
|
118706
|
+
const costHints = nodes.map((n) => Number(n?.data?.estCostEur ?? n?.estCostEur)).filter((x) => !Number.isNaN(x));
|
|
118707
|
+
const estCostEur = costHints.length ? costHints.reduce((a, b) => a + b, 0) : null;
|
|
118708
|
+
return { steps, paidSteps, estCostEur };
|
|
118709
|
+
}
|
|
118710
|
+
|
|
118711
|
+
// src/tasks/dashboard-template.ts
|
|
118712
|
+
init_esm_shims();
|
|
118713
|
+
var DEFAULT_DASHBOARD_FILES = [
|
|
118714
|
+
"VISION.md",
|
|
118715
|
+
"CONSTRAINTS.md",
|
|
118716
|
+
"QUEUE.md",
|
|
118717
|
+
"STATUS.md",
|
|
118718
|
+
"README.md",
|
|
118719
|
+
"rounds.jsonl"
|
|
118720
|
+
];
|
|
118721
|
+
function parseProcessSpec(manifest) {
|
|
118722
|
+
if (typeof manifest !== "string" || !manifest.trim()) return null;
|
|
118723
|
+
let parsed;
|
|
118724
|
+
try {
|
|
118725
|
+
parsed = JSON.parse(manifest);
|
|
118726
|
+
} catch {
|
|
118727
|
+
return null;
|
|
118728
|
+
}
|
|
118729
|
+
const raw = parsed?.processSpec;
|
|
118730
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
118731
|
+
const spec = raw;
|
|
118732
|
+
const stages = (Array.isArray(spec.stages) ? spec.stages : []).map((s) => {
|
|
118733
|
+
if (typeof s === "string" && s.trim()) return { label: s.trim() };
|
|
118734
|
+
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
118735
|
+
const o = s;
|
|
118736
|
+
if (typeof o.label === "string" && o.label.trim()) {
|
|
118737
|
+
return {
|
|
118738
|
+
label: o.label.trim(),
|
|
118739
|
+
...typeof o.id === "string" && o.id.trim() ? { id: o.id.trim() } : {},
|
|
118740
|
+
...typeof o.detail === "string" && o.detail.trim() ? { detail: o.detail.trim() } : {}
|
|
118741
|
+
};
|
|
118742
|
+
}
|
|
118743
|
+
}
|
|
118744
|
+
return null;
|
|
118745
|
+
}).filter((s) => s !== null);
|
|
118746
|
+
const strings = (v) => (Array.isArray(v) ? v : []).filter((x) => typeof x === "string" && x.trim() !== "");
|
|
118747
|
+
const out = {
|
|
118748
|
+
stages,
|
|
118749
|
+
inputs: strings(spec.inputs),
|
|
118750
|
+
outputs: strings(spec.outputs),
|
|
118751
|
+
...typeof spec.title === "string" && spec.title.trim() ? { title: spec.title.trim() } : {}
|
|
118752
|
+
};
|
|
118753
|
+
return stages.length || out.inputs.length || out.outputs.length ? out : null;
|
|
118754
|
+
}
|
|
118755
|
+
function defaultDashboardManifest() {
|
|
118756
|
+
return JSON.stringify({ files: [...DEFAULT_DASHBOARD_FILES] });
|
|
118757
|
+
}
|
|
118758
|
+
function embedJson(value2) {
|
|
118759
|
+
return JSON.stringify(value2).replace(/</g, "\\u003c");
|
|
118760
|
+
}
|
|
118761
|
+
function escapeHtml(s) {
|
|
118762
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
118763
|
+
}
|
|
118764
|
+
function renderTaskDashboardTemplate(args) {
|
|
118765
|
+
const title = escapeHtml(args.processSpec?.title ?? args.slug);
|
|
118766
|
+
const seed = embedJson({
|
|
118767
|
+
slug: args.slug,
|
|
118768
|
+
descriptionShort: args.descriptionShort ?? "",
|
|
118769
|
+
processSpec: args.processSpec ?? null
|
|
118770
|
+
});
|
|
118771
|
+
return `<!doctype html>
|
|
118772
|
+
<html lang="en">
|
|
118773
|
+
<head>
|
|
118774
|
+
<meta charset="utf-8">
|
|
118775
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
118776
|
+
<title>${title} \u2014 task dashboard</title>
|
|
118777
|
+
<style>
|
|
118778
|
+
:root { --bg:#f7f7fb; --panel:#fff; --border:#e3e3ee; --muted:#6b6b80; --accent:#5b5bd6; --ok:#1a7f37; --bad:#b42318; }
|
|
118779
|
+
* { box-sizing: border-box; }
|
|
118780
|
+
body { margin:0; font:14px/1.5 -apple-system, "Segoe UI", Roboto, sans-serif; background:var(--bg); color:#1d1d2b; padding:24px; }
|
|
118781
|
+
h1 { font-size:20px; margin:0 0 4px; }
|
|
118782
|
+
h2 { font-size:14px; margin:24px 0 8px; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); }
|
|
118783
|
+
.sub { color:var(--muted); margin:0 0 16px; }
|
|
118784
|
+
.panel { background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:16px; }
|
|
118785
|
+
.map { display:flex; flex-wrap:wrap; align-items:stretch; gap:8px; }
|
|
118786
|
+
.stage { background:var(--panel); border:1px solid var(--border); border-left:3px solid var(--accent); border-radius:10px; padding:10px 14px; min-width:140px; flex:1; }
|
|
118787
|
+
.stage .label { font-weight:600; }
|
|
118788
|
+
.stage .detail { color:var(--muted); font-size:12px; margin-top:2px; }
|
|
118789
|
+
.arrow { align-self:center; color:var(--muted); }
|
|
118790
|
+
.io { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
|
|
118791
|
+
ul { margin:6px 0 0; padding-left:18px; }
|
|
118792
|
+
table { width:100%; border-collapse:collapse; background:var(--panel); border:1px solid var(--border); border-radius:12px; overflow:hidden; }
|
|
118793
|
+
th, td { text-align:left; padding:8px 12px; border-top:1px solid var(--border); font-size:13px; vertical-align:top; }
|
|
118794
|
+
thead th { border-top:none; background:var(--bg); color:var(--muted); font-weight:600; }
|
|
118795
|
+
.ok { color:var(--ok); } .bad { color:var(--bad); }
|
|
118796
|
+
details { background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:10px 14px; margin-bottom:8px; }
|
|
118797
|
+
summary { cursor:pointer; font-weight:600; }
|
|
118798
|
+
pre { overflow-x:auto; font-size:12px; background:var(--bg); border-radius:8px; padding:10px; }
|
|
118799
|
+
.empty { color:var(--muted); font-style:italic; }
|
|
118800
|
+
#live { font-size:12px; color:var(--muted); float:right; }
|
|
118801
|
+
</style>
|
|
118802
|
+
</head>
|
|
118803
|
+
<body>
|
|
118804
|
+
<span id="live">loading\u2026</span>
|
|
118805
|
+
<h1 id="title"></h1>
|
|
118806
|
+
<p class="sub" id="desc"></p>
|
|
118807
|
+
|
|
118808
|
+
<h2>Process</h2>
|
|
118809
|
+
<div id="map" class="map panel"></div>
|
|
118810
|
+
|
|
118811
|
+
<h2>Inputs & outputs</h2>
|
|
118812
|
+
<div class="io">
|
|
118813
|
+
<div class="panel"><strong>Inputs</strong><ul id="inputs"></ul></div>
|
|
118814
|
+
<div class="panel"><strong>Outputs</strong><ul id="outputs"></ul></div>
|
|
118815
|
+
</div>
|
|
118816
|
+
|
|
118817
|
+
<h2>Rounds</h2>
|
|
118818
|
+
<div id="rounds"></div>
|
|
118819
|
+
|
|
118820
|
+
<h2>Files</h2>
|
|
118821
|
+
<div id="files"></div>
|
|
118822
|
+
|
|
118823
|
+
<script type="application/json" id="loop-seed">${seed}</script>
|
|
118824
|
+
<script>
|
|
118825
|
+
(function () {
|
|
118826
|
+
"use strict";
|
|
118827
|
+
var seed = JSON.parse(document.getElementById("loop-seed").textContent);
|
|
118828
|
+
document.getElementById("title").textContent = (seed.processSpec && seed.processSpec.title) || seed.slug;
|
|
118829
|
+
document.getElementById("desc").textContent = seed.descriptionShort || "";
|
|
118830
|
+
document.title = seed.slug + " \u2014 task dashboard";
|
|
118831
|
+
|
|
118832
|
+
function el(tag, cls, text) {
|
|
118833
|
+
var e = document.createElement(tag);
|
|
118834
|
+
if (cls) e.className = cls;
|
|
118835
|
+
if (text !== undefined) e.textContent = text;
|
|
118836
|
+
return e;
|
|
118837
|
+
}
|
|
118838
|
+
|
|
118839
|
+
// \u2500\u2500 Process map + IO from the embedded spec (static \u2014 rendered once). \u2500\u2500
|
|
118840
|
+
var map = document.getElementById("map");
|
|
118841
|
+
var spec = seed.processSpec;
|
|
118842
|
+
if (spec && spec.stages && spec.stages.length) {
|
|
118843
|
+
spec.stages.forEach(function (s, i) {
|
|
118844
|
+
if (i > 0) map.appendChild(el("div", "arrow", "\\u2192"));
|
|
118845
|
+
var box = el("div", "stage");
|
|
118846
|
+
box.appendChild(el("div", "label", s.label));
|
|
118847
|
+
if (s.detail) box.appendChild(el("div", "detail", s.detail));
|
|
118848
|
+
map.appendChild(box);
|
|
118849
|
+
});
|
|
118850
|
+
} else {
|
|
118851
|
+
map.appendChild(el("span", "empty", "No process spec on this task \\u2014 live files and rounds below."));
|
|
118852
|
+
}
|
|
118853
|
+
function fillList(id, items) {
|
|
118854
|
+
var ul = document.getElementById(id);
|
|
118855
|
+
ul.textContent = "";
|
|
118856
|
+
if (!items || !items.length) { ul.appendChild(el("li", "empty", "\\u2014")); return; }
|
|
118857
|
+
items.forEach(function (x) { ul.appendChild(el("li", null, x)); });
|
|
118858
|
+
}
|
|
118859
|
+
fillList("inputs", spec && spec.inputs);
|
|
118860
|
+
fillList("outputs", spec && spec.outputs);
|
|
118861
|
+
|
|
118862
|
+
// \u2500\u2500 Live data: rounds + files, refreshed from GET /data. \u2500\u2500
|
|
118863
|
+
function parseJsonl(text) {
|
|
118864
|
+
var rows = [];
|
|
118865
|
+
(text || "").split("\\n").forEach(function (line) {
|
|
118866
|
+
line = line.trim();
|
|
118867
|
+
if (!line) return;
|
|
118868
|
+
try { rows.push(JSON.parse(line)); } catch (e) { /* skip malformed line */ }
|
|
118869
|
+
});
|
|
118870
|
+
return rows;
|
|
118871
|
+
}
|
|
118872
|
+
|
|
118873
|
+
function renderRounds(fires, rounds) {
|
|
118874
|
+
var host = document.getElementById("rounds");
|
|
118875
|
+
host.textContent = "";
|
|
118876
|
+
if (!fires.length && !rounds.length) {
|
|
118877
|
+
var p = el("div", "panel"); p.appendChild(el("span", "empty", "No rounds yet \\u2014 this fills in as the task runs."));
|
|
118878
|
+
host.appendChild(p);
|
|
118879
|
+
return;
|
|
118880
|
+
}
|
|
118881
|
+
var table = document.createElement("table");
|
|
118882
|
+
var thead = document.createElement("thead");
|
|
118883
|
+
var hr = document.createElement("tr");
|
|
118884
|
+
["when", "duration", "turns", "tokens", "exit", "round detail (rounds.jsonl)"].forEach(function (h) {
|
|
118885
|
+
hr.appendChild(el("th", null, h));
|
|
118886
|
+
});
|
|
118887
|
+
thead.appendChild(hr); table.appendChild(thead);
|
|
118888
|
+
var tbody = document.createElement("tbody");
|
|
118889
|
+
var n = Math.max(fires.length, rounds.length);
|
|
118890
|
+
for (var i = n - 1; i >= 0; i--) { // newest first
|
|
118891
|
+
var f = fires[i] || {};
|
|
118892
|
+
var r = rounds[i];
|
|
118893
|
+
var tr = document.createElement("tr");
|
|
118894
|
+
tr.appendChild(el("td", null, f.ts || (r && r.ts) || "\\u2014"));
|
|
118895
|
+
tr.appendChild(el("td", null, f.duration_s != null ? f.duration_s + "s" : "\\u2014"));
|
|
118896
|
+
tr.appendChild(el("td", null, f.turns != null ? String(f.turns) : "\\u2014"));
|
|
118897
|
+
tr.appendChild(el("td", null, f.tokens_total != null ? Number(f.tokens_total).toLocaleString() : "\\u2014"));
|
|
118898
|
+
tr.appendChild(el("td", f.exit === 0 ? "ok" : f.exit != null ? "bad" : null, f.exit != null ? String(f.exit) : "\\u2014"));
|
|
118899
|
+
var detail = el("td");
|
|
118900
|
+
if (f.launch_failed) { detail.textContent = "launch failed \\u2014 " + (f.reason || "unknown reason"); }
|
|
118901
|
+
else if (r) { var pre = document.createElement("pre"); pre.textContent = JSON.stringify(r, null, 1); detail.appendChild(pre); }
|
|
118902
|
+
else detail.textContent = "\\u2014";
|
|
118903
|
+
tr.appendChild(detail);
|
|
118904
|
+
tbody.appendChild(tr);
|
|
118905
|
+
}
|
|
118906
|
+
table.appendChild(tbody);
|
|
118907
|
+
host.appendChild(table);
|
|
118908
|
+
}
|
|
118909
|
+
|
|
118910
|
+
function renderFiles(files) {
|
|
118911
|
+
var host = document.getElementById("files");
|
|
118912
|
+
host.textContent = "";
|
|
118913
|
+
var names = Object.keys(files || {}).filter(function (n) { return n !== "rounds.jsonl"; });
|
|
118914
|
+
if (!names.length) { var p = el("div", "panel"); p.appendChild(el("span", "empty", "No files surfaced by the manifest.")); host.appendChild(p); return; }
|
|
118915
|
+
names.forEach(function (name) {
|
|
118916
|
+
var d = document.createElement("details");
|
|
118917
|
+
d.appendChild(el("summary", null, name));
|
|
118918
|
+
var pre = document.createElement("pre");
|
|
118919
|
+
pre.textContent = files[name];
|
|
118920
|
+
d.appendChild(pre);
|
|
118921
|
+
host.appendChild(d);
|
|
118922
|
+
});
|
|
118923
|
+
}
|
|
118924
|
+
|
|
118925
|
+
function refresh() {
|
|
118926
|
+
fetch("/data").then(function (res) { return res.json(); }).then(function (data) {
|
|
118927
|
+
var fires = parseJsonl(data.state && data.state["fires.jsonl"]);
|
|
118928
|
+
var rounds = parseJsonl(data.files && data.files["rounds.jsonl"]);
|
|
118929
|
+
renderRounds(fires, rounds);
|
|
118930
|
+
renderFiles(data.files);
|
|
118931
|
+
document.getElementById("live").textContent = "updated " + new Date().toLocaleTimeString();
|
|
118932
|
+
}).catch(function () {
|
|
118933
|
+
document.getElementById("live").textContent = "server stopped";
|
|
118934
|
+
});
|
|
118935
|
+
}
|
|
118936
|
+
refresh();
|
|
118937
|
+
setInterval(refresh, 5000);
|
|
118938
|
+
})();
|
|
118939
|
+
</script>
|
|
118940
|
+
</body>
|
|
118941
|
+
</html>
|
|
118942
|
+
`;
|
|
118943
|
+
}
|
|
118944
|
+
function injectDefaultDashboard(task) {
|
|
118945
|
+
const hasHtml = typeof task.dashboardHtml === "string" && task.dashboardHtml.trim() !== "";
|
|
118946
|
+
if (hasHtml) return;
|
|
118947
|
+
if (task.dashboardManifest && typeof task.dashboardManifest === "object") {
|
|
118948
|
+
try {
|
|
118949
|
+
task.dashboardManifest = JSON.stringify(task.dashboardManifest);
|
|
118950
|
+
} catch {
|
|
118951
|
+
}
|
|
118952
|
+
}
|
|
118953
|
+
const manifest = typeof task.dashboardManifest === "string" ? task.dashboardManifest : void 0;
|
|
118954
|
+
task.dashboardHtml = renderTaskDashboardTemplate({
|
|
118955
|
+
slug: typeof task.slug === "string" ? task.slug : "task",
|
|
118956
|
+
descriptionShort: typeof task.descriptionShort === "string" ? task.descriptionShort : void 0,
|
|
118957
|
+
processSpec: parseProcessSpec(manifest)
|
|
118958
|
+
});
|
|
118959
|
+
if (!manifest || !manifest.trim()) task.dashboardManifest = defaultDashboardManifest();
|
|
118960
|
+
}
|
|
118961
|
+
|
|
118677
118962
|
// src/mcp-server/memory-scope.ts
|
|
118678
118963
|
init_esm_shims();
|
|
118679
118964
|
var MEMORY_SCOPE_EXPLANATION = "Global memory: everyone who uses this task (when it is shared) reads and writes the same copy \u2014 edits can conflict and build up over time. Per-member memory: each person who uses it gets their own copy. For a private task the difference doesn't matter. If unsure, choose per-member to avoid conflicts. Examples: a product PBI someone picks up and leaves half-done \u2192 per member; competitor signals the whole workspace should see \u2192 global.";
|
|
@@ -118700,51 +118985,41 @@ init_esm_shims();
|
|
|
118700
118985
|
|
|
118701
118986
|
// src/tasks/task-run-section.ts
|
|
118702
118987
|
init_esm_shims();
|
|
118703
|
-
var
|
|
118704
|
-
function
|
|
118705
|
-
return kind === "loop" ? "Never pick a default silently; a loop armed on a schedule the user did not choose keeps costing money and merging code on its own." : "Never pick a default silently; a compound armed on a schedule the user did not choose keeps costing money on its own.";
|
|
118706
|
-
}
|
|
118707
|
-
function defaultModeHighlight(kind, mode2) {
|
|
118988
|
+
var CADENCE_WARNING = "Never pick a default silently; a task armed on a schedule the user did not choose keeps costing money on its own.";
|
|
118989
|
+
function defaultModeHighlight(mode2) {
|
|
118708
118990
|
if (!mode2) return "";
|
|
118709
|
-
if (kind === "loop" && mode2 === "in-chat") {
|
|
118710
|
-
return `
|
|
118711
|
-
The author's \`defaultRunMode: in-chat\` cannot apply to a loop (see option 1) \u2014 ask anyway, and mention that its stated default is unavailable.
|
|
118712
|
-
`;
|
|
118713
|
-
}
|
|
118714
118991
|
return `
|
|
118715
118992
|
The author suggests \`${mode2}\` (\`defaultRunMode:\` in the task's frontmatter). Highlight it when you ask \u2014 it is a suggestion, never a reason to skip the question.
|
|
118716
118993
|
`;
|
|
118717
118994
|
}
|
|
118718
|
-
function taskRunSection(slug,
|
|
118719
|
-
const inChat = kind === "loop" ? LOOP_IN_CHAT_EXCLUDED : `call the \`runTask\` MCP tool with task="${slug}" and mode="in-chat" (present the match and get confirmation first, per the getTask stop-gate convention).`;
|
|
118995
|
+
function taskRunSection(slug, opts = {}) {
|
|
118720
118996
|
return `Before running it, ASK the user HOW to run it, and wait for an answer \u2014 always ask, never pick one silently:
|
|
118721
118997
|
|
|
118722
|
-
1. **one-time, in this chat, followed here** \u2014 ${
|
|
118998
|
+
1. **one-time, in this chat, followed here** \u2014 call the \`runTask\` MCP tool with task="${slug}" and mode="in-chat" (present the match and get confirmation first, per the getTask stop-gate convention).
|
|
118723
118999
|
2. **one-time, headless in its own process** \u2014 \`ametyst task run ${slug}\`
|
|
118724
|
-
3. **scheduled, recurring** \u2014 \`ametyst task schedule ${slug} --every <dur>\` (e.g. \`--every 30m\`, \`--every 1h\`), or \`ametyst task schedule ${slug} --at HH:MM\` for a fixed daily time. ASK which cadence. ${
|
|
118725
|
-
${defaultModeHighlight(
|
|
119000
|
+
3. **scheduled, recurring** \u2014 \`ametyst task schedule ${slug} --every <dur>\` (e.g. \`--every 30m\`, \`--every 1h\`), or \`ametyst task schedule ${slug} --at HH:MM\` for a fixed daily time. ASK which cadence. ${CADENCE_WARNING}
|
|
119001
|
+
${defaultModeHighlight(opts.defaultRunMode)}
|
|
118726
119002
|
Stop or inspect a schedule: \`ametyst task unschedule ${slug}\` \xB7 \`ametyst task schedules\`.
|
|
118727
119003
|
To READ it without executing: \`ametyst task show ${slug}\`.
|
|
118728
119004
|
`;
|
|
118729
119005
|
}
|
|
118730
119006
|
|
|
118731
119007
|
// src/mcp-server/prompt-descriptors.ts
|
|
118732
|
-
function promptName(
|
|
118733
|
-
return
|
|
119008
|
+
function promptName(slug) {
|
|
119009
|
+
return `task__${slug}`;
|
|
118734
119010
|
}
|
|
118735
|
-
function buildPromptEntry(
|
|
119011
|
+
function buildPromptEntry(summary) {
|
|
118736
119012
|
const cat = summary.category ? ` [${summary.category}]` : "";
|
|
118737
|
-
const label2 = kind === "compound" ? "Compound skill" : "Loop";
|
|
118738
119013
|
return {
|
|
118739
|
-
name: promptName(
|
|
118740
|
-
description:
|
|
119014
|
+
name: promptName(summary.slug),
|
|
119015
|
+
description: `Task: ${summary.descriptionShort ?? summary.slug}${cat}`
|
|
118741
119016
|
};
|
|
118742
119017
|
}
|
|
118743
|
-
function buildPromptContent(
|
|
118744
|
-
const text = `This is the
|
|
119018
|
+
function buildPromptContent(full) {
|
|
119019
|
+
const text = `This is the task "${full.slug}".
|
|
118745
119020
|
|
|
118746
|
-
${taskRunSection(full.slug,
|
|
118747
|
-
---
|
|
119021
|
+
${taskRunSection(full.slug, { defaultRunMode: parseDefaultRunMode(full.markdownBody) })}
|
|
119022
|
+
--- task body ---
|
|
118748
119023
|
${full.markdownBody ?? ""}`;
|
|
118749
119024
|
return { messages: [{ role: "user", content: { type: "text", text } }] };
|
|
118750
119025
|
}
|
|
@@ -118772,9 +119047,9 @@ function resolveBodyFromInlineOrFile(inline, filePath) {
|
|
|
118772
119047
|
return void 0;
|
|
118773
119048
|
}
|
|
118774
119049
|
|
|
118775
|
-
// src/mcp-server/
|
|
119050
|
+
// src/mcp-server/task-upsert-summary.ts
|
|
118776
119051
|
init_esm_shims();
|
|
118777
|
-
var
|
|
119052
|
+
var TASK_CONTENT_FIELDS = [
|
|
118778
119053
|
"markdownBody",
|
|
118779
119054
|
"visionMd",
|
|
118780
119055
|
"constraintsMd",
|
|
@@ -118877,7 +119152,7 @@ var BRANCH_MATCHERS = [
|
|
|
118877
119152
|
];
|
|
118878
119153
|
function allLinesOf(content) {
|
|
118879
119154
|
const out = [];
|
|
118880
|
-
for (const f of
|
|
119155
|
+
for (const f of TASK_CONTENT_FIELDS) {
|
|
118881
119156
|
const v = content[f];
|
|
118882
119157
|
if (typeof v === "string" && v) out.push(...v.split(/\r?\n/));
|
|
118883
119158
|
}
|
|
@@ -118886,7 +119161,7 @@ function allLinesOf(content) {
|
|
|
118886
119161
|
function mergeEffective(sent, previous) {
|
|
118887
119162
|
const base2 = previous && previous.available ? { ...previous.content } : {};
|
|
118888
119163
|
const provenance = [];
|
|
118889
|
-
const keys = [...
|
|
119164
|
+
const keys = [...TASK_CONTENT_FIELDS, "descriptionShort"];
|
|
118890
119165
|
const content = { ...base2 };
|
|
118891
119166
|
for (const k of keys) {
|
|
118892
119167
|
const sentVal = sent[k];
|
|
@@ -118949,7 +119224,7 @@ function buildReceipt(input) {
|
|
|
118949
119224
|
if (prevVal === void 0) return "changed";
|
|
118950
119225
|
return String(sentVal) === String(prevVal) ? "unchanged" : "changed";
|
|
118951
119226
|
};
|
|
118952
|
-
for (const f of
|
|
119227
|
+
for (const f of TASK_CONTENT_FIELDS) {
|
|
118953
119228
|
const sentVal = sent[f];
|
|
118954
119229
|
const wasSent = typeof sentVal === "string" && sentVal.length > 0;
|
|
118955
119230
|
if (!wasSent && mode2 === "created") continue;
|
|
@@ -118989,7 +119264,7 @@ function deriveChanges(input, receipt) {
|
|
|
118989
119264
|
if (mode2 === "created") {
|
|
118990
119265
|
return {
|
|
118991
119266
|
title: "WHAT CHANGED",
|
|
118992
|
-
lines: ["new
|
|
119267
|
+
lines: ["new task \u2014 there is no previous version to compare against"]
|
|
118993
119268
|
};
|
|
118994
119269
|
}
|
|
118995
119270
|
if (!previous || !previous.available) {
|
|
@@ -119022,7 +119297,7 @@ function deriveChanges(input, receipt) {
|
|
|
119022
119297
|
function renderSections(sections2) {
|
|
119023
119298
|
return sections2.map((s) => [s.title, ...s.lines.map((l) => ` - ${l}`)].join("\n")).join("\n\n");
|
|
119024
119299
|
}
|
|
119025
|
-
function
|
|
119300
|
+
function buildTaskUpsertSummary(input) {
|
|
119026
119301
|
const { content, provenance } = mergeEffective(input.sent, input.previous);
|
|
119027
119302
|
const sections2 = deriveNarrative(content);
|
|
119028
119303
|
const receipt = buildReceipt(input);
|
|
@@ -119045,9 +119320,9 @@ var IDENTITY_KEYS = [
|
|
|
119045
119320
|
"createdAt",
|
|
119046
119321
|
"updatedAt"
|
|
119047
119322
|
];
|
|
119048
|
-
function
|
|
119049
|
-
if (!
|
|
119050
|
-
const src =
|
|
119323
|
+
function projectTaskIdentity(task) {
|
|
119324
|
+
if (!task || typeof task !== "object") return {};
|
|
119325
|
+
const src = task;
|
|
119051
119326
|
const out = {};
|
|
119052
119327
|
for (const k of IDENTITY_KEYS) {
|
|
119053
119328
|
const v = src[k];
|
|
@@ -119424,12 +119699,12 @@ async function openBridgeConnection(socketPath, deps = {}) {
|
|
|
119424
119699
|
} catch {
|
|
119425
119700
|
return null;
|
|
119426
119701
|
}
|
|
119427
|
-
const opened = await new Promise((
|
|
119702
|
+
const opened = await new Promise((resolve3) => {
|
|
119428
119703
|
let settled = false;
|
|
119429
119704
|
const done = (ok) => {
|
|
119430
119705
|
if (settled) return;
|
|
119431
119706
|
settled = true;
|
|
119432
|
-
|
|
119707
|
+
resolve3(ok);
|
|
119433
119708
|
};
|
|
119434
119709
|
socket.once("connect", () => done(true));
|
|
119435
119710
|
socket.once("error", () => done(false));
|
|
@@ -119449,17 +119724,17 @@ async function openBridgeConnection(socketPath, deps = {}) {
|
|
|
119449
119724
|
for (const frame of frames) {
|
|
119450
119725
|
const res = parseBridgeResponse(frame);
|
|
119451
119726
|
if (!res) continue;
|
|
119452
|
-
const
|
|
119453
|
-
if (
|
|
119727
|
+
const resolve3 = pending.get(res.id);
|
|
119728
|
+
if (resolve3) {
|
|
119454
119729
|
pending.delete(res.id);
|
|
119455
|
-
|
|
119730
|
+
resolve3(res);
|
|
119456
119731
|
}
|
|
119457
119732
|
}
|
|
119458
119733
|
});
|
|
119459
119734
|
const failAll = (error) => {
|
|
119460
119735
|
closed = true;
|
|
119461
|
-
for (const [id,
|
|
119462
|
-
|
|
119736
|
+
for (const [id, resolve3] of pending) {
|
|
119737
|
+
resolve3({ v: BRIDGE_PROTOCOL_VERSION, id, ok: false, error });
|
|
119463
119738
|
}
|
|
119464
119739
|
pending.clear();
|
|
119465
119740
|
};
|
|
@@ -119470,10 +119745,10 @@ async function openBridgeConnection(socketPath, deps = {}) {
|
|
|
119470
119745
|
if (closed) {
|
|
119471
119746
|
return Promise.resolve({ v: BRIDGE_PROTOCOL_VERSION, id, ok: false, error: "bridge_closed" });
|
|
119472
119747
|
}
|
|
119473
|
-
return new Promise((
|
|
119748
|
+
return new Promise((resolve3) => {
|
|
119474
119749
|
const timer = setTimeout(() => {
|
|
119475
119750
|
pending.delete(id);
|
|
119476
|
-
|
|
119751
|
+
resolve3({
|
|
119477
119752
|
v: BRIDGE_PROTOCOL_VERSION,
|
|
119478
119753
|
id,
|
|
119479
119754
|
ok: false,
|
|
@@ -119484,7 +119759,7 @@ async function openBridgeConnection(socketPath, deps = {}) {
|
|
|
119484
119759
|
if (typeof timer.unref === "function") timer.unref();
|
|
119485
119760
|
pending.set(id, (res) => {
|
|
119486
119761
|
clearTimeout(timer);
|
|
119487
|
-
|
|
119762
|
+
resolve3(res);
|
|
119488
119763
|
});
|
|
119489
119764
|
socket.write(encodeFrame({ ...req, v: BRIDGE_PROTOCOL_VERSION, id, clientPid, nonce }));
|
|
119490
119765
|
});
|
|
@@ -119688,7 +119963,7 @@ function createShimHandler(deps) {
|
|
|
119688
119963
|
}
|
|
119689
119964
|
var TOO_LARGE = /* @__PURE__ */ Symbol.for("ametyst.delegate.shim.too-large");
|
|
119690
119965
|
function readBody2(req) {
|
|
119691
|
-
return new Promise((
|
|
119966
|
+
return new Promise((resolve3) => {
|
|
119692
119967
|
let data = "";
|
|
119693
119968
|
let over = false;
|
|
119694
119969
|
req.setEncoding("utf-8");
|
|
@@ -119698,14 +119973,14 @@ function readBody2(req) {
|
|
|
119698
119973
|
if (data.length > MAX_FRAME_BYTES) {
|
|
119699
119974
|
over = true;
|
|
119700
119975
|
data = "";
|
|
119701
|
-
|
|
119976
|
+
resolve3(TOO_LARGE);
|
|
119702
119977
|
}
|
|
119703
119978
|
});
|
|
119704
119979
|
req.on("end", () => {
|
|
119705
|
-
if (!over)
|
|
119980
|
+
if (!over) resolve3(data);
|
|
119706
119981
|
});
|
|
119707
119982
|
req.on("error", () => {
|
|
119708
|
-
if (!over)
|
|
119983
|
+
if (!over) resolve3(data);
|
|
119709
119984
|
});
|
|
119710
119985
|
});
|
|
119711
119986
|
}
|
|
@@ -119717,17 +119992,17 @@ async function startShim(deps) {
|
|
|
119717
119992
|
res.end(JSON.stringify({ error: { message: "shim failure", type: "internal_error" } }));
|
|
119718
119993
|
});
|
|
119719
119994
|
});
|
|
119720
|
-
await new Promise((
|
|
119995
|
+
await new Promise((resolve3, reject) => {
|
|
119721
119996
|
server2.once("error", reject);
|
|
119722
|
-
server2.listen(0, "127.0.0.1", () =>
|
|
119997
|
+
server2.listen(0, "127.0.0.1", () => resolve3());
|
|
119723
119998
|
});
|
|
119724
119999
|
const port = server2.address().port;
|
|
119725
120000
|
return {
|
|
119726
120001
|
port,
|
|
119727
120002
|
baseURL: `http://127.0.0.1:${port}/v1`,
|
|
119728
|
-
close: () => new Promise((
|
|
120003
|
+
close: () => new Promise((resolve3) => {
|
|
119729
120004
|
server2.closeAllConnections?.();
|
|
119730
|
-
server2.close(() =>
|
|
120005
|
+
server2.close(() => resolve3());
|
|
119731
120006
|
})
|
|
119732
120007
|
};
|
|
119733
120008
|
}
|
|
@@ -119830,7 +120105,7 @@ init_paths();
|
|
|
119830
120105
|
import * as nodeFs4 from "fs";
|
|
119831
120106
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
119832
120107
|
import { homedir as homedir8 } from "os";
|
|
119833
|
-
import { basename as
|
|
120108
|
+
import { basename as basename5, join as join14 } from "path";
|
|
119834
120109
|
var DELEGATED_CHILD_ENV_VAR = "AMETYST_DELEGATED";
|
|
119835
120110
|
var SPEND_GRANT_TOKEN_ENV_VAR = "AMETYST_DELEGATE_SPEND_TOKEN";
|
|
119836
120111
|
var SPEND_KILL_SWITCH_ENV_VAR = "AMETYST_DELEGATE_NO_SPEND";
|
|
@@ -119872,7 +120147,7 @@ function defaultProcessTable() {
|
|
|
119872
120147
|
function isOurOpencodeBinary(argv0, home = homedir8()) {
|
|
119873
120148
|
if (!argv0) return false;
|
|
119874
120149
|
if (argv0 === opencodeBinaryPath(home)) return true;
|
|
119875
|
-
return /^opencode-\d/.test(
|
|
120150
|
+
return /^opencode-\d/.test(basename5(argv0));
|
|
119876
120151
|
}
|
|
119877
120152
|
var ancestryMemo;
|
|
119878
120153
|
function classifyAncestry(deps = {}) {
|
|
@@ -120378,25 +120653,26 @@ function killChild(child, signal, detached, kill = (pid, sig) => process.kill(pi
|
|
|
120378
120653
|
function collectStream(child, which, onChunk) {
|
|
120379
120654
|
const stream = child[which];
|
|
120380
120655
|
if (!stream) return Promise.resolve("");
|
|
120381
|
-
return new Promise((
|
|
120656
|
+
return new Promise((resolve3) => {
|
|
120382
120657
|
let data = "";
|
|
120383
120658
|
stream.setEncoding("utf-8");
|
|
120384
120659
|
stream.on("data", (chunk) => {
|
|
120385
120660
|
data += chunk;
|
|
120386
120661
|
onChunk(chunk);
|
|
120387
120662
|
});
|
|
120388
|
-
stream.on("end", () =>
|
|
120389
|
-
stream.on("error", () =>
|
|
120663
|
+
stream.on("end", () => resolve3(data));
|
|
120664
|
+
stream.on("error", () => resolve3(data));
|
|
120390
120665
|
});
|
|
120391
120666
|
}
|
|
120392
120667
|
function waitForExit(child) {
|
|
120393
|
-
return new Promise((
|
|
120394
|
-
child.on("close", (code) =>
|
|
120395
|
-
child.on("error", () =>
|
|
120668
|
+
return new Promise((resolve3) => {
|
|
120669
|
+
child.on("close", (code) => resolve3(code));
|
|
120670
|
+
child.on("error", () => resolve3(1));
|
|
120396
120671
|
});
|
|
120397
120672
|
}
|
|
120398
120673
|
|
|
120399
120674
|
// src/delegate/jobs.ts
|
|
120675
|
+
init_paths();
|
|
120400
120676
|
var DEFAULT_DELEGATE_TIMEOUT_MS = 30 * 6e4;
|
|
120401
120677
|
var MAX_DELEGATE_TIMEOUT_MS = MAX_DELEGATE_DEADLINE_MS;
|
|
120402
120678
|
function resolveDelegateTimeoutMs(env = process.env) {
|
|
@@ -120458,9 +120734,10 @@ function validateStartInput(input, deps = {}) {
|
|
|
120458
120734
|
message: "delegate_start needs `model` \u2014 the model slug the openrouter passthrough should run, e.g. openai/gpt-5-mini."
|
|
120459
120735
|
};
|
|
120460
120736
|
}
|
|
120461
|
-
const
|
|
120737
|
+
const explicitDir = typeof input.dir === "string" && input.dir.trim() ? input.dir.trim() : "";
|
|
120738
|
+
const dir = explicitDir || resolveRunRoot(void 0, { ...deps.runRoot, ...deps.cwd ? { cwd: deps.cwd } : {} }).root;
|
|
120462
120739
|
const isDirectory = deps.isDirectory;
|
|
120463
|
-
if (isDirectory && !isDirectory(dir)) {
|
|
120740
|
+
if (explicitDir && isDirectory && !isDirectory(dir)) {
|
|
120464
120741
|
return {
|
|
120465
120742
|
ok: false,
|
|
120466
120743
|
error: "dir_not_found",
|
|
@@ -120929,23 +121206,23 @@ async function startDelegateBridge(deps) {
|
|
|
120929
121206
|
socket.on("close", () => sockets.delete(socket));
|
|
120930
121207
|
};
|
|
120931
121208
|
const server2 = (deps.createServer ?? ((h) => createNetServer(h)))(onConnection);
|
|
120932
|
-
const listening = await new Promise((
|
|
121209
|
+
const listening = await new Promise((resolve3) => {
|
|
120933
121210
|
server2.once("error", (err) => {
|
|
120934
121211
|
log(`[delegate-bridge] listen failed: ${errText(err)}`);
|
|
120935
|
-
|
|
121212
|
+
resolve3(false);
|
|
120936
121213
|
});
|
|
120937
121214
|
try {
|
|
120938
|
-
server2.listen(socketPath, () =>
|
|
121215
|
+
server2.listen(socketPath, () => resolve3(true));
|
|
120939
121216
|
} catch (err) {
|
|
120940
121217
|
log(`[delegate-bridge] listen threw: ${errText(err)}`);
|
|
120941
|
-
|
|
121218
|
+
resolve3(false);
|
|
120942
121219
|
}
|
|
120943
121220
|
});
|
|
120944
121221
|
if (!listening) return null;
|
|
120945
121222
|
const close = async () => {
|
|
120946
121223
|
for (const s of sockets) s.destroy();
|
|
120947
121224
|
sockets.clear();
|
|
120948
|
-
await new Promise((
|
|
121225
|
+
await new Promise((resolve3) => server2.close(() => resolve3()));
|
|
120949
121226
|
try {
|
|
120950
121227
|
fs.rmSync(socketPath, { force: true });
|
|
120951
121228
|
} catch {
|
|
@@ -121667,7 +121944,7 @@ function registerDelegateTools(deps) {
|
|
|
121667
121944
|
return { registered: [...defs.keys()], parentDeathHook: isProductionStore, mcpServers };
|
|
121668
121945
|
}
|
|
121669
121946
|
|
|
121670
|
-
// src/mcp-server/write-
|
|
121947
|
+
// src/mcp-server/write-task-body.ts
|
|
121671
121948
|
init_esm_shims();
|
|
121672
121949
|
import { tmpdir as tmpdir2 } from "os";
|
|
121673
121950
|
import { join as joinPath } from "path";
|
|
@@ -122152,8 +122429,6 @@ var allowlistCache = null;
|
|
|
122152
122429
|
var capabilityIndexCache = null;
|
|
122153
122430
|
var firstMcpSessionSeen = false;
|
|
122154
122431
|
var firstSessionGuideEmitted = false;
|
|
122155
|
-
var compoundIndexCache = null;
|
|
122156
|
-
var loopIndexCache = null;
|
|
122157
122432
|
var taskIndexCache = null;
|
|
122158
122433
|
var cardCounts = null;
|
|
122159
122434
|
var lastNudgeAtCall = null;
|
|
@@ -122285,8 +122560,6 @@ function dropIdentityScopedCaches() {
|
|
|
122285
122560
|
transactionsCache = null;
|
|
122286
122561
|
allowlistCache = null;
|
|
122287
122562
|
capabilityIndexCache = null;
|
|
122288
|
-
compoundIndexCache = null;
|
|
122289
|
-
loopIndexCache = null;
|
|
122290
122563
|
taskIndexCache = null;
|
|
122291
122564
|
cardCounts = null;
|
|
122292
122565
|
servicesDiscovered = null;
|
|
@@ -122834,7 +123107,7 @@ ${lines}${footer}${buildPersonaProposalSection(index2.persona)}`;
|
|
|
122834
123107
|
}
|
|
122835
123108
|
var INDEX_MAX_CATEGORIES = 12;
|
|
122836
123109
|
var INDEX_MAX_RECENT = 5;
|
|
122837
|
-
function
|
|
123110
|
+
function buildTaskIndexSection(items, noun) {
|
|
122838
123111
|
if (!items || items.length === 0) return "";
|
|
122839
123112
|
const counts = /* @__PURE__ */ new Map();
|
|
122840
123113
|
for (const it of items) {
|
|
@@ -122858,9 +123131,9 @@ Your workspace has ${total} ${noun}${total === 1 ? "" : "s"} across ${catCount}
|
|
|
122858
123131
|
${catLines}${moreCats}${recentLine}
|
|
122859
123132
|
Call with an intent or category to pull the full body of the one you want.`;
|
|
122860
123133
|
}
|
|
122861
|
-
var GET_TASK_BASE_DESCRIPTION = "Search the workspace's TASKS by intent and RETURN the matching task(s) as markdown. A task is the
|
|
123134
|
+
var GET_TASK_BASE_DESCRIPTION = "Search the workspace's TASKS by intent and RETURN the matching task(s) as markdown. A task is the workspace's reusable card \u2014 a one-shot procedure and a scheduled, memory-carrying job are the same kind of row, so this ONE tool searches all of them. Like getAllowlist, it does NOT execute anything \u2014 present the matched task(s) to the user and get their explicit confirmation before running one with `runTask`. Input: intent (natural-language description of what the user wants), optional category. On an unambiguous single match it also returns the raw `markdownBody`, spilled to a file path when large, so you can edit the task cross-session without hand-stripping the display prefix \u2014 and the task's `stateDocs` memory manifest ({docs:[{key,scope}],records:[{kind,scope}]}, scope shared|member) when it declares one. To create, publish/import or audit a task (including local skills), fetch the `task-architect` task first and follow it \u2014 `createTask` is only the final upsert it performs.";
|
|
122862
123135
|
function buildGetTaskDescription(items) {
|
|
122863
|
-
return `${GET_TASK_BASE_DESCRIPTION}${
|
|
123136
|
+
return `${GET_TASK_BASE_DESCRIPTION}${buildTaskIndexSection(items, "task")}`;
|
|
122864
123137
|
}
|
|
122865
123138
|
var refreshGetAllowlistDescriptionInFlight = false;
|
|
122866
123139
|
var pendingRefreshTimer = null;
|
|
@@ -122880,7 +123153,7 @@ async function refreshGetAllowlistDescription() {
|
|
|
122880
123153
|
}
|
|
122881
123154
|
} catch (err) {
|
|
122882
123155
|
console.warn("[mcp] fetchCapabilityIndex attempt 1 failed:", err);
|
|
122883
|
-
await new Promise((
|
|
123156
|
+
await new Promise((resolve3) => setTimeout(resolve3, 2e3));
|
|
122884
123157
|
try {
|
|
122885
123158
|
index2 = await fetchCapabilityIndex(currentCredentials.apiKey, true);
|
|
122886
123159
|
if (index2 === null) {
|
|
@@ -122916,7 +123189,7 @@ async function refreshGetAllowlistDescription() {
|
|
|
122916
123189
|
const msg = first.err instanceof Error ? first.err.message : String(first.err);
|
|
122917
123190
|
if (msg.includes("Not connected")) {
|
|
122918
123191
|
} else {
|
|
122919
|
-
await new Promise((
|
|
123192
|
+
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
122920
123193
|
const second = await tryNotify();
|
|
122921
123194
|
if (!second.ok) {
|
|
122922
123195
|
console.error("[capability-index] tools/list_changed failed twice", second.err);
|
|
@@ -122939,19 +123212,14 @@ async function refreshDynamicPrompts() {
|
|
|
122939
123212
|
const sdk = await getSDK();
|
|
122940
123213
|
const nativeServer = server.nativeServer;
|
|
122941
123214
|
if (!nativeServer?.registerPrompt) return;
|
|
122942
|
-
const
|
|
122943
|
-
|
|
122944
|
-
|
|
122945
|
-
|
|
122946
|
-
const compounds = compRes?.status === "ok" ? compRes.items.filter((c) => c.draft === false) : [];
|
|
122947
|
-
const loops2 = loopRes?.status === "ok" ? loopRes.items.filter((l) => l.draft === false) : [];
|
|
122948
|
-
if (compRes?.status === "ok" && loopRes?.status === "ok" && Array.isArray(compRes.items) && Array.isArray(loopRes.items)) {
|
|
122949
|
-
cardCounts = { compounds: compRes.items.length, loops: loopRes.items.length };
|
|
123215
|
+
const listRes = await sdk.loops.list(apiKey).catch(() => ({ status: "nok" }));
|
|
123216
|
+
const tasks2 = listRes?.status === "ok" ? listRes.items.filter((l) => l.draft === false) : [];
|
|
123217
|
+
if (listRes?.status === "ok" && Array.isArray(listRes.items)) {
|
|
123218
|
+
cardCounts = { tasks: listRes.items.length };
|
|
122950
123219
|
}
|
|
122951
|
-
compoundIndexCache = compounds.map((c) => ({ slug: c.slug, category: c.category, updatedAt: c.updatedAt }));
|
|
122952
|
-
loopIndexCache = loops2.map((l) => ({ slug: l.slug, category: l.category, updatedAt: l.updatedAt }));
|
|
122953
123220
|
const taskBySlug = /* @__PURE__ */ new Map();
|
|
122954
|
-
for (const
|
|
123221
|
+
for (const l of tasks2) {
|
|
123222
|
+
const it = { slug: l.slug, category: l.category, updatedAt: l.updatedAt };
|
|
122955
123223
|
const existing = taskBySlug.get(it.slug);
|
|
122956
123224
|
if (!existing || String(it.updatedAt ?? "") > String(existing.updatedAt ?? "")) taskBySlug.set(it.slug, it);
|
|
122957
123225
|
}
|
|
@@ -122966,8 +123234,7 @@ async function refreshDynamicPrompts() {
|
|
|
122966
123234
|
} catch {
|
|
122967
123235
|
}
|
|
122968
123236
|
const desired = /* @__PURE__ */ new Map();
|
|
122969
|
-
for (const
|
|
122970
|
-
for (const l of loops2) desired.set(promptName("loop", l.slug), { kind: "loop", id: l.id, summary: l });
|
|
123237
|
+
for (const l of tasks2) if (!desired.has(promptName(l.slug))) desired.set(promptName(l.slug), { id: l.id, summary: l });
|
|
122971
123238
|
for (const [name, handle] of dynamicPromptHandles) {
|
|
122972
123239
|
if (!desired.has(name)) {
|
|
122973
123240
|
try {
|
|
@@ -122977,19 +123244,13 @@ async function refreshDynamicPrompts() {
|
|
|
122977
123244
|
dynamicPromptHandles.delete(name);
|
|
122978
123245
|
}
|
|
122979
123246
|
}
|
|
122980
|
-
for (const [name, {
|
|
123247
|
+
for (const [name, { id, summary }] of desired) {
|
|
122981
123248
|
if (dynamicPromptHandles.has(name)) continue;
|
|
122982
|
-
const entry = buildPromptEntry(
|
|
123249
|
+
const entry = buildPromptEntry(summary);
|
|
122983
123250
|
const cb = async () => {
|
|
122984
|
-
|
|
122985
|
-
|
|
122986
|
-
|
|
122987
|
-
full = got?.status === "ok" ? got.skill : void 0;
|
|
122988
|
-
} else {
|
|
122989
|
-
const got = await sdk.loops.get(apiKey, id);
|
|
122990
|
-
full = got?.status === "ok" ? got.loop : void 0;
|
|
122991
|
-
}
|
|
122992
|
-
return buildPromptContent(kind, full ?? { slug: summary.slug, markdownBody: "" });
|
|
123251
|
+
const got = await sdk.loops.get(apiKey, id);
|
|
123252
|
+
const full = got?.status === "ok" ? got.loop : void 0;
|
|
123253
|
+
return buildPromptContent(full ?? { slug: summary.slug, markdownBody: "" });
|
|
122993
123254
|
};
|
|
122994
123255
|
try {
|
|
122995
123256
|
const handle = nativeServer.registerPrompt(name, { description: entry.description, argsSchema: void 0 }, cb);
|
|
@@ -123014,7 +123275,7 @@ async function refreshDynamicPrompts() {
|
|
|
123014
123275
|
} else {
|
|
123015
123276
|
const msg = first.err instanceof Error ? first.err.message : String(first.err);
|
|
123016
123277
|
if (!msg.includes("Not connected")) {
|
|
123017
|
-
await new Promise((
|
|
123278
|
+
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
123018
123279
|
const second = await tryNotify();
|
|
123019
123280
|
if (!second.ok) console.error("[dynamic-prompts] prompts/list_changed failed twice", second.err);
|
|
123020
123281
|
}
|
|
@@ -123283,7 +123544,7 @@ var approvalWaitConfig = (() => {
|
|
|
123283
123544
|
return {
|
|
123284
123545
|
attempts,
|
|
123285
123546
|
intervalMs,
|
|
123286
|
-
delay: (ms) => new Promise((
|
|
123547
|
+
delay: (ms) => new Promise((resolve3) => setTimeout(resolve3, ms))
|
|
123287
123548
|
};
|
|
123288
123549
|
})();
|
|
123289
123550
|
async function tryResolvePendingApproval(probe) {
|
|
@@ -124417,19 +124678,17 @@ function suggestedCategoryFromSlug(slug) {
|
|
|
124417
124678
|
const prefix = String(slug ?? "").trim().split(/[-_]/)[0]?.trim();
|
|
124418
124679
|
return prefix ? prefix.toLowerCase() : void 0;
|
|
124419
124680
|
}
|
|
124420
|
-
function categoryGateResponse(params, slug
|
|
124681
|
+
function categoryGateResponse(params, slug) {
|
|
124421
124682
|
if (typeof params.category === "string" && params.category.trim()) return void 0;
|
|
124422
124683
|
const suggestion = suggestedCategoryFromSlug(slug);
|
|
124423
124684
|
const suggestionText = suggestion ? ` A reasonable default (derived from the slug prefix) is "${suggestion}", but confirm it with the user.` : "";
|
|
124424
|
-
const noun = kind === "compound" ? "compound" : kind === "loop" ? "loop" : "task";
|
|
124425
|
-
const toolSuffix = kind === "compound" ? "Compound" : kind === "loop" ? "Loop" : "Task";
|
|
124426
124685
|
return {
|
|
124427
124686
|
content: [{ type: "text", text: JSON.stringify({
|
|
124428
124687
|
success: false,
|
|
124429
124688
|
error: "category_required",
|
|
124430
124689
|
guidance: {
|
|
124431
|
-
say_to_user: `Before I publish this
|
|
124432
|
-
next_action: `Ask the user for the category, then call
|
|
124690
|
+
say_to_user: `Before I publish this task, which category should it go under?${suggestionText}`,
|
|
124691
|
+
next_action: `Ask the user for the category, then call createTask again with an explicit \`category\`.`,
|
|
124433
124692
|
stop: true
|
|
124434
124693
|
},
|
|
124435
124694
|
...suggestion ? { suggestedCategory: suggestion } : {}
|
|
@@ -124489,12 +124748,29 @@ server.tool(
|
|
|
124489
124748
|
toolName: "getTask",
|
|
124490
124749
|
responseKey: "tasks",
|
|
124491
124750
|
resolve: (sdk, apiKey, intent, category) => sdk.tasks.resolve(apiKey, intent, category),
|
|
124492
|
-
|
|
124751
|
+
// Same wording as the slash-prompt directive (src/tasks/task-run-section.ts): after the pick,
|
|
124752
|
+
// ASK HOW to run it — a run mode is a spend decision, so no surface picks one silently.
|
|
124753
|
+
sayToUser: "Present these matching tasks to the user, briefly explain them, and ask which one to run before proceeding. Then, before running the one they pick, ASK HOW to run it and wait for an answer \u2014 always ask, never pick one silently: (1) one-time, in this chat, followed here; (2) one-time, headless in its own process via `ametyst task run <slug>`; (3) scheduled, recurring via `ametyst task schedule <slug> --every <dur>` (ask which cadence). Only then call runTask with the chosen mode (`in-chat` for 1, `headless` for 2) \u2014 for 3, hand the user the schedule command instead. When the user gave arguments for the run (a company, a cap, a list), pass them to runTask as `input` \u2014 they reach the task exactly as if the user had typed them in chat.",
|
|
124493
124754
|
resolverFailedSayToUser: "Couldn't search tasks right now.",
|
|
124494
124755
|
surfaceRawBodies: true,
|
|
124495
124756
|
fetchManifest: (sdk, apiKey, slug) => readTaskManifest(sdk, apiKey, slug)
|
|
124496
124757
|
})
|
|
124497
124758
|
);
|
|
124759
|
+
function shellQuote2(s) {
|
|
124760
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
124761
|
+
}
|
|
124762
|
+
async function resolveTaskOwnership(sdk, apiKey, entity) {
|
|
124763
|
+
try {
|
|
124764
|
+
const createdBy = typeof entity?.createdBy === "string" ? entity.createdBy.trim() : "";
|
|
124765
|
+
if (!createdBy) return { resolved: false };
|
|
124766
|
+
const res = await sdk.compoundedSkills.getSyncSelection(apiKey);
|
|
124767
|
+
const name = res?.status === "ok" && typeof res.self?.name === "string" ? res.self.name.trim() : "";
|
|
124768
|
+
if (!name) return { resolved: false };
|
|
124769
|
+
return { resolved: true, isOwner: name === createdBy, createdBy };
|
|
124770
|
+
} catch {
|
|
124771
|
+
return { resolved: false };
|
|
124772
|
+
}
|
|
124773
|
+
}
|
|
124498
124774
|
function taskMemoryBlock(slug, docKey) {
|
|
124499
124775
|
const listRecords = `taskMemoryGet({ taskSlug: "${slug}", records: true, kind: "<kind>" })`;
|
|
124500
124776
|
return {
|
|
@@ -124517,7 +124793,7 @@ function closeAllLiveDashboards() {
|
|
|
124517
124793
|
liveDashboards.clear();
|
|
124518
124794
|
}
|
|
124519
124795
|
process.once("exit", closeAllLiveDashboards);
|
|
124520
|
-
async function startLiveDashboard(entity, dir) {
|
|
124796
|
+
async function startLiveDashboard(entity, dir, sdk, apiKey) {
|
|
124521
124797
|
const slug = typeof entity?.slug === "string" ? entity.slug : "";
|
|
124522
124798
|
const html = entity?.dashboardHtml;
|
|
124523
124799
|
if (typeof html !== "string" || !html) return null;
|
|
@@ -124528,8 +124804,11 @@ async function startLiveDashboard(entity, dir) {
|
|
|
124528
124804
|
previous.close();
|
|
124529
124805
|
}
|
|
124530
124806
|
const handle = await startDashboardServer({
|
|
124531
|
-
|
|
124532
|
-
|
|
124807
|
+
task: { slug, dashboardHtml: html, dashboardManifest: entity?.dashboardManifest },
|
|
124808
|
+
runDir: dir,
|
|
124809
|
+
// `/data` reads the manifest-named docs from Ametyst MEMORY first — the run writes there
|
|
124810
|
+
// through taskMemoryAppend and touches the local file only at exit — then the file.
|
|
124811
|
+
readDoc: memoryDocResolver(sdk, apiKey, slug, normalizeMemoryManifest(entity?.stateDocs ?? null)),
|
|
124533
124812
|
deps: { log: (line) => console.error(line) }
|
|
124534
124813
|
});
|
|
124535
124814
|
if (!handle) return null;
|
|
@@ -124572,16 +124851,20 @@ async function runTaskCore(params, flavor) {
|
|
|
124572
124851
|
guidance: { say_to_user: flavor.missingRefSayToUser, next_action: flavor.missingRefNextAction }
|
|
124573
124852
|
}) }] };
|
|
124574
124853
|
}
|
|
124575
|
-
const
|
|
124576
|
-
|
|
124577
|
-
|
|
124578
|
-
|
|
124579
|
-
|
|
124580
|
-
|
|
124581
|
-
|
|
124582
|
-
|
|
124583
|
-
|
|
124584
|
-
|
|
124854
|
+
const input = typeof params.input === "string" && params.input.trim() !== "" ? params.input : void 0;
|
|
124855
|
+
const headless = (modeSource2) => {
|
|
124856
|
+
const command = flavor.headlessCommand(ref, input);
|
|
124857
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
124858
|
+
success: true,
|
|
124859
|
+
mode: "headless",
|
|
124860
|
+
...modeSource2 ? { modeSource: modeSource2 } : {},
|
|
124861
|
+
command,
|
|
124862
|
+
guidance: {
|
|
124863
|
+
say_to_user: flavor.headlessSayToUser(ref, command),
|
|
124864
|
+
next_action: "Tell the user to run the command in a shell."
|
|
124865
|
+
}
|
|
124866
|
+
}) }] };
|
|
124867
|
+
};
|
|
124585
124868
|
const invalidMode = (provided) => ({ content: [{ type: "text", text: JSON.stringify({
|
|
124586
124869
|
success: false,
|
|
124587
124870
|
error: "invalid_mode",
|
|
@@ -124613,19 +124896,31 @@ async function runTaskCore(params, flavor) {
|
|
|
124613
124896
|
if (resolved.mode === "headless") return headless(resolved.source);
|
|
124614
124897
|
}
|
|
124615
124898
|
const memoryDocKey = defaultDocKey(normalizeMemoryManifest(entity?.stateDocs ?? null));
|
|
124616
|
-
const
|
|
124899
|
+
const runRoot = resolveRunRoot(typeof params.dir === "string" ? params.dir : void 0);
|
|
124900
|
+
if (runRoot.reason === "fallback") {
|
|
124901
|
+
console.error(
|
|
124902
|
+
`(${entity.slug}: run folder anchored on the fallback ${runRoot.root} \u2014 ${(runRoot.rejected ?? []).map((r) => `${r.dir}: ${r.why}`).join("; ")})`
|
|
124903
|
+
);
|
|
124904
|
+
}
|
|
124905
|
+
const legacyHint = legacyRunFolderHint(entity.slug, runRoot.root);
|
|
124906
|
+
if (legacyHint) console.error(legacyHint);
|
|
124907
|
+
const materialized = flavor.materialize(entity, randomUUID4(), runRoot.root);
|
|
124617
124908
|
const docBoot = await materializeMemoryDocs(sdk, apiKey, entity, materialized.dir);
|
|
124618
124909
|
for (const note of docBoot.notes) console.error(`(${entity.slug} memory docs: ${note})`);
|
|
124619
124910
|
const runFiles = { ...materialized.files, ...docBoot.files };
|
|
124620
124911
|
const est = estimateBlastRadius(entity);
|
|
124621
|
-
const shipBack = flavor.buildShipBack({ dir: materialized.dir, entity });
|
|
124622
|
-
const dashboardUrl = await startLiveDashboard(entity, materialized.dir);
|
|
124912
|
+
const shipBack = await flavor.buildShipBack({ dir: materialized.dir, entity, sdk, apiKey });
|
|
124913
|
+
const dashboardUrl = await startLiveDashboard(entity, materialized.dir, sdk, apiKey);
|
|
124623
124914
|
const dashboardLine = dashboardUrl ? `Live dashboard: ${dashboardUrl}` : NO_DASHBOARD_MESSAGE;
|
|
124624
124915
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
124625
124916
|
success: true,
|
|
124626
124917
|
mode: "in-chat",
|
|
124627
124918
|
...modeSource ? { modeSource } : {},
|
|
124628
124919
|
dir: materialized.dir,
|
|
124920
|
+
// Which root the folder hangs off and why — `fallback` means "not under your project".
|
|
124921
|
+
runRoot: runRoot.root,
|
|
124922
|
+
runRootReason: runRoot.reason,
|
|
124923
|
+
...runRoot.rejected?.length ? { runRootRejected: runRoot.rejected } : {},
|
|
124629
124924
|
...dashboardUrl ? { dashboard: dashboardUrl } : {},
|
|
124630
124925
|
blastRadius: est,
|
|
124631
124926
|
files: runFiles,
|
|
@@ -124635,7 +124930,9 @@ async function runTaskCore(params, flavor) {
|
|
|
124635
124930
|
// the slug because the run context carries no task identity — which is exactly why
|
|
124636
124931
|
// the taskMemory* tools take an explicit `taskSlug`.
|
|
124637
124932
|
memory: taskMemoryBlock(entity.slug, memoryDocKey),
|
|
124638
|
-
|
|
124933
|
+
// The user's arguments lead the directive, in the same LAUNCH INPUT block the headless
|
|
124934
|
+
// launcher prepends to its prompt — so both surfaces hand them over in the same words.
|
|
124935
|
+
directive: launchInputBlock(input) + flavor.buildDirective({ dir: materialized.dir, slug: entity.slug, files: runFiles, docKey: memoryDocKey, runRoot }),
|
|
124639
124936
|
...shipBack ? { shipBack } : {}
|
|
124640
124937
|
}) }, { type: "text", text: dashboardLine }] };
|
|
124641
124938
|
} catch (error) {
|
|
@@ -124646,10 +124943,12 @@ async function runTaskCore(params, flavor) {
|
|
|
124646
124943
|
server.tool(
|
|
124647
124944
|
{
|
|
124648
124945
|
name: "runTask",
|
|
124649
|
-
description: "Run a workspace TASK after the user has confirmed it (getTask already presented + stopped). A task is the
|
|
124946
|
+
description: "Run a workspace TASK after the user has confirmed it (getTask already presented + stopped). A task is the workspace's reusable card \u2014 a one-shot procedure and a scheduled, memory-carrying job are the same kind of row, and this ONE tool runs either. Two modes: `in-chat` (default) materializes the task's definition files locally and returns them plus a directive so YOU execute it inline in this session, paying for any step via the `spend` tool; `headless` returns the shell command to run it unattended in its own process. Only the NON-EMPTY definition files are materialized \u2014 a task with no VISION/CONSTRAINTS gets just SKILL.md (+README.md) and NO constraints ship-back, instead of empty files that look checked but say nothing. DEFAULT RUN MODE: when you OMIT `mode` (or pass it empty), the task body's YAML frontmatter is consulted for a `defaultRunMode: headless|in-chat` key and that is used; an explicit `mode` wins over it. The ONLY accepted values are `in-chat` and `headless` \u2014 anything else (a typo like `in-chatt`) is REJECTED with `invalid_mode` and nothing runs: a value you passed is never quietly ignored, never coerced, and never falls back to the frontmatter, because that would answer an explicit `in-chat` with an unattended headless run. The response reports `modeSource` (explicit / frontmatter / default) so you can see which one decided.",
|
|
124650
124947
|
inputs: [
|
|
124651
124948
|
{ name: "task", type: "string", required: true, description: "Slug or id of the task to run (as returned by getTask)." },
|
|
124652
|
-
{ name: "mode", type: "string", required: false, description: 'Exactly "in-chat" (you run it inline now) or "headless" (run unattended via `ametyst task run`) \u2014 any other value is rejected with `invalid_mode` rather than coerced or ignored. Omit it (or pass empty) to use the task body\'s `defaultRunMode` frontmatter, falling back to "in-chat". An explicit value wins over the frontmatter.' }
|
|
124949
|
+
{ name: "mode", type: "string", required: false, description: 'Exactly "in-chat" (you run it inline now) or "headless" (run unattended via `ametyst task run`) \u2014 any other value is rejected with `invalid_mode` rather than coerced or ignored. Omit it (or pass empty) to use the task body\'s `defaultRunMode` frontmatter, falling back to "in-chat". An explicit value wins over the frontmatter.' },
|
|
124950
|
+
{ name: "dir", type: "string", required: false, description: "Directory to materialize and run in; defaults to the current project folder, falling back to a writable per-user location when there is none. The response reports the chosen root as `runRoot` and why as `runRootReason` (explicit | cwd | fallback)." },
|
|
124951
|
+
{ name: "input", type: "string", required: false, description: 'The user\'s arguments for THIS run, exactly as they would type them in chat \u2014 a company, a cap, a list (e.g. "run it on skyfire.com, cap $5"). A parameterised task run without them silently runs in its no-argument mode. Headless: the returned command carries them as `--input`. In-chat: the directive opens with a LAUNCH INPUT block holding them. Omit when the user gave no arguments.' }
|
|
124653
124952
|
]
|
|
124654
124953
|
},
|
|
124655
124954
|
async (params) => runTaskCore(params, {
|
|
@@ -124661,15 +124960,16 @@ server.tool(
|
|
|
124661
124960
|
notFoundError: "task_not_found",
|
|
124662
124961
|
fetch: (sdk, apiKey, ref) => sdk.tasks.get(apiKey, ref),
|
|
124663
124962
|
pick: (res) => res.task,
|
|
124664
|
-
headlessCommand: (ref) => `ametyst task run ${ref}`,
|
|
124665
|
-
headlessSayToUser: (
|
|
124963
|
+
headlessCommand: (ref, input) => `ametyst task run ${ref}${input === void 0 ? "" : ` --input ${shellQuote2(input)}`}`,
|
|
124964
|
+
headlessSayToUser: (_ref, command) => `Run \`${command}\` in a terminal \u2014 it materializes the task, runs it unattended, and ships back improvements on a clean finish. (\`task run\` is the unattended runner for every task row, whatever the task was originally authored as.) Paid steps go through your on-chain policy.`,
|
|
124666
124965
|
honorFrontmatterDefault: true,
|
|
124667
|
-
materialize: (task, runId) => materializeTask(task, runId),
|
|
124668
|
-
buildDirective: ({ dir, slug, files, docKey }) => {
|
|
124966
|
+
materialize: (task, runId, runRoot) => materializeTask(task, runId, runRoot),
|
|
124967
|
+
buildDirective: ({ dir, slug, files, docKey, runRoot }) => {
|
|
124669
124968
|
const present = Object.entries(files).filter(([label2]) => label2 !== "status").map(([, filename]) => filename);
|
|
124969
|
+
const whereNote = runRoot.reason === "fallback" ? `NOTE: this run's folder lives under the per-user fallback ${runRoot.root}, NOT under the current project, because the current folder was not writable (${(runRoot.rejected ?? []).map((r) => `${r.dir}: ${r.why}`).join("; ")}). Read the files from ${dir} exactly as given \u2014 do not go looking for them in the project. ` : "";
|
|
124670
124970
|
const readDoc = typeof docKey === "string" ? `taskMemoryGet({ taskSlug: "${slug}", docKey: "${docKey}" }) for the "${docKey}" document (the first one this task declares), then ` : `this task declares NO memory document \u2014 do not read or create one; read `;
|
|
124671
124971
|
const writeDoc = typeof docKey === "string" ? `, and rewrite the "${docKey}" document with taskMemoryAppend({ taskSlug: "${slug}", docKey: "${docKey}", content: "..." })` : ` \u2014 and NO document upsert, because this task declares none`;
|
|
124672
|
-
return
|
|
124972
|
+
return `${whereNote}Read the task files in ${dir} \u2014 the ONLY files materialized are: ${present.join(", ")} (plus STATUS.md, this run's own state). SKILL.md drives${files.vision ? "; VISION.md = done-condition" : ""}${files.constraints ? "; CONSTRAINTS.md = hard limits" : ""}. Any definition file NOT listed was empty on the task and was deliberately not written \u2014 do not go looking for it, and do not assume limits you cannot read. If the task has a QUEUE of work items it is EXTERNAL \u2014 NOT one of these files; SKILL.md tells you WHERE to read the queue from and WHERE to write the results. Execute the task here, in this session, until it is done (or, if it carries a VISION or a queue, until VISION is met / the queue is drained). Pay for any step ONLY via the spend tool (the on-chain policy is the budget). Keep ${dir}/STATUS.md updated. CLEAN UP AFTER YOURSELF: on a CLEAN finish (done / queue drained, no brake), DELETE the ${dir} folder \u2014 it is this run's scratch space, not a record, and every run gets its own, so leaving them behind piles up orphans. On a dirty stop, leave ${dir} in place so the run can be resumed.
|
|
124673
124973
|
|
|
124674
124974
|
Your durable memory is available via the taskMemory* tools with taskSlug "${slug}" \u2014 and it, not ${dir}, is what survives this run. Nothing from it was injected into this run: READ WHAT YOU NEED FIRST with taskMemoryGet \u2014 ${readDoc}one list per record kind you need, taskMemoryGet({ taskSlug: "${slug}", records: true, kind: "<kind>" }) (latest version per key, archived=false by default); empty means this is the first run. WRITE A RUN RECORD BEFORE EXITING, on every path including a brake: taskMemoryAppend({ taskSlug: "${slug}", kind: "run", content: "<what you did, what you learned, what the next run should pick up>" })${writeDoc}.
|
|
124675
124975
|
|
|
@@ -124680,7 +124980,22 @@ ${TASK_MEMORY_MODEL_SECTION}`;
|
|
|
124680
124980
|
// The `dir` cleanup does NOT live here: it is owed by EVERY in-chat run, and a task
|
|
124681
124981
|
// with no constraints gets no ship-back at all — so it is stated once, in the
|
|
124682
124982
|
// directive. This sentence only orders the two (ship back, then clean up).
|
|
124683
|
-
|
|
124983
|
+
//
|
|
124984
|
+
// ⛔ ONLY THE OWNER CAN SHIP BACK. buyer-api lets the row's creator or the workspace admin
|
|
124985
|
+
// modify a task; a member running an admin-owned task was told to `createTask` anyway,
|
|
124986
|
+
// tripped the category gate first and then got a 403. When ownership resolves and the
|
|
124987
|
+
// caller is not the owner, the directive says where the learnings go instead. When the
|
|
124988
|
+
// caller IS the owner, the call names the task's existing `category` so a MODIFY does not
|
|
124989
|
+
// trip `category_required`. Unresolvable → today's directive.
|
|
124990
|
+
buildShipBack: async ({ entity, sdk, apiKey }) => {
|
|
124991
|
+
if (!(typeof entity?.constraintsMd === "string" && entity.constraintsMd.length > 0)) return void 0;
|
|
124992
|
+
const owner = await resolveTaskOwnership(sdk, apiKey, entity);
|
|
124993
|
+
if (owner.resolved && !owner.isOwner) {
|
|
124994
|
+
return `SHIP-BACK: none. This task is owned by ${owner.createdBy}; do NOT patch its CONSTRAINTS (you would get 403). Your learnings belong in your run diary record, and in the task's member-scoped "learnings" document when its manifest declares one (taskMemoryAppend docKey "learnings"). To propose a change to the shared CONSTRAINTS, tell the user to open a card proposal from the web app.`;
|
|
124995
|
+
}
|
|
124996
|
+
const category = typeof entity?.category === "string" && entity.category.trim() ? `, category="${entity.category}"` : "";
|
|
124997
|
+
return `On a CLEAN finish (done / VISION met / queue drained, no brake), call createTask with id="${entity.id}"${category} and the updated constraintsMd to ship improvements back \u2014 patch ONLY constraintsMd (F11: never the queue). Do that BEFORE the directive's clean-up step.`;
|
|
124998
|
+
}
|
|
124684
124999
|
})
|
|
124685
125000
|
);
|
|
124686
125001
|
function taskMemoryContext(params) {
|
|
@@ -124700,7 +125015,7 @@ function taskMemoryContext(params) {
|
|
|
124700
125015
|
error: "taskSlug_required",
|
|
124701
125016
|
guidance: {
|
|
124702
125017
|
say_to_user: "I need to know which task's memory to use.",
|
|
124703
|
-
next_action: "Pass taskSlug explicitly \u2014 it is the slug of the task you are running (also in
|
|
125018
|
+
next_action: "Pass taskSlug explicitly \u2014 it is the slug of the task you are running (also in AMETYST_TASK_SLUG)."
|
|
124704
125019
|
}
|
|
124705
125020
|
}) }] }
|
|
124706
125021
|
};
|
|
@@ -124797,7 +125112,7 @@ server.tool(
|
|
|
124797
125112
|
name: "taskMemoryAppend",
|
|
124798
125113
|
description: "Write to a task's durable memory \u2014 the state that survives the run (the task's materialized directory does not outlive a clean exit). See the TASK MEMORY MODEL in your instructions for what docs and records are. Pass `content` WITH `docKey` to upsert a state document in place; pass `content` with `kind` to append a record: a RESERVED diary kind (run | decision | error | import) takes NO `key`; a FREE item kind (declared in the task's manifest) REQUIRES `key`, and every append with the same key is a new VERSION of that item (`archived: true` writes a closed version directly). The kind/key/size rules are checked here before the write and named on refusal; the server's own refusals are relayed verbatim. NAMESPACE: omit `scope` for a key/kind the task's `stateDocs` manifest declares (the server resolves it; a disagreeing scope is refused with `memory_scope_mismatch`); for an undeclared doc key pass `scope` explicitly \u2014 omitted, it lands on the seat and the response carries `warning: \"not in manifest\"`.",
|
|
124799
125114
|
inputs: [
|
|
124800
|
-
{ name: "taskSlug", type: "string", required: true, description: "Slug of the task whose memory to write. Required and explicit \u2014 the fire context carries no task identity. Also exported as
|
|
125115
|
+
{ name: "taskSlug", type: "string", required: true, description: "Slug of the task whose memory to write. Required and explicit \u2014 the fire context carries no task identity. Also exported as AMETYST_TASK_SLUG." },
|
|
124801
125116
|
{ name: "content", type: "string", required: true, description: "What to store. For a run record: what you did, what you learned, what the next fire should pick up. \u2264 32 KB on a diary kind, \u2264 256 KB on an item kind or a doc." },
|
|
124802
125117
|
{ name: "docKey", type: "string", required: false, description: `Set this to upsert a STATE DOCUMENT under that key instead of appending a record \u2014 the key the task's OWN manifest declares (e.g. "board"); there is no universal default key, and upserting an undeclared one mints a document nobody reads. Upserts replace in place, so rewriting a doc at a steady size costs nothing against the quota.` },
|
|
124803
125118
|
{ name: "kind", type: "string", required: false, description: `Record kind when appending \u2014 a reserved diary kind ("run", "decision", "error", "import") or a free item kind the task's manifest declares (a token [a-z0-9-]{1,64}, e.g. "pbi"). Defaults to "run". Ignored when docKey is set.` },
|
|
@@ -124906,7 +125221,7 @@ server.tool(
|
|
|
124906
125221
|
name: "taskMemoryGet",
|
|
124907
125222
|
description: 'Read a task\'s durable memory \u2014 what past runs left behind. See the TASK MEMORY MODEL in your instructions for what docs and records are and when to read which. Selectors: `docKey` for one state document; `records: true` for a record LISTING (filters: `kind`, `archived` false|true|"all" \u2014 default false, i.e. live items plus the diary; `key`, `keyPrefix`, `since`; `fields: "keys"` for identities + one-line summaries with no bodies; `count: true` for a number only; `limit` + `cursor` for keyset paging \u2014 the response carries `nextCursor`); `key` ALONE (no `records`, no filter) opens one item by key \u2014 its latest version, live or archived \u2014 with `history: true` for every version oldest-first; `usage: true` for the quota footprint. ANY record filter (`kind`, `keyPrefix`, `archived`, `since`, `fields`, `count`, `limit`, `cursor`, or `key` with `records`) selects the LISTING and reads no document, whether or not you also pass `records: true`. With no selector it returns the task\'s FIRST DECLARED document plus the default listing \u2014 a task declaring none gets records only, and no document is invented for it. Keyed items always collapse to their latest version per key. NAMESPACES: a DECLARED doc key is read from its manifest namespace by the server; an UNDECLARED one from this seat\'s row first, then the shared row. An empty result means no run has written yet.',
|
|
124908
125223
|
inputs: [
|
|
124909
|
-
{ name: "taskSlug", type: "string", required: true, description: "Slug of the task whose memory to read. Required and explicit \u2014 the fire context carries no task identity. Also exported as
|
|
125224
|
+
{ name: "taskSlug", type: "string", required: true, description: "Slug of the task whose memory to read. Required and explicit \u2014 the fire context carries no task identity. Also exported as AMETYST_TASK_SLUG." },
|
|
124910
125225
|
{ name: "docKey", type: "string", required: false, description: `Read one state document by key \u2014 the key the task's own manifest declares (e.g. "board"); there is no universal default key. Declared in the manifest \u2192 its declared namespace; undeclared \u2192 own row, then shared.` },
|
|
124911
125226
|
{ name: "records", type: "boolean", required: false, description: "Read a record LISTING instead of the default read \u2014 the explicit spelling; any filter below already implies it." },
|
|
124912
125227
|
{ name: "key", type: "string", required: false, description: "With `records: true`: narrow the listing to this one key. ALONE: open the item by key (latest version, live or archived; `record_not_found` if never written)." },
|
|
@@ -125100,7 +125415,7 @@ server.tool(
|
|
|
125100
125415
|
name: "taskMemoryArchive",
|
|
125101
125416
|
description: "CLOSE a keyed item in a task's durable memory \u2014 see the TASK MEMORY MODEL in your instructions. Appends one more version of the item with `archived: true` (archivedAt stamped by the server), copying the latest live version's content; the history stays intact and nothing is deleted or edited. Archived items drop out of default listings (archived=false) and stay readable by key or with archived: true|\"all\". `record_not_found` if the key was never written; `already_archived` if its latest version is already closed.",
|
|
125102
125417
|
inputs: [
|
|
125103
|
-
{ name: "taskSlug", type: "string", required: true, description: "Slug of the task whose item to close. Required and explicit \u2014 the fire context carries no task identity. Also exported as
|
|
125418
|
+
{ name: "taskSlug", type: "string", required: true, description: "Slug of the task whose item to close. Required and explicit \u2014 the fire context carries no task identity. Also exported as AMETYST_TASK_SLUG." },
|
|
125104
125419
|
{ name: "key", type: "string", required: true, description: "The item's key, exactly as written (case-sensitive)." },
|
|
125105
125420
|
{ name: "note", type: "string", required: false, description: 'Optional archive note \u2014 why it is closed ("shipped in #212", "superseded by pbi-9").' }
|
|
125106
125421
|
]
|
|
@@ -125178,7 +125493,7 @@ async function upsertTaskCore(params, flavor) {
|
|
|
125178
125493
|
void refreshDynamicPrompts();
|
|
125179
125494
|
const mode2 = id ? "modified" : "created";
|
|
125180
125495
|
const effectiveManifest = "stateDocs" in entity ? entity.stateDocs : !id ? null : previous && previous.available ? previous.content.stateDocs ?? null : void 0;
|
|
125181
|
-
const summary =
|
|
125496
|
+
const summary = buildTaskUpsertSummary({
|
|
125182
125497
|
mode: mode2,
|
|
125183
125498
|
sent: entity,
|
|
125184
125499
|
previous,
|
|
@@ -125187,7 +125502,7 @@ async function upsertTaskCore(params, flavor) {
|
|
|
125187
125502
|
const payload = enforceResponseCap({
|
|
125188
125503
|
success: true,
|
|
125189
125504
|
mode: mode2,
|
|
125190
|
-
[flavor.responseKey]:
|
|
125505
|
+
[flavor.responseKey]: projectTaskIdentity(flavor.pick(res)),
|
|
125191
125506
|
// WHERE IT LANDED, on the receipt itself. The 2026-08-31 incident put a
|
|
125192
125507
|
// task into the wrong (admin) workspace and the success payload named
|
|
125193
125508
|
// no workspace at all, so neither the agent nor the human reading the
|
|
@@ -125241,7 +125556,7 @@ async function upsertTaskCore(params, flavor) {
|
|
|
125241
125556
|
["visionMd", "visionFilePath"],
|
|
125242
125557
|
["constraintsMd", "constraintsFilePath"],
|
|
125243
125558
|
["readmeMd", "readmeFilePath"],
|
|
125244
|
-
// Per-
|
|
125559
|
+
// Per-task dashboard (loop-run D12): same inline-or-file verbatim plumbing as the
|
|
125245
125560
|
// *Md files — the bytes reach the SDK CreateLoopInput unchanged.
|
|
125246
125561
|
["dashboardHtml", "dashboardHtmlFilePath"],
|
|
125247
125562
|
["dashboardManifest", "dashboardManifestFilePath"]
|
|
@@ -125274,7 +125589,7 @@ async function upsertTaskCore(params, flavor) {
|
|
|
125274
125589
|
}) }] };
|
|
125275
125590
|
}
|
|
125276
125591
|
}
|
|
125277
|
-
const gate = categoryGateResponse(params, slug
|
|
125592
|
+
const gate = categoryGateResponse(params, slug);
|
|
125278
125593
|
if (gate) return gate;
|
|
125279
125594
|
let graphJson;
|
|
125280
125595
|
if (graphJsonProvided) {
|
|
@@ -125327,10 +125642,10 @@ async function upsertTaskCore(params, flavor) {
|
|
|
125327
125642
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
125328
125643
|
success: false,
|
|
125329
125644
|
redirect: "task-architect",
|
|
125330
|
-
reason: "a new
|
|
125645
|
+
reason: "a new task needs the qualification gate, brakes, status vocabulary and dashboard that the architect interview produces",
|
|
125331
125646
|
guidance: {
|
|
125332
|
-
say_to_user: "I can't spin up a
|
|
125333
|
-
next_action: 'Call getTask({ intent: "create a new
|
|
125647
|
+
say_to_user: "I can't spin up a task from a one-liner \u2014 a new task needs the qualification gate, brakes, status vocabulary and dashboard that the architect interview produces. Run the `task-architect` task instead; it interviews you and then publishes the finished task.",
|
|
125648
|
+
next_action: 'Call getTask({ intent: "create a new task" }) to fetch the `task-architect` task, present it, and run it once the user confirms. When you do, make sure the task it designs reads its memory at boot and writes a run record before exiting (see this tool\'s description) \u2014 a task that skips either one restarts from zero on every fire.',
|
|
125334
125649
|
stop: true
|
|
125335
125650
|
}
|
|
125336
125651
|
}) }] };
|
|
@@ -125344,7 +125659,7 @@ async function upsertTaskCore(params, flavor) {
|
|
|
125344
125659
|
server.tool(
|
|
125345
125660
|
{
|
|
125346
125661
|
name: "createTask",
|
|
125347
|
-
description: "Create OR modify a workspace TASK (upsert). A task is the
|
|
125662
|
+
description: "Create OR modify a workspace TASK (upsert). A task is the workspace's reusable card, and this ONE tool authors every shape of it: a task with only a `markdownBody` is a one-shot procedure, a task that also carries visionMd/constraintsMd/readmeMd is a scheduled job with a done-condition, hard limits and its own memory. Pass `id` to MODIFY, omit it to CREATE. MODIFY IS A NO-CLOBBER PATCH: send `id` plus ONLY the field(s) you want to change \u2014 any of markdownBody/visionMd/constraintsMd/readmeMd/dashboardHtml/dashboardManifest/stateDocs/descriptionShort/category/draft/graphJson \u2014 fields you don't send are preserved, so a constraints-only ship-back never has to resend the body. VERBATIM BODIES: for any LONG markdown field, WRITE it to a local file first and pass the matching *FilePath (`filePath` for the body, `visionFilePath`/`constraintsFilePath`/`readmeFilePath`/`dashboardHtmlFilePath`/`dashboardManifestFilePath`) INSTEAD of inlining it \u2014 the local MCP server reads the bytes from disk and pushes them verbatim (no arg-size limit, no drift). RESPONSE: a closing `summary` derived strictly from the published content (never invented) plus a compact `receipt` of per-field byte counts and source paths; the full bodies are NOT echoed back. NOT AN AUTHORING TOOL: it publishes what you give it. To create, publish or audit a task, run `task-architect` first \u2014 this tool is only the final upsert it performs. NOTE: if the user's intent is to publish/upload/import local skills to Ametyst, call getTask FIRST to fetch the `task-architect` task \u2014 it contains the publish procedure to follow before using this tool. MEMORY: `stateDocs` is the task's memory manifest \u2014 which memory docs / record kinds it owns and whether each is `shared` by the workspace or per-`member`; the runner creates the declared docs at boot and ships each back to its declared namespace. \u26D4 THE SCOPE CHOICE IS THE USER'S, NOT YOURS: BEFORE you create or modify a task that carries a memory manifest, EXPLAIN THE CHOICE TO THEM in your own words \u2014 never as `stateDocs` or `scope` \u2014 covering all of the following, and ASK them when their intent is ambiguous instead of deciding for them. Quote it verbatim if that is clearer; never contradict it: \"" + MEMORY_SCOPE_EXPLANATION + '" The response says which one the task ended up with, as a `memory` line on the receipt (`Memory: global` / `Memory: per member` / `Memory: mixed \u2014 N global \xB7 M per member` / `Memory: none`); getTask carries the same line. \u26D4 DECLARING THE MANIFEST IS EXPECTED ON EVERY TASK: a CREATE that declares none \u2014 or a declared-empty one \u2014 is given a floor rather than being born with no memory (the run diary, per member: `{"docs":[],"records":[{"kind":"run","scope":"member"}]}`), and the receipt says so with `(defaulted \u2014 no manifest declared)`, but that floor is a backstop and NOT a substitute for deriving the manifest this task actually needs and explaining the scope choice to the user first. DASHBOARD: createTask does NOT inject a default monitoring page on create \u2014 pass `dashboardHtml` if the task wants one.',
|
|
125348
125663
|
inputs: [
|
|
125349
125664
|
{ name: "slug", type: "string", required: false, description: "URL-safe unique slug for the task within the workspace. Required on CREATE." },
|
|
125350
125665
|
{ name: "descriptionShort", type: "string", required: false, description: "One-line description of what the task does. Required on CREATE." },
|
|
@@ -125370,7 +125685,6 @@ server.tool(
|
|
|
125370
125685
|
async (params) => upsertTaskCore(params, {
|
|
125371
125686
|
toolName: "createTask",
|
|
125372
125687
|
responseKey: "task",
|
|
125373
|
-
gateKind: "task",
|
|
125374
125688
|
get: (sdk, apiKey, id) => sdk.tasks.get(apiKey, id),
|
|
125375
125689
|
// `body` is assembled field-by-field in `upsertTaskCore` precisely so MODIFY stays a
|
|
125376
125690
|
// no-clobber PATCH, so it cannot statically satisfy `CreateTaskInput`'s required
|
|
@@ -126743,11 +127057,11 @@ import { existsSync as existsSync15 } from "fs";
|
|
|
126743
127057
|
import { homedir as homedir12 } from "os";
|
|
126744
127058
|
import { join as join19 } from "path";
|
|
126745
127059
|
|
|
126746
|
-
// src/
|
|
127060
|
+
// src/tasks/sync-skills.ts
|
|
126747
127061
|
init_esm_shims();
|
|
126748
127062
|
init_paths();
|
|
126749
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync10 } from "fs";
|
|
126750
|
-
import { join as join18 } from "path";
|
|
127063
|
+
import { accessSync as accessSync3, constants as constants3, existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync10 } from "fs";
|
|
127064
|
+
import { dirname as dirname8, join as join18, parse as parse4, resolve as resolve2 } from "path";
|
|
126751
127065
|
var MANAGED_MARKER = "<!-- ametyst-managed: sync-skills -->";
|
|
126752
127066
|
var GITIGNORE_HEADER = "# ametyst-managed: sync-skills \u2014 pointer skills generated for this account; never commit them.";
|
|
126753
127067
|
var GITIGNORE_HEADER_2 = "# Maintained by `ametyst serve` on every boot. Real skills without the managed marker are not listed.";
|
|
@@ -126777,7 +127091,7 @@ async function fetchSelectionContext(sdk, apiKey) {
|
|
|
126777
127091
|
}
|
|
126778
127092
|
}
|
|
126779
127093
|
function buildStubContent(item) {
|
|
126780
|
-
const {
|
|
127094
|
+
const { slug, descriptionShort } = item;
|
|
126781
127095
|
return `---
|
|
126782
127096
|
name: ${slug}
|
|
126783
127097
|
description: ${JSON.stringify(descriptionShort)}
|
|
@@ -126785,9 +127099,9 @@ description: ${JSON.stringify(descriptionShort)}
|
|
|
126785
127099
|
|
|
126786
127100
|
${MANAGED_MARKER}
|
|
126787
127101
|
|
|
126788
|
-
This is a pointer to the Ametyst
|
|
127102
|
+
This is a pointer to the Ametyst task \`${slug}\` \u2014 the real task lives in the Ametyst workspace, not in this file.
|
|
126789
127103
|
|
|
126790
|
-
${taskRunSection(slug
|
|
127104
|
+
${taskRunSection(slug)}`;
|
|
126791
127105
|
}
|
|
126792
127106
|
function isManaged(file) {
|
|
126793
127107
|
try {
|
|
@@ -126827,31 +127141,56 @@ function maintainGitignore(root2) {
|
|
|
126827
127141
|
writeFileSync10(file, buildGitignoreContent(managed));
|
|
126828
127142
|
return "written";
|
|
126829
127143
|
}
|
|
127144
|
+
function resolveSkillsScope(target, deps = {}) {
|
|
127145
|
+
const exists = deps.existsSync ?? existsSync14;
|
|
127146
|
+
const access = deps.accessSync ?? accessSync3;
|
|
127147
|
+
let cwd;
|
|
127148
|
+
try {
|
|
127149
|
+
cwd = (deps.cwd ?? (() => process.cwd()))();
|
|
127150
|
+
} catch (err) {
|
|
127151
|
+
const why2 = `the current directory cannot be read (${err instanceof Error ? err.message : String(err)})`;
|
|
127152
|
+
return { global: true, root: skillsRoot(target, true), fallback: { from: "<cwd>", why: why2 } };
|
|
127153
|
+
}
|
|
127154
|
+
const local = join18(cwd, SKILLS_DIR_BY_TARGET2[target], "skills");
|
|
127155
|
+
const abs = resolve2(cwd);
|
|
127156
|
+
let why;
|
|
127157
|
+
if (parse4(abs).root === abs) {
|
|
127158
|
+
why = "the current directory is the filesystem root, not a project folder";
|
|
127159
|
+
} else if (!exists(abs)) {
|
|
127160
|
+
why = `the current directory ${abs} does not exist`;
|
|
127161
|
+
} else {
|
|
127162
|
+
let probe = local;
|
|
127163
|
+
while (!exists(probe)) {
|
|
127164
|
+
const parent = dirname8(probe);
|
|
127165
|
+
if (parent === probe) break;
|
|
127166
|
+
probe = parent;
|
|
127167
|
+
}
|
|
127168
|
+
try {
|
|
127169
|
+
access(probe, constants3.W_OK);
|
|
127170
|
+
} catch (err) {
|
|
127171
|
+
const code = err?.code;
|
|
127172
|
+
why = `${probe} is not writable${code ? ` (${code})` : ""}`;
|
|
127173
|
+
}
|
|
127174
|
+
}
|
|
127175
|
+
if (why === void 0) return { global: false, root: local };
|
|
127176
|
+
return { global: true, root: skillsRoot(target, true), fallback: { from: local, why } };
|
|
127177
|
+
}
|
|
127178
|
+
var SKILLS_DIR_BY_TARGET2 = { claude: ".claude", codex: ".codex" };
|
|
126830
127179
|
async function syncSkills(opts = {}) {
|
|
126831
|
-
const global2 = opts.global ?? false;
|
|
126832
127180
|
const target = opts.target ?? "claude";
|
|
127181
|
+
const scope = opts.global ? { global: true, root: skillsRoot(target, true) } : resolveSkillsScope(target);
|
|
127182
|
+
const global2 = scope.global;
|
|
127183
|
+
const fallback2 = "fallback" in scope ? scope.fallback : void 0;
|
|
126833
127184
|
const { sdk, apiKey } = await getCliSdk();
|
|
126834
|
-
const
|
|
126835
|
-
if (
|
|
126836
|
-
|
|
126837
|
-
|
|
126838
|
-
|
|
126839
|
-
|
|
126840
|
-
|
|
126841
|
-
|
|
126842
|
-
|
|
126843
|
-
category: c.category ?? null,
|
|
126844
|
-
createdBy: c.createdBy ?? ""
|
|
126845
|
-
})),
|
|
126846
|
-
...loopRes.items.filter((l) => l.draft === false).map((l) => ({
|
|
126847
|
-
kind: "loop",
|
|
126848
|
-
slug: l.slug,
|
|
126849
|
-
descriptionShort: l.descriptionShort,
|
|
126850
|
-
id: l.id ?? "",
|
|
126851
|
-
category: l.category ?? null,
|
|
126852
|
-
createdBy: l.createdBy ?? ""
|
|
126853
|
-
}))
|
|
126854
|
-
];
|
|
127185
|
+
const listRes = await sdk.loops.list(apiKey);
|
|
127186
|
+
if (listRes?.status !== "ok") throw new Error(`failed to list tasks: ${listRes?.error ?? "unknown error"}`);
|
|
127187
|
+
const allItems = listRes.items.filter((l) => l.draft === false).map((l) => ({
|
|
127188
|
+
slug: l.slug,
|
|
127189
|
+
descriptionShort: l.descriptionShort,
|
|
127190
|
+
id: l.id ?? "",
|
|
127191
|
+
category: l.category ?? null,
|
|
127192
|
+
createdBy: l.createdBy ?? ""
|
|
127193
|
+
}));
|
|
126855
127194
|
const items = applySyncSelection(allItems, await fetchSelectionContext(sdk, apiKey));
|
|
126856
127195
|
const desired = /* @__PURE__ */ new Map();
|
|
126857
127196
|
for (const item of items) {
|
|
@@ -126888,7 +127227,7 @@ async function syncSkills(opts = {}) {
|
|
|
126888
127227
|
pruned++;
|
|
126889
127228
|
}
|
|
126890
127229
|
const gitignore = global2 ? "none" : maintainGitignore(root2);
|
|
126891
|
-
return { root: root2, written, pruned, skipped, gitignore };
|
|
127230
|
+
return { root: root2, written, pruned, skipped, gitignore, ...fallback2 ? { fallback: fallback2 } : {} };
|
|
126892
127231
|
}
|
|
126893
127232
|
|
|
126894
127233
|
// src/commands/autosync-skills.ts
|
|
@@ -126931,7 +127270,7 @@ async function autoSyncSkillsOnBoot(deps = {}) {
|
|
|
126931
127270
|
try {
|
|
126932
127271
|
const r = await sync(target);
|
|
126933
127272
|
log(
|
|
126934
|
-
`\u2705 skills autosync [${target}]: ${r.written} written, ${r.pruned} pruned` + (r.skipped.length ? `, ${r.skipped.length} skipped (unmanaged)` : "")
|
|
127273
|
+
`\u2705 skills autosync [${target}]: ${r.written} written, ${r.pruned} pruned` + (r.skipped.length ? `, ${r.skipped.length} skipped (unmanaged)` : "") + ` \u2192 ${r.root}` + (r.fallback ? ` (fallback to the home root: ${r.fallback.why}; project-local ${r.fallback.from} was not used)` : "")
|
|
126935
127274
|
);
|
|
126936
127275
|
} catch (err) {
|
|
126937
127276
|
log(
|
|
@@ -126979,7 +127318,7 @@ async function serveCommand() {
|
|
|
126979
127318
|
);
|
|
126980
127319
|
await startMCPServer(keystore, eoa, { ...config, apiKey, apiKeySource: resolved.source ?? void 0 }, versionNotice, {
|
|
126981
127320
|
// Best-effort: materialize local `/`-command pointer skills for every published
|
|
126982
|
-
//
|
|
127321
|
+
// task so they're available without a manual `ametyst task sync-skills`.
|
|
126983
127322
|
// Deferred from boot to the MCP initialize handshake so the sync is CLIENT-AWARE:
|
|
126984
127323
|
// clientInfo.name tells us whether the host reads `.claude/skills` (Claude) or
|
|
126985
127324
|
// `.codex/skills` (Codex); when it doesn't, presence detection picks the roots.
|
|
@@ -127586,53 +127925,53 @@ connectionsCommand.command("remove <provider>").description("Delete a stored con
|
|
|
127586
127925
|
// src/commands/task.ts
|
|
127587
127926
|
init_esm_shims();
|
|
127588
127927
|
|
|
127589
|
-
// src/
|
|
127928
|
+
// src/tasks/index.ts
|
|
127590
127929
|
init_esm_shims();
|
|
127591
127930
|
|
|
127592
|
-
// src/
|
|
127931
|
+
// src/tasks/materialize.ts
|
|
127593
127932
|
init_esm_shims();
|
|
127594
127933
|
init_paths();
|
|
127595
127934
|
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
127596
127935
|
import { join as join20 } from "path";
|
|
127597
127936
|
var MIRRORED_DEFINITION_FILES = ["SKILL.md", "VISION.md", "README.md"];
|
|
127598
|
-
function materialize(
|
|
127599
|
-
const dir =
|
|
127937
|
+
function materialize(task, fireId, stateDocs = [], runRoot) {
|
|
127938
|
+
const dir = runFireDir(task.slug, fireId, runRoot);
|
|
127600
127939
|
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
127601
127940
|
for (const doc of stateDocs) {
|
|
127602
127941
|
writeFileSync11(join20(dir, doc.filename), doc.body, { mode: 384 });
|
|
127603
127942
|
}
|
|
127604
127943
|
const files = {
|
|
127605
|
-
"SKILL.md":
|
|
127606
|
-
"VISION.md":
|
|
127607
|
-
"CONSTRAINTS.md":
|
|
127608
|
-
"README.md":
|
|
127944
|
+
"SKILL.md": task.markdownBody ?? "",
|
|
127945
|
+
"VISION.md": task.visionMd ?? "",
|
|
127946
|
+
"CONSTRAINTS.md": task.constraintsMd ?? "",
|
|
127947
|
+
"README.md": task.readmeMd ?? ""
|
|
127609
127948
|
};
|
|
127610
|
-
if (typeof
|
|
127611
|
-
files["dashboard.html"] =
|
|
127949
|
+
if (typeof task.dashboardHtml === "string" && task.dashboardHtml) {
|
|
127950
|
+
files["dashboard.html"] = task.dashboardHtml;
|
|
127612
127951
|
}
|
|
127613
|
-
if (typeof
|
|
127614
|
-
files["dashboard.manifest.json"] =
|
|
127952
|
+
if (typeof task.dashboardManifest === "string" && task.dashboardManifest) {
|
|
127953
|
+
files["dashboard.manifest.json"] = task.dashboardManifest;
|
|
127615
127954
|
}
|
|
127616
127955
|
for (const [name, body] of Object.entries(files)) {
|
|
127617
127956
|
writeFileSync11(join20(dir, name), body, { mode: 384 });
|
|
127618
127957
|
}
|
|
127619
127958
|
writeFileSync11(
|
|
127620
127959
|
join20(dir, "STATUS.md"),
|
|
127621
|
-
`# STATUS \u2014 ${
|
|
127960
|
+
`# STATUS \u2014 ${task.slug}
|
|
127622
127961
|
|
|
127623
|
-
loop_id: ${
|
|
127962
|
+
loop_id: ${task.id}
|
|
127624
127963
|
fire_id: ${fireId}
|
|
127625
127964
|
started: pending
|
|
127626
127965
|
queue: not started
|
|
127627
127966
|
`,
|
|
127628
127967
|
{ mode: 384 }
|
|
127629
127968
|
);
|
|
127630
|
-
mirrorDefinitionFiles(
|
|
127969
|
+
mirrorDefinitionFiles(task.slug, files, runRoot);
|
|
127631
127970
|
return dir;
|
|
127632
127971
|
}
|
|
127633
|
-
function mirrorDefinitionFiles(slug, files) {
|
|
127972
|
+
function mirrorDefinitionFiles(slug, files, runRoot) {
|
|
127634
127973
|
try {
|
|
127635
|
-
const root2 =
|
|
127974
|
+
const root2 = runDir(slug, runRoot);
|
|
127636
127975
|
mkdirSync10(root2, { recursive: true, mode: 448 });
|
|
127637
127976
|
for (const name of [...MIRRORED_DEFINITION_FILES, "dashboard.html", "dashboard.manifest.json"]) {
|
|
127638
127977
|
const body = files[name];
|
|
@@ -127643,134 +127982,17 @@ function mirrorDefinitionFiles(slug, files) {
|
|
|
127643
127982
|
}
|
|
127644
127983
|
}
|
|
127645
127984
|
|
|
127646
|
-
// src/
|
|
127647
|
-
init_esm_shims();
|
|
127648
|
-
import { spawnSync } from "child_process";
|
|
127649
|
-
function readGlobalGitConfig(key) {
|
|
127650
|
-
try {
|
|
127651
|
-
const r = spawnSync("git", ["config", "--global", "--get", key], { encoding: "utf-8" });
|
|
127652
|
-
const v = r.status === 0 ? r.stdout.trim() : "";
|
|
127653
|
-
return v || void 0;
|
|
127654
|
-
} catch {
|
|
127655
|
-
return void 0;
|
|
127656
|
-
}
|
|
127657
|
-
}
|
|
127658
|
-
function deriveGitIdentityEnv(env = process.env, readGitConfig = readGlobalGitConfig) {
|
|
127659
|
-
const name = env.AMETYST_LOOP_GIT_AUTHOR_NAME?.trim() || readGitConfig("user.name");
|
|
127660
|
-
const email = env.AMETYST_LOOP_GIT_AUTHOR_EMAIL?.trim() || readGitConfig("user.email");
|
|
127661
|
-
const out = {};
|
|
127662
|
-
if (name) {
|
|
127663
|
-
out.GIT_AUTHOR_NAME = name;
|
|
127664
|
-
out.GIT_COMMITTER_NAME = name;
|
|
127665
|
-
}
|
|
127666
|
-
if (email) {
|
|
127667
|
-
out.GIT_AUTHOR_EMAIL = email;
|
|
127668
|
-
out.GIT_COMMITTER_EMAIL = email;
|
|
127669
|
-
}
|
|
127670
|
-
return out;
|
|
127671
|
-
}
|
|
127672
|
-
function buildLaunchArgs(dir, opts = {}, slug, deps = {}) {
|
|
127673
|
-
const env = deps.env ?? process.env;
|
|
127674
|
-
const maxBudget = resolveMaxBudgetUsd(opts, env);
|
|
127675
|
-
const docKey = defaultDocKey(opts.memoryManifest);
|
|
127676
|
-
const readDocSentence = typeof docKey === "string" ? `taskMemoryGet({ taskSlug: "${slug}", docKey: "${docKey}" }) for your "${docKey}" document \u2014 the FIRST document this task's memory manifest declares, materialized for you at boot as ${filenameForKey(docKey)} \u2014 then ` : docKey === null ? `THIS TASK DECLARES NO MEMORY DOCUMENT \u2014 do not read one and do not create one; your records ARE its memory. Read ` : `no memory document is named here \u2014 this launcher could not resolve the task's manifest, so do NOT assume one exists. Read `;
|
|
127677
|
-
const writeDocSentence = typeof docKey === "string" ? `, and rewrite your "${docKey}" document with taskMemoryAppend({ taskSlug: "${slug}", docKey: "${docKey}", content: "<the state the next fire needs>" }).` : docKey === null ? `. This task declares no memory document, so there is nothing to rewrite \u2014 do not invent one; the run record and your keyed items are what the next fire reads.` : `. If this task declares a memory document, rewrite it with taskMemoryAppend({ taskSlug: "${slug}", docKey: "<the key its manifest declares>", content: "..." }) \u2014 this launcher could not name it for you, so do not guess a key.`;
|
|
127678
|
-
const prompt = `You are running the Ametyst loop${slug ? ` "${slug}"` : ""} headless and unattended.
|
|
127679
|
-
|
|
127680
|
-
${dir} is THIS FIRE'S OWN directory. Other fires of the same loop may be running right now, each with its own directory alongside yours \u2014 wherever the loop's SKILL says <LOOPDIR> it means exactly ${dir}, never a sibling's directory and never their shared parent. Derive every path the SKILL asks you to create (links, run-state) from ${dir}; never from a path written literally in the SKILL prose.
|
|
127681
|
-
|
|
127682
|
-
Read the loop definition files in ${dir}:
|
|
127683
|
-
- SKILL.md \u2014 the driver; follow it.
|
|
127684
|
-
- VISION.md \u2014 the objective / done-condition.
|
|
127685
|
-
- CONSTRAINTS.md \u2014 hard limits; never violate them.
|
|
127686
|
-
- STATUS.md \u2014 your run-state; keep it updated as you progress.
|
|
127687
|
-
|
|
127688
|
-
The QUEUE (the work items) is EXTERNAL \u2014 it is NOT one of these files. SKILL.md tells you WHERE to read the queue from and WHERE to write the results; read your work items from that source.
|
|
127689
|
-
${slug ? `
|
|
127690
|
-
YOUR DURABLE MEMORY survives this fire, and ${dir} does not \u2014 this directory is deleted when you exit, so anything you want the NEXT fire to know must go into memory, not into a file here. Reach it with the taskMemory* MCP tools, always with taskSlug "${slug}" (also exported as AMETYST_LOOP_SLUG). Nothing from it was injected into this fire beyond the materialized docs:
|
|
127691
|
-
- FIRST, before you start work, read what you need with taskMemoryGet: ${readDocSentence}one list per record kind you need \u2014 taskMemoryGet({ taskSlug: "${slug}", records: true, kind: "<kind>" }) \u2014 which returns the latest version per key, archived=false by default. If everything is empty this is your first fire \u2014 say so in your run record.
|
|
127692
|
-
- BEFORE YOU EXIT, on every path including a brake: taskMemoryAppend({ taskSlug: "${slug}", kind: "run", content: "<what you did, what you learned, what the next fire should pick up>" })${writeDocSentence}
|
|
127693
|
-
- Items with an identity (a PBI, a test, a merchant) are keyed records of a free kind: taskMemoryAppend({ taskSlug: "${slug}", kind: "<kind>", key: "<id>", content }) writes or versions one; taskMemoryArchive({ taskSlug: "${slug}", key: "<id>", note }) closes it.
|
|
127694
|
-
Memory is quota-bounded per workspace \u2014 an over-limit write is REJECTED and tells you the limit, never silently truncated. If a write is refused, shorten it and write again; do not skip the run record.
|
|
127695
|
-
|
|
127696
|
-
${TASK_MEMORY_MODEL_SECTION}
|
|
127697
|
-
` : ""}
|
|
127698
|
-
Execute the loop until VISION is met or the queue is drained. For any step that costs money, use the Ametyst \`spend\` MCP tool (the on-chain policy enforces the budget) \u2014 do NOT invent another payment path.
|
|
127699
|
-
|
|
127700
|
-
When you finish cleanly (VISION met / queue drained), write "status: done" and "queue drained" into ${dir}/STATUS.md. If you must stop early (a brake/constraint was hit or an unrecoverable error occurred), write "brake: <reason>" into ${dir}/STATUS.md and exit. Never exceed the constraints.`;
|
|
127701
|
-
const args = [
|
|
127702
|
-
"-p",
|
|
127703
|
-
prompt,
|
|
127704
|
-
"--dangerously-skip-permissions",
|
|
127705
|
-
// The WRAPPER (`ametyst task run`, see runTask) owns shipping the loop's learned
|
|
127706
|
-
// CONSTRAINTS back to Ametyst on exit. The headless agent must NEVER rewrite its own
|
|
127707
|
-
// stored task record, so deny the upsert tool even though `--dangerously-skip-permissions`
|
|
127708
|
-
// otherwise grants every tool. (STATUS/QUEUE stay local run-state and are never shipped.)
|
|
127709
|
-
// ⛔ THE LIST IS DERIVED, NOT TYPED, on both axes — a deny that names the wrong string is
|
|
127710
|
-
// indistinguishable from no deny at all:
|
|
127711
|
-
// - TOOL: `createTask` is what this server exposes now; `createLoop`, its retired alias,
|
|
127712
|
-
// is kept because the fire connects to whichever `ametyst serve` the host's MCP config
|
|
127713
|
-
// points at, which may still be an older build offering it. Denying only the retired
|
|
127714
|
-
// name is what the retirement would otherwise leave behind — an inert deny.
|
|
127715
|
-
// - ENTRY NAME: this line used to hardcode the `ametyst-staging` prefix, but a PROD build
|
|
127716
|
-
// registers as `ametyst` (AMETYST_MCP_NAME), so the guard was silently inert in prod.
|
|
127717
|
-
// AMETYST_MCP_NAMES is the repo's own list of every entry name this CLI family writes.
|
|
127718
|
-
// `--disallowedTools` is variadic and comma-or-space separated, so one arg carries them all.
|
|
127719
|
-
"--disallowedTools",
|
|
127720
|
-
AMETYST_MCP_NAMES.flatMap((n) => [`mcp__${n}__createTask`, `mcp__${n}__createLoop`]).join(","),
|
|
127721
|
-
"--add-dir",
|
|
127722
|
-
dir
|
|
127723
|
-
];
|
|
127724
|
-
if (maxBudget !== void 0) {
|
|
127725
|
-
args.push("--max-budget-usd", String(maxBudget));
|
|
127726
|
-
}
|
|
127727
|
-
if (opts.sessionId) {
|
|
127728
|
-
args.push("--session-id", opts.sessionId);
|
|
127729
|
-
}
|
|
127730
|
-
args.push(
|
|
127731
|
-
// Eager-load MCP tools: with tool search enabled, MCP tools (including Ametyst's) are
|
|
127732
|
-
// deferred behind a ToolSearch step that smaller orchestrator models (e.g. Haiku) never
|
|
127733
|
-
// perform — the run ends its turn without the tools (BUG-15). Eager loading is safe for
|
|
127734
|
-
// all models, so this is universal rather than model-gated.
|
|
127735
|
-
"--settings",
|
|
127736
|
-
'{"env":{"ENABLE_TOOL_SEARCH":"false"}}'
|
|
127737
|
-
);
|
|
127738
|
-
return {
|
|
127739
|
-
cmd: "claude",
|
|
127740
|
-
args,
|
|
127741
|
-
cwd: process.cwd(),
|
|
127742
|
-
env: {
|
|
127743
|
-
...deriveGitIdentityEnv(env, deps.readGitConfig ?? readGlobalGitConfig),
|
|
127744
|
-
// Loop memory is addressed by SLUG, and the fire context carries no loop
|
|
127745
|
-
// identity today — which is why the taskMemory* tools take an explicit
|
|
127746
|
-
// `taskSlug`. Exporting it here is the convenience half: the prompt names
|
|
127747
|
-
// the slug literally, and this lets any shell step in the loop reach the
|
|
127748
|
-
// same value without re-deriving it. Env only, never argv — the launch
|
|
127749
|
-
// argv is pinned byte-for-byte by launch.test.ts, and widening it would
|
|
127750
|
-
// be a change to the command rather than to the child's environment.
|
|
127751
|
-
...slug ? { AMETYST_LOOP_SLUG: slug } : {}
|
|
127752
|
-
}
|
|
127753
|
-
};
|
|
127754
|
-
}
|
|
127755
|
-
function resolveMaxBudgetUsd(opts, env = process.env) {
|
|
127756
|
-
if (opts.maxBudgetUsd !== void 0) return opts.maxBudgetUsd;
|
|
127757
|
-
const raw = env.AMETYST_LOOP_MAX_BUDGET_USD;
|
|
127758
|
-
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
127759
|
-
const n = Number(raw);
|
|
127760
|
-
return Number.isFinite(n) ? n : void 0;
|
|
127761
|
-
}
|
|
127762
|
-
|
|
127763
|
-
// src/loops/heartbeat.ts
|
|
127985
|
+
// src/tasks/heartbeat.ts
|
|
127764
127986
|
init_esm_shims();
|
|
127765
127987
|
import * as realFs2 from "fs";
|
|
127766
127988
|
import { join as join21 } from "path";
|
|
127767
|
-
function startHeartbeat(
|
|
127989
|
+
function startHeartbeat(runDir2, info, deps = {}) {
|
|
127768
127990
|
const fs = deps.fs ?? realFs2;
|
|
127769
127991
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
127770
127992
|
const setI = deps.setInterval ?? globalThis.setInterval;
|
|
127771
127993
|
const clearI = deps.clearInterval ?? globalThis.clearInterval;
|
|
127772
127994
|
const intervalMs = deps.intervalMs ?? 6e4;
|
|
127773
|
-
const stateDir = join21(
|
|
127995
|
+
const stateDir = join21(runDir2, ".state");
|
|
127774
127996
|
const path2 = join21(stateDir, "fire.running");
|
|
127775
127997
|
try {
|
|
127776
127998
|
fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
@@ -127807,7 +128029,7 @@ ${info.sessionId ? `session=${info.sessionId}
|
|
|
127807
128029
|
};
|
|
127808
128030
|
}
|
|
127809
128031
|
|
|
127810
|
-
// src/
|
|
128032
|
+
// src/tasks/accounting.ts
|
|
127811
128033
|
init_esm_shims();
|
|
127812
128034
|
import * as realFs3 from "fs";
|
|
127813
128035
|
import { homedir as homedir13 } from "os";
|
|
@@ -127871,21 +128093,21 @@ function recordFireAccounting(args) {
|
|
|
127871
128093
|
const stats = parseTranscriptStats(fs.readFileSync(transcript, "utf-8"));
|
|
127872
128094
|
const rec = {
|
|
127873
128095
|
ts: new Date(args.startedAtEpochMs).toISOString(),
|
|
127874
|
-
loop: args.
|
|
128096
|
+
loop: args.taskSlug,
|
|
127875
128097
|
session: args.sessionId,
|
|
127876
128098
|
exit: args.exitCode,
|
|
127877
128099
|
duration_s: Math.max(0, Math.round((now().getTime() - args.startedAtEpochMs) / 1e3)),
|
|
127878
128100
|
...stats
|
|
127879
128101
|
};
|
|
127880
|
-
appendFireLine(fs, args.
|
|
128102
|
+
appendFireLine(fs, args.runDir, rec);
|
|
127881
128103
|
return rec;
|
|
127882
128104
|
} catch (err) {
|
|
127883
128105
|
log(` (accounting failed \u2014 fire itself unaffected: ${err instanceof Error ? err.message : String(err)})`);
|
|
127884
128106
|
return null;
|
|
127885
128107
|
}
|
|
127886
128108
|
}
|
|
127887
|
-
function appendFireLine(fs,
|
|
127888
|
-
const stateDir = join22(
|
|
128109
|
+
function appendFireLine(fs, runDir2, rec) {
|
|
128110
|
+
const stateDir = join22(runDir2, ".state");
|
|
127889
128111
|
fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
127890
128112
|
fs.appendFileSync(join22(stateDir, "fires.jsonl"), JSON.stringify(rec) + "\n", { mode: 384 });
|
|
127891
128113
|
}
|
|
@@ -127896,7 +128118,7 @@ function recordFailedLaunch(args) {
|
|
|
127896
128118
|
try {
|
|
127897
128119
|
const rec = {
|
|
127898
128120
|
ts: new Date(args.startedAtEpochMs).toISOString(),
|
|
127899
|
-
loop: args.
|
|
128121
|
+
loop: args.taskSlug,
|
|
127900
128122
|
session: args.sessionId,
|
|
127901
128123
|
exit: args.exitCode ?? 1,
|
|
127902
128124
|
duration_s: Math.max(0, Math.round((now().getTime() - args.startedAtEpochMs) / 1e3)),
|
|
@@ -127904,7 +128126,7 @@ function recordFailedLaunch(args) {
|
|
|
127904
128126
|
launch_failed: true,
|
|
127905
128127
|
reason: args.reason
|
|
127906
128128
|
};
|
|
127907
|
-
appendFireLine(fs, args.
|
|
128129
|
+
appendFireLine(fs, args.runDir, rec);
|
|
127908
128130
|
return rec;
|
|
127909
128131
|
} catch (err) {
|
|
127910
128132
|
log(` (failed-launch record could not be written: ${err instanceof Error ? err.message : String(err)})`);
|
|
@@ -127912,14 +128134,14 @@ function recordFailedLaunch(args) {
|
|
|
127912
128134
|
}
|
|
127913
128135
|
}
|
|
127914
128136
|
|
|
127915
|
-
// src/
|
|
128137
|
+
// src/tasks/run.ts
|
|
127916
128138
|
init_esm_shims();
|
|
127917
128139
|
import { spawn as spawn2 } from "child_process";
|
|
127918
128140
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
127919
128141
|
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
127920
|
-
import { dirname as
|
|
128142
|
+
import { dirname as dirname9, join as join24 } from "path";
|
|
127921
128143
|
|
|
127922
|
-
// src/
|
|
128144
|
+
// src/tasks/claude-binary.ts
|
|
127923
128145
|
init_esm_shims();
|
|
127924
128146
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
127925
128147
|
var defaultRun = (cmd, args) => {
|
|
@@ -127966,7 +128188,7 @@ function ensureClaudeBinary(deps = {}) {
|
|
|
127966
128188
|
return { status: "healed" };
|
|
127967
128189
|
}
|
|
127968
128190
|
|
|
127969
|
-
// src/
|
|
128191
|
+
// src/tasks/concurrency.ts
|
|
127970
128192
|
init_esm_shims();
|
|
127971
128193
|
import * as realFs4 from "fs";
|
|
127972
128194
|
import { join as join23 } from "path";
|
|
@@ -127986,21 +128208,21 @@ function parseHeartbeatPid(body) {
|
|
|
127986
128208
|
const pid = Number(m[1]);
|
|
127987
128209
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
127988
128210
|
}
|
|
127989
|
-
function liveFires(
|
|
128211
|
+
function liveFires(taskRoot, deps = {}) {
|
|
127990
128212
|
const fs = deps.fs ?? realFs4;
|
|
127991
128213
|
const isAlive = deps.isAlive ?? pidIsAlive;
|
|
127992
128214
|
const now = deps.now ?? Date.now;
|
|
127993
128215
|
const staleMs = deps.staleMs ?? DEFAULT_HEARTBEAT_STALE_MS;
|
|
127994
128216
|
let entries;
|
|
127995
128217
|
try {
|
|
127996
|
-
entries = fs.readdirSync(join23(
|
|
128218
|
+
entries = fs.readdirSync(join23(taskRoot, "fires"));
|
|
127997
128219
|
} catch {
|
|
127998
128220
|
return [];
|
|
127999
128221
|
}
|
|
128000
128222
|
const out = [];
|
|
128001
128223
|
for (const entry of entries) {
|
|
128002
128224
|
try {
|
|
128003
|
-
const beat = join23(
|
|
128225
|
+
const beat = join23(taskRoot, "fires", String(entry), ".state", "fire.running");
|
|
128004
128226
|
const st = fs.statSync(beat);
|
|
128005
128227
|
const heartbeatAgeMs = now() - Number(st.mtimeMs);
|
|
128006
128228
|
if (!(heartbeatAgeMs <= staleMs)) continue;
|
|
@@ -128015,20 +128237,24 @@ function liveFires(loopRoot, deps = {}) {
|
|
|
128015
128237
|
}
|
|
128016
128238
|
function resolveMaxConcurrentFires(opts = {}, env = process.env) {
|
|
128017
128239
|
const explicit = opts.maxConcurrentFires;
|
|
128018
|
-
const raw = env
|
|
128240
|
+
const raw = readTaskEnv("MAX_CONCURRENT_FIRES", env);
|
|
128019
128241
|
const fromEnv = raw !== void 0 && raw.trim() !== "" ? Number(raw) : void 0;
|
|
128020
128242
|
const picked = explicit !== void 0 && Number.isFinite(explicit) ? explicit : fromEnv !== void 0 && Number.isFinite(fromEnv) ? fromEnv : DEFAULT_MAX_CONCURRENT_FIRES;
|
|
128021
128243
|
if (picked <= 0) return Infinity;
|
|
128022
128244
|
return Math.floor(picked);
|
|
128023
128245
|
}
|
|
128024
128246
|
|
|
128025
|
-
// src/
|
|
128247
|
+
// src/tasks/run.ts
|
|
128026
128248
|
init_paths();
|
|
128027
128249
|
var SHIPBACK_SIGNAL_TIMEOUT_MS = 8e3;
|
|
128028
|
-
function
|
|
128250
|
+
function isForbidden(res) {
|
|
128251
|
+
if (res.code === 403) return true;
|
|
128252
|
+
return /\bHTTP 403\b|forbidden|only the creator/i.test(res.error ?? "");
|
|
128253
|
+
}
|
|
128254
|
+
function preserveRefusedConstraints(slug, fireId, body, runRoot) {
|
|
128029
128255
|
try {
|
|
128030
|
-
const path2 = refusedConstraintsPath(slug, fireId);
|
|
128031
|
-
mkdirSync11(
|
|
128256
|
+
const path2 = refusedConstraintsPath(slug, fireId, runRoot);
|
|
128257
|
+
mkdirSync11(dirname9(path2), { recursive: true, mode: 448 });
|
|
128032
128258
|
writeFileSync12(path2, body, { mode: 384 });
|
|
128033
128259
|
return { path: path2 };
|
|
128034
128260
|
} catch (err) {
|
|
@@ -128047,65 +128273,74 @@ function killTree(pid) {
|
|
|
128047
128273
|
}
|
|
128048
128274
|
}, 2e3).unref?.();
|
|
128049
128275
|
}
|
|
128050
|
-
async function
|
|
128276
|
+
async function runTask(taskId, opts = {}) {
|
|
128051
128277
|
const { sdk, apiKey } = await getCliSdk();
|
|
128052
|
-
const got = await sdk.loops.get(apiKey,
|
|
128053
|
-
if (got.status !== "ok") throw new Error(`
|
|
128054
|
-
const
|
|
128055
|
-
const
|
|
128278
|
+
const got = await sdk.loops.get(apiKey, taskId);
|
|
128279
|
+
if (got.status !== "ok") throw new Error(`task not found: ${got.error}`);
|
|
128280
|
+
const task = got.loop;
|
|
128281
|
+
const runRoot = resolveRunRoot();
|
|
128282
|
+
if (runRoot.reason === "fallback") {
|
|
128283
|
+
const why = runRoot.rejected?.map((r) => `${r.dir}: ${r.why}`).join("; ") ?? "no usable cwd";
|
|
128284
|
+
console.log(`Task ${task.slug}: running under ${runRoot.root} \u2014 the current folder cannot host a run (${why}).`);
|
|
128285
|
+
}
|
|
128286
|
+
const taskRoot = runDir(task.slug, runRoot.root);
|
|
128287
|
+
const legacyHint = legacyRunFolderHint(task.slug, runRoot.root);
|
|
128288
|
+
if (legacyHint) console.log(legacyHint);
|
|
128056
128289
|
const maxConcurrent = resolveMaxConcurrentFires(opts, process.env);
|
|
128057
|
-
const inFlight = liveFires(
|
|
128290
|
+
const inFlight = liveFires(taskRoot);
|
|
128058
128291
|
if (inFlight.length >= maxConcurrent) {
|
|
128059
128292
|
console.log(
|
|
128060
|
-
`
|
|
128293
|
+
`Task ${task.slug}: standing down \u2014 ${inFlight.length} fire(s) already in flight (pids ${inFlight.map((f) => f.pid).join(", ")}), ceiling ${maxConcurrent}. Raise it with --max-concurrent-fires / AMETYST_TASK_MAX_CONCURRENT_FIRES (0 = unlimited).`
|
|
128061
128294
|
);
|
|
128062
|
-
return { status: "skipped", dir:
|
|
128295
|
+
return { status: "skipped", dir: taskRoot, shipBack: { outcome: "nothing" } };
|
|
128063
128296
|
}
|
|
128064
|
-
const est = estimateBlastRadius(
|
|
128297
|
+
const est = estimateBlastRadius(task);
|
|
128065
128298
|
const sessionId2 = opts.sessionId ?? randomUUID5();
|
|
128066
|
-
const discovered = await discoverMemoryDocs(sdk, apiKey,
|
|
128299
|
+
const discovered = await discoverMemoryDocs(sdk, apiKey, task.slug);
|
|
128067
128300
|
for (const scope of discovered.failed) {
|
|
128068
128301
|
console.error(
|
|
128069
|
-
`
|
|
128302
|
+
`Task ${task.slug}: could not list the ${scope === "member" ? "seat's own" : "shared"} memory docs \u2014 any undeclared doc that lives only there is NOT materialized this fire (declared docs are still resolved individually from the manifest), and nothing is overwritten.`
|
|
128070
128303
|
);
|
|
128071
128304
|
}
|
|
128072
128305
|
for (const scope of discovered.truncated) {
|
|
128073
128306
|
console.error(
|
|
128074
|
-
`
|
|
128307
|
+
`Task ${task.slug}: the ${scope === "member" ? "seat's own" : "shared"} memory holds at least ${(scope === "member" ? discovered.own : discovered.shared).length} docs and the listing has no cursor \u2014 undeclared docs past the first page are NOT materialized this fire.`
|
|
128075
128308
|
);
|
|
128076
128309
|
}
|
|
128077
|
-
const plan = planStateDocs(
|
|
128310
|
+
const plan = planStateDocs(task.stateDocs, discovered);
|
|
128078
128311
|
if (plan.skipped.length > 0) {
|
|
128079
128312
|
console.error(
|
|
128080
|
-
`
|
|
128313
|
+
`Task ${task.slug}: IGNORING ${plan.skipped.length} memory doc(s) (${plan.skipped.map((d) => `${d.key}: ${d.reason}`).join("; ")}). They are NOT materialized and NOT shipped back \u2014 anything this fire writes to those filenames is the wrapper's own, and would overwrite the record if sent up.`
|
|
128081
128314
|
);
|
|
128082
128315
|
}
|
|
128083
|
-
const { fetched: stateDocBodies, failed: stateDocFailures } = plan.docs.length > 0 ? await fetchStateDocs(sdk, apiKey,
|
|
128316
|
+
const { fetched: stateDocBodies, failed: stateDocFailures } = plan.docs.length > 0 ? await fetchStateDocs(sdk, apiKey, task.slug, plan.docs) : { fetched: [], failed: [] };
|
|
128084
128317
|
if (stateDocFailures.length > 0) {
|
|
128085
128318
|
console.error(
|
|
128086
|
-
`
|
|
128319
|
+
`Task ${task.slug}: could not read or create ${stateDocFailures.length} memory doc(s) (${stateDocFailures.map((f) => `${f.key}: ${f.reason}`).join("; ")}) \u2014 they are NOT materialized this fire, and their records are left untouched rather than overwritten with an empty file.`
|
|
128087
128320
|
);
|
|
128088
128321
|
}
|
|
128089
128322
|
if (stateDocBodies.length > 0) {
|
|
128090
|
-
console.log(`
|
|
128323
|
+
console.log(`Task ${task.slug}: state docs \u2192 ${stateDocBodies.map(describeSource).join(", ")}`);
|
|
128091
128324
|
}
|
|
128092
128325
|
const undeclared = stateDocBodies.filter((d) => !d.declared);
|
|
128093
128326
|
if (undeclared.length > 0) {
|
|
128094
128327
|
console.log(
|
|
128095
|
-
`
|
|
128328
|
+
`Task ${task.slug}: ${undeclared.length} memory doc(s) not in manifest (${undeclared.map((d) => `${d.key} \u2190 ${d.scope}`).join(", ")}) \u2014 materialized anyway, and shipped back to the namespace each came from. Declare them in the task's stateDocs manifest to make that explicit.`
|
|
128096
128329
|
);
|
|
128097
128330
|
}
|
|
128098
|
-
const dir = materialize(
|
|
128099
|
-
console.log(`
|
|
128331
|
+
const dir = materialize(task, sessionId2, stateDocBodies, runRoot.root);
|
|
128332
|
+
console.log(`Task ${task.slug}: fire ${sessionId2} \u2192 ${dir}`);
|
|
128100
128333
|
const launch = buildLaunchArgs(
|
|
128101
128334
|
dir,
|
|
128102
|
-
|
|
128103
|
-
|
|
128335
|
+
// The child runs IN the resolved root: for a project cwd that is the cwd it always was; under
|
|
128336
|
+
// the fallback it is the writable folder rather than the `/` launchd handed us.
|
|
128337
|
+
{ ...opts, sessionId: sessionId2, cwd: opts.cwd ?? runRoot.root, memoryManifest: normalizeMemoryManifest(task.stateDocs) },
|
|
128338
|
+
task.slug
|
|
128104
128339
|
);
|
|
128105
128340
|
const budget = resolveMaxBudgetUsd(opts);
|
|
128106
128341
|
const capLabel = budget !== void 0 ? `capped at \u20AC${budget}` : "no external budget cap";
|
|
128107
128342
|
console.log(
|
|
128108
|
-
`
|
|
128343
|
+
`Task ${task.slug}: ${est.steps} steps (${est.paidSteps} paid), est \u20AC${est.estCostEur ?? "?"} \u2014 ${capLabel}`
|
|
128109
128344
|
);
|
|
128110
128345
|
const statusPath = join24(dir, "STATUS.md");
|
|
128111
128346
|
const statusBefore = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
|
|
@@ -128119,10 +128354,14 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128119
128354
|
{ mode: 384 }
|
|
128120
128355
|
);
|
|
128121
128356
|
const heartbeat = startHeartbeat(dir, { pid: process.pid, sessionId: sessionId2 });
|
|
128122
|
-
const dashboard = await startDashboardServer({
|
|
128357
|
+
const dashboard = await startDashboardServer({
|
|
128358
|
+
task,
|
|
128359
|
+
runDir: dir,
|
|
128360
|
+
readDoc: memoryDocResolver(sdk, apiKey, task.slug, normalizeMemoryManifest(task.stateDocs))
|
|
128361
|
+
});
|
|
128123
128362
|
if (dashboard) {
|
|
128124
128363
|
openDashboardInBrowser(`http://localhost:${dashboard.port}`, { isTTY: Boolean(process.stdout.isTTY) });
|
|
128125
|
-
} else if (typeof
|
|
128364
|
+
} else if (typeof task.dashboardHtml !== "string" || !task.dashboardHtml) {
|
|
128126
128365
|
console.log(NO_DASHBOARD_MESSAGE);
|
|
128127
128366
|
}
|
|
128128
128367
|
const startedAtEpochMs = Date.now();
|
|
@@ -128130,7 +128369,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128130
128369
|
ensureClaudeBinary();
|
|
128131
128370
|
} catch (err) {
|
|
128132
128371
|
const reason = err instanceof Error ? err.message : String(err);
|
|
128133
|
-
recordFailedLaunch({
|
|
128372
|
+
recordFailedLaunch({ runDir: dir, taskSlug: task.slug, sessionId: sessionId2, reason, startedAtEpochMs });
|
|
128134
128373
|
heartbeat.stop();
|
|
128135
128374
|
dashboard?.close();
|
|
128136
128375
|
throw err;
|
|
@@ -128151,17 +128390,17 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128151
128390
|
async function shipDocs() {
|
|
128152
128391
|
if (stateDocBodies.length === 0) return;
|
|
128153
128392
|
try {
|
|
128154
|
-
const outcomes = await shipBackStateDocs(sdk, apiKey,
|
|
128393
|
+
const outcomes = await shipBackStateDocs(sdk, apiKey, task.slug, dir, stateDocBodies);
|
|
128155
128394
|
for (const o of outcomes) {
|
|
128156
128395
|
if (o.outcome === "shipped") {
|
|
128157
|
-
console.log(`
|
|
128396
|
+
console.log(`Task ${task.slug}: shipped back state doc '${o.key}'.`);
|
|
128158
128397
|
} else if (o.outcome === "refused" || o.outcome === "failed") {
|
|
128159
|
-
console.error(`
|
|
128398
|
+
console.error(`Task ${task.slug}: state doc '${o.key}' ${o.outcome} \u2014 ${o.detail}`);
|
|
128160
128399
|
}
|
|
128161
128400
|
}
|
|
128162
128401
|
} catch (err) {
|
|
128163
128402
|
console.error(
|
|
128164
|
-
`
|
|
128403
|
+
`Task ${task.slug}: state-doc ship-back failed: ${err instanceof Error ? err.message : String(err)}`
|
|
128165
128404
|
);
|
|
128166
128405
|
}
|
|
128167
128406
|
}
|
|
@@ -128170,36 +128409,51 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128170
128409
|
const constraintsPath = join24(dir, "CONSTRAINTS.md");
|
|
128171
128410
|
const materializedConstraints = existsSync16(constraintsPath) ? readFileSync15(constraintsPath, "utf-8") : void 0;
|
|
128172
128411
|
if (materializedConstraints === void 0) return;
|
|
128173
|
-
const boot =
|
|
128412
|
+
const boot = task.constraintsMd ?? "";
|
|
128174
128413
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
128175
|
-
const reread = await sdk.loops.get(apiKey,
|
|
128414
|
+
const reread = await sdk.loops.get(apiKey, taskId);
|
|
128176
128415
|
if (reread.status !== "ok") {
|
|
128177
128416
|
constraintsReport = { outcome: "failed" };
|
|
128178
128417
|
console.error(
|
|
128179
|
-
`
|
|
128418
|
+
`Task ${task.slug}: ship-back ABORTED \u2014 could not re-read the record (${reread.error}). Refusing to merge against the stale boot snapshot; this fire's CONSTRAINTS.md is kept at ${constraintsPath} for recovery.`
|
|
128180
128419
|
);
|
|
128181
128420
|
return;
|
|
128182
128421
|
}
|
|
128183
128422
|
const fresh = reread.loop.constraintsMd ?? "";
|
|
128184
128423
|
const merged = mergeConstraints(boot, materializedConstraints, fresh);
|
|
128185
128424
|
if (merged.refusal) {
|
|
128186
|
-
const kept = preserveRefusedConstraints(
|
|
128425
|
+
const kept = preserveRefusedConstraints(task.slug, sessionId2, materializedConstraints, runRoot.root);
|
|
128187
128426
|
constraintsReport = { outcome: "refused", keptAt: kept.path ?? constraintsPath };
|
|
128188
128427
|
console.error(
|
|
128189
|
-
`
|
|
128428
|
+
`Task ${task.slug}: ${merged.refusal} This fire's constraints were NOT discarded: its folder is KEPT at ${dir}` + (kept.path ? `, and the refused document is copied to ${kept.path}.` : ` \u2014 but the durable copy could NOT be written (${kept.error}), so that folder is the only place it survives.`)
|
|
128190
128429
|
);
|
|
128191
128430
|
return;
|
|
128192
128431
|
}
|
|
128193
128432
|
if (merged.next === void 0) return;
|
|
128194
128433
|
if (!merged.cleanAppend && attempt === 1) {
|
|
128195
128434
|
console.warn(
|
|
128196
|
-
`
|
|
128435
|
+
`Task ${task.slug}: this fire edited existing constraints text, not only appended to it \u2014 shipping its own document. No section was lost (the shrink guard passed).`
|
|
128436
|
+
);
|
|
128437
|
+
}
|
|
128438
|
+
const put = await sdk.loops.update(apiKey, taskId, { constraintsMd: merged.next });
|
|
128439
|
+
if (put?.status !== "ok") {
|
|
128440
|
+
if (isForbidden(put ?? { status: "nok" })) {
|
|
128441
|
+
const owner = typeof task.createdBy === "string" ? task.createdBy : "someone else";
|
|
128442
|
+
constraintsReport = { outcome: "skipped-not-owner", owner };
|
|
128443
|
+
console.log(
|
|
128444
|
+
`Task ${task.slug}: ship-back skipped: this task is owned by ${owner}; your learnings stay in the run diary`
|
|
128445
|
+
);
|
|
128446
|
+
return;
|
|
128447
|
+
}
|
|
128448
|
+
constraintsReport = { outcome: "failed" };
|
|
128449
|
+
console.error(
|
|
128450
|
+
`Task ${task.slug}: ship-back failed: the record refused the write (${put?.error ?? "unknown error"}). This fire's CONSTRAINTS.md is kept at ${constraintsPath} for recovery.`
|
|
128197
128451
|
);
|
|
128452
|
+
return;
|
|
128198
128453
|
}
|
|
128199
|
-
await sdk.loops.
|
|
128200
|
-
const after = await sdk.loops.get(apiKey, loopId);
|
|
128454
|
+
const after = await sdk.loops.get(apiKey, taskId);
|
|
128201
128455
|
if (after.status !== "ok") {
|
|
128202
|
-
console.warn(`
|
|
128456
|
+
console.warn(`Task ${task.slug}: shipped back, but could not verify it landed.`);
|
|
128203
128457
|
return;
|
|
128204
128458
|
}
|
|
128205
128459
|
const afterText = after.loop.constraintsMd ?? "";
|
|
@@ -128208,28 +128462,28 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128208
128462
|
constraintsReport = { outcome: "shipped" };
|
|
128209
128463
|
if (merged.added.length > 0) {
|
|
128210
128464
|
console.log(
|
|
128211
|
-
`
|
|
128465
|
+
`Task ${task.slug}: shipped back ${merged.added.length} new constraints section(s).`
|
|
128212
128466
|
);
|
|
128213
128467
|
} else {
|
|
128214
|
-
console.log(`
|
|
128468
|
+
console.log(`Task ${task.slug}: shipped back a constraints rewrite (no new sections).`);
|
|
128215
128469
|
}
|
|
128216
128470
|
return;
|
|
128217
128471
|
}
|
|
128218
128472
|
if (attempt === 2) {
|
|
128219
128473
|
constraintsReport = { outcome: "failed" };
|
|
128220
128474
|
console.error(
|
|
128221
|
-
`
|
|
128475
|
+
`Task ${task.slug}: ship-back RACED and could not be repaired \u2014 ${missing.length} section(s) did not survive a concurrent write: ${missing.join(", ")}. They remain at ${constraintsPath}.`
|
|
128222
128476
|
);
|
|
128223
128477
|
return;
|
|
128224
128478
|
}
|
|
128225
128479
|
console.warn(
|
|
128226
|
-
`
|
|
128480
|
+
`Task ${task.slug}: a concurrent write clobbered ${missing.length} of our sections; retrying the merge.`
|
|
128227
128481
|
);
|
|
128228
128482
|
}
|
|
128229
128483
|
} catch (err) {
|
|
128230
128484
|
constraintsReport = { outcome: "failed" };
|
|
128231
128485
|
console.error(
|
|
128232
|
-
`
|
|
128486
|
+
`Task ${task.slug}: ship-back failed: ${err instanceof Error ? err.message : String(err)}`
|
|
128233
128487
|
);
|
|
128234
128488
|
}
|
|
128235
128489
|
}
|
|
@@ -128243,7 +128497,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128243
128497
|
void Promise.race([shipBackConstraints().then(() => "shipped"), deadline]).then((outcome) => {
|
|
128244
128498
|
if (outcome === "timeout") {
|
|
128245
128499
|
console.error(
|
|
128246
|
-
`
|
|
128500
|
+
`Task ${task.slug}: the ship-back did not finish within ${SHIPBACK_SIGNAL_TIMEOUT_MS}ms of the signal \u2014 exiting anyway so the shutdown is not held open. Nothing was discarded: this fire's rules remain at ${join24(dir, "CONSTRAINTS.md")}, and any state doc that did not get shipped remains beside it in ${dir}. The next fire can recover them by hand from there \u2014 nothing in the CLI reads a prior fire's folder automatically. \u26D4 Constraints are shipped FIRST, so the state docs are the likelier victim of this deadline \u2014 though a constraints ship-back that alone exceeds it truncates the rules too.`
|
|
128247
128501
|
);
|
|
128248
128502
|
}
|
|
128249
128503
|
}).finally(() => process.exit(130));
|
|
@@ -128251,19 +128505,19 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128251
128505
|
process.on("SIGINT", onSignal);
|
|
128252
128506
|
process.on("SIGTERM", onSignal);
|
|
128253
128507
|
let spawnError;
|
|
128254
|
-
const exitCode = await new Promise((
|
|
128255
|
-
child.on("exit", (code) =>
|
|
128508
|
+
const exitCode = await new Promise((resolve3) => {
|
|
128509
|
+
child.on("exit", (code) => resolve3(code ?? 1));
|
|
128256
128510
|
child.on("error", (err) => {
|
|
128257
128511
|
spawnError = err instanceof Error ? err.message : String(err);
|
|
128258
|
-
|
|
128512
|
+
resolve3(1);
|
|
128259
128513
|
});
|
|
128260
128514
|
});
|
|
128261
128515
|
if (child.pid) killTree(child.pid);
|
|
128262
128516
|
heartbeat.stop();
|
|
128263
128517
|
dashboard?.close();
|
|
128264
128518
|
const accounted = recordFireAccounting({
|
|
128265
|
-
|
|
128266
|
-
|
|
128519
|
+
runDir: dir,
|
|
128520
|
+
taskSlug: task.slug,
|
|
128267
128521
|
sessionId: sessionId2,
|
|
128268
128522
|
cwd: launch.cwd,
|
|
128269
128523
|
exitCode,
|
|
@@ -128271,8 +128525,8 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128271
128525
|
});
|
|
128272
128526
|
if (!accounted) {
|
|
128273
128527
|
recordFailedLaunch({
|
|
128274
|
-
|
|
128275
|
-
|
|
128528
|
+
runDir: dir,
|
|
128529
|
+
taskSlug: task.slug,
|
|
128276
128530
|
sessionId: sessionId2,
|
|
128277
128531
|
reason: spawnError ? `spawn failed: ${spawnError}` : `the fire produced no transcript (exit ${exitCode}) \u2014 it never started`,
|
|
128278
128532
|
exitCode,
|
|
@@ -128287,7 +128541,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128287
128541
|
if (clean2) {
|
|
128288
128542
|
if (constraintsReport.outcome === "refused") {
|
|
128289
128543
|
console.log(
|
|
128290
|
-
`
|
|
128544
|
+
`Task ${task.slug}: folder KEPT at ${dir} \u2014 the constraints ship-back was refused, so this fire's rules exist nowhere else. Recovery copy: ${constraintsReport.keptAt}.`
|
|
128291
128545
|
);
|
|
128292
128546
|
return { status: "clean", dir, shipBack: constraintsReport };
|
|
128293
128547
|
}
|
|
@@ -128297,29 +128551,29 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
128297
128551
|
return { status: "dirty", dir, shipBack: constraintsReport };
|
|
128298
128552
|
}
|
|
128299
128553
|
|
|
128300
|
-
// src/
|
|
128554
|
+
// src/tasks/show.ts
|
|
128301
128555
|
init_esm_shims();
|
|
128302
|
-
function
|
|
128556
|
+
function formatTaskFiles(task) {
|
|
128303
128557
|
const section = (title, body) => `
|
|
128304
128558
|
===== ${title} =====
|
|
128305
128559
|
${(body ?? "").trim() || "(empty)"}
|
|
128306
128560
|
`;
|
|
128307
|
-
return `
|
|
128308
|
-
` + section("SKILL.md",
|
|
128561
|
+
return `Task: ${task.slug} (${task.id})
|
|
128562
|
+
` + section("SKILL.md", task.markdownBody) + section("VISION.md", task.visionMd) + section("CONSTRAINTS.md", task.constraintsMd) + section("README.md", task.readmeMd) + section("stateDocs", formatMemoryManifest(task.stateDocs));
|
|
128309
128563
|
}
|
|
128310
|
-
async function
|
|
128564
|
+
async function showTask(taskId) {
|
|
128311
128565
|
const { sdk, apiKey } = await getCliSdk();
|
|
128312
|
-
const got = await sdk.loops.get(apiKey,
|
|
128313
|
-
if (got.status !== "ok") throw new Error(`
|
|
128314
|
-
console.log(
|
|
128566
|
+
const got = await sdk.loops.get(apiKey, taskId);
|
|
128567
|
+
if (got.status !== "ok") throw new Error(`task not found: ${got.error}`);
|
|
128568
|
+
console.log(formatTaskFiles(got.loop));
|
|
128315
128569
|
}
|
|
128316
128570
|
|
|
128317
|
-
// src/
|
|
128571
|
+
// src/tasks/schedule.ts
|
|
128318
128572
|
init_esm_shims();
|
|
128319
128573
|
init_paths();
|
|
128320
|
-
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync12, rmSync as rmSync3, existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync16, accessSync as
|
|
128574
|
+
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync12, rmSync as rmSync3, existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync16, accessSync as accessSync4, constants as constants4 } from "fs";
|
|
128321
128575
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
128322
|
-
import { join as join25, dirname as
|
|
128576
|
+
import { join as join25, dirname as dirname10 } from "path";
|
|
128323
128577
|
import { homedir as homedir14 } from "os";
|
|
128324
128578
|
var LOOP_KIND = {
|
|
128325
128579
|
labelPrefix: "xyz.ametyst.loop.",
|
|
@@ -128330,7 +128584,7 @@ var LOOP_KIND = {
|
|
|
128330
128584
|
return args;
|
|
128331
128585
|
},
|
|
128332
128586
|
stateDir(slug, cwd) {
|
|
128333
|
-
return join25(cwd, `.ametyst${ENV_SUFFIX}`,
|
|
128587
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, LEGACY_RUNS_SEGMENT, safeSlug2(slug, this.noun), ".state");
|
|
128334
128588
|
}
|
|
128335
128589
|
};
|
|
128336
128590
|
var TASK_KIND = {
|
|
@@ -128339,10 +128593,11 @@ var TASK_KIND = {
|
|
|
128339
128593
|
buildRunCmd(slug, opts) {
|
|
128340
128594
|
const args = [process.execPath, process.argv[1], "task", "run", slug];
|
|
128341
128595
|
if (opts.maxBudgetUsd != null) args.push("--max-budget-usd", String(opts.maxBudgetUsd));
|
|
128596
|
+
if (typeof opts.input === "string" && opts.input.trim() !== "") args.push("--input", opts.input);
|
|
128342
128597
|
return args;
|
|
128343
128598
|
},
|
|
128344
128599
|
stateDir(slug, cwd) {
|
|
128345
|
-
return join25(cwd, `.ametyst${ENV_SUFFIX}`,
|
|
128600
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, RUNS_SEGMENT, safeSlug2(slug, this.noun), ".state");
|
|
128346
128601
|
}
|
|
128347
128602
|
};
|
|
128348
128603
|
var COMPOUND_KIND = {
|
|
@@ -128450,7 +128705,7 @@ function validatedExtraEnv(extraEnv) {
|
|
|
128450
128705
|
}
|
|
128451
128706
|
function assertWritable(dir, what) {
|
|
128452
128707
|
try {
|
|
128453
|
-
|
|
128708
|
+
accessSync4(dir, constants4.W_OK);
|
|
128454
128709
|
} catch {
|
|
128455
128710
|
throw new Error(
|
|
128456
128711
|
`refusing to schedule: the ${what} ${dir} is not writable, so the job would be registered but every fire would die before writing any trace. Schedule from a writable project directory.`
|
|
@@ -128463,11 +128718,11 @@ function cadenceLabel(opts) {
|
|
|
128463
128718
|
function escapeXml(s) {
|
|
128464
128719
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
128465
128720
|
}
|
|
128466
|
-
function
|
|
128721
|
+
function shellQuote3(s) {
|
|
128467
128722
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
128468
128723
|
}
|
|
128469
128724
|
function shellArg(s) {
|
|
128470
|
-
return /^[A-Za-z0-9_@+=:,./-]+$/.test(s) ? s :
|
|
128725
|
+
return /^[A-Za-z0-9_@+=:,./-]+$/.test(s) ? s : shellQuote3(s);
|
|
128471
128726
|
}
|
|
128472
128727
|
function cronEscapePercent(s) {
|
|
128473
128728
|
return s.replace(/%/g, "\\%");
|
|
@@ -128543,7 +128798,19 @@ function schedule(kind, slug, opts = {}) {
|
|
|
128543
128798
|
const lbl = label(kind, slug);
|
|
128544
128799
|
const args = kind.buildRunCmd(slug, opts);
|
|
128545
128800
|
const cadence = cadenceLabel(opts);
|
|
128546
|
-
const
|
|
128801
|
+
const resolved = resolveRunRoot(void 0, {
|
|
128802
|
+
cwd: () => opts.cwd ?? process.cwd(),
|
|
128803
|
+
homedir: () => home
|
|
128804
|
+
});
|
|
128805
|
+
const cwd = resolved.root;
|
|
128806
|
+
if (resolved.reason === "fallback") {
|
|
128807
|
+
const why = (resolved.rejected ?? []).map((r) => `${r.dir}: ${r.why}`).join("; ");
|
|
128808
|
+
console.warn(`\u26A0\uFE0F the job will run in ${cwd} \u2014 the current folder cannot host a run (${why}).`);
|
|
128809
|
+
}
|
|
128810
|
+
if (kind === TASK_KIND) {
|
|
128811
|
+
const legacyHint = legacyRunFolderHint(slug, cwd);
|
|
128812
|
+
if (legacyHint) console.warn(legacyHint);
|
|
128813
|
+
}
|
|
128547
128814
|
const procEnv = opts.env ?? process.env;
|
|
128548
128815
|
const envPath = procEnv.PATH ?? "";
|
|
128549
128816
|
const model = resolveScheduledModel(opts, home, procEnv);
|
|
@@ -128566,8 +128833,7 @@ function schedule(kind, slug, opts = {}) {
|
|
|
128566
128833
|
})();
|
|
128567
128834
|
const path2 = plistPath(kind, home, slug);
|
|
128568
128835
|
const stateDir2 = kind.stateDir(slug, cwd);
|
|
128569
|
-
|
|
128570
|
-
mkdirSync12(dirname9(path2), { recursive: true });
|
|
128836
|
+
mkdirSync12(dirname10(path2), { recursive: true });
|
|
128571
128837
|
mkdirSync12(stateDir2, { recursive: true });
|
|
128572
128838
|
assertWritable(stateDir2, "log directory");
|
|
128573
128839
|
writeFileSync13(path2, plistXml(lbl, args, scheduleBlock, cwd, jobEnv, stateDir2), { mode: 384 });
|
|
@@ -128587,24 +128853,23 @@ launchctl said: ${unloaded.output.trim()}` : "")
|
|
|
128587
128853
|
launchctl said: ${loaded.output.trim()}` : "")
|
|
128588
128854
|
);
|
|
128589
128855
|
}
|
|
128590
|
-
return { label: lbl, cadence, workingDirectory: cwd, model: effectiveModel };
|
|
128856
|
+
return { label: lbl, cadence, workingDirectory: cwd, workingDirectoryReason: resolved.reason, model: effectiveModel };
|
|
128591
128857
|
}
|
|
128592
128858
|
const stateDir = kind.stateDir(slug, cwd);
|
|
128593
|
-
assertWritable(cwd, "working directory");
|
|
128594
128859
|
mkdirSync12(stateDir, { recursive: true });
|
|
128595
128860
|
assertWritable(stateDir, "log directory");
|
|
128596
128861
|
const cronEnv = { PATH: envPath };
|
|
128597
128862
|
if (model) cronEnv.ANTHROPIC_MODEL = model;
|
|
128598
128863
|
Object.assign(cronEnv, extraEnv);
|
|
128599
128864
|
const effectiveCronModel = cronEnv.ANTHROPIC_MODEL;
|
|
128600
|
-
const envPrefix = Object.entries(cronEnv).map(([k, v]) => `${k}=${
|
|
128865
|
+
const envPrefix = Object.entries(cronEnv).map(([k, v]) => `${k}=${shellQuote3(v)}`).join(" ");
|
|
128601
128866
|
const cmd = launcherArgv(args, stateDir).map(shellArg).join(" ");
|
|
128602
|
-
const command = cronEscapePercent(`cd ${
|
|
128867
|
+
const command = cronEscapePercent(`cd ${shellQuote3(cwd)} && ${envPrefix} ${cmd}`);
|
|
128603
128868
|
const line = `${cronField(opts)} ${command} # ${lbl}`;
|
|
128604
128869
|
const kept = stripLabel(readCrontab(), lbl);
|
|
128605
128870
|
kept.push(line);
|
|
128606
128871
|
writeCrontab(kept.join("\n"));
|
|
128607
|
-
return { label: lbl, cadence, workingDirectory: cwd, model: effectiveCronModel };
|
|
128872
|
+
return { label: lbl, cadence, workingDirectory: cwd, workingDirectoryReason: resolved.reason, model: effectiveCronModel };
|
|
128608
128873
|
}
|
|
128609
128874
|
function unschedule(kind, slug, opts = {}) {
|
|
128610
128875
|
const platform = opts.platform ?? process.platform;
|
|
@@ -128738,114 +129003,14 @@ function unscheduleEverywhere(slug, opts = {}) {
|
|
|
128738
129003
|
}
|
|
128739
129004
|
return removed;
|
|
128740
129005
|
}
|
|
128741
|
-
function scheduleLoop(slug, opts = {}) {
|
|
128742
|
-
return schedule(LOOP_KIND, slug, opts);
|
|
128743
|
-
}
|
|
128744
|
-
function unscheduleLoop(slug, opts = {}) {
|
|
128745
|
-
unschedule(LOOP_KIND, slug, opts);
|
|
128746
|
-
}
|
|
128747
|
-
function listScheduleEntries(opts = {}) {
|
|
128748
|
-
return listEntries(LOOP_KIND, opts);
|
|
128749
|
-
}
|
|
128750
|
-
function scheduleCompound(slug, opts = {}) {
|
|
128751
|
-
return schedule(COMPOUND_KIND, slug, opts);
|
|
128752
|
-
}
|
|
128753
|
-
function unscheduleCompound(slug, opts = {}) {
|
|
128754
|
-
unschedule(COMPOUND_KIND, slug, opts);
|
|
128755
|
-
}
|
|
128756
|
-
function listCompoundSchedules(opts = {}) {
|
|
128757
|
-
return list(COMPOUND_KIND, opts);
|
|
128758
|
-
}
|
|
128759
|
-
|
|
128760
|
-
// src/commands/task-verbs.ts
|
|
128761
|
-
init_esm_shims();
|
|
128762
|
-
|
|
128763
|
-
// src/compounds/index.ts
|
|
128764
|
-
init_esm_shims();
|
|
128765
|
-
|
|
128766
|
-
// src/compounds/run.ts
|
|
128767
|
-
init_esm_shims();
|
|
128768
|
-
import { spawn as spawn3 } from "child_process";
|
|
128769
|
-
|
|
128770
|
-
// src/compounds/launch.ts
|
|
128771
|
-
init_esm_shims();
|
|
128772
|
-
function buildCompoundLaunchArgs(body, opts = {}, slug) {
|
|
128773
|
-
const maxBudget = resolveCompoundMaxBudgetUsd(opts);
|
|
128774
|
-
const prompt = `You are running the Ametyst compound skill${slug ? ` "${slug}"` : ""} headless and unattended.
|
|
128775
|
-
|
|
128776
|
-
Follow these instructions exactly, then STOP (a compound is a one-shot skill \u2014 run it once, do not loop):
|
|
128777
|
-
|
|
128778
|
-
${body}
|
|
128779
|
-
|
|
128780
|
-
For any step that costs money, use the Ametyst \`spend\` MCP tool (the on-chain policy enforces the budget) \u2014 do NOT invent another payment path. When you have completed the instructions, exit.`;
|
|
128781
|
-
const args = [
|
|
128782
|
-
"-p",
|
|
128783
|
-
prompt,
|
|
128784
|
-
"--dangerously-skip-permissions",
|
|
128785
|
-
"--add-dir",
|
|
128786
|
-
process.cwd()
|
|
128787
|
-
];
|
|
128788
|
-
if (maxBudget !== void 0) {
|
|
128789
|
-
args.push("--max-budget-usd", String(maxBudget));
|
|
128790
|
-
}
|
|
128791
|
-
args.push("--settings", '{"env":{"ENABLE_TOOL_SEARCH":"false"}}');
|
|
128792
|
-
return {
|
|
128793
|
-
cmd: "claude",
|
|
128794
|
-
args,
|
|
128795
|
-
cwd: process.cwd()
|
|
128796
|
-
};
|
|
128797
|
-
}
|
|
128798
|
-
function resolveCompoundMaxBudgetUsd(opts, env = process.env) {
|
|
128799
|
-
if (opts.maxBudgetUsd !== void 0) return opts.maxBudgetUsd;
|
|
128800
|
-
const raw = env.AMETYST_COMPOUND_MAX_BUDGET_USD;
|
|
128801
|
-
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
128802
|
-
const n = Number(raw);
|
|
128803
|
-
return Number.isFinite(n) ? n : void 0;
|
|
128804
|
-
}
|
|
128805
129006
|
|
|
128806
|
-
// src/
|
|
128807
|
-
function killTree2(pid) {
|
|
128808
|
-
try {
|
|
128809
|
-
process.kill(-pid, "SIGTERM");
|
|
128810
|
-
} catch {
|
|
128811
|
-
}
|
|
128812
|
-
setTimeout(() => {
|
|
128813
|
-
try {
|
|
128814
|
-
process.kill(-pid, "SIGKILL");
|
|
128815
|
-
} catch {
|
|
128816
|
-
}
|
|
128817
|
-
}, 2e3).unref?.();
|
|
128818
|
-
}
|
|
128819
|
-
async function runCompound(compoundId, opts = {}) {
|
|
128820
|
-
const { sdk, apiKey } = await getCliSdk();
|
|
128821
|
-
const got = await sdk.compoundedSkills.get(apiKey, compoundId);
|
|
128822
|
-
if (got.status !== "ok") throw new Error(`compound not found: ${got.error}`);
|
|
128823
|
-
const compound = got.skill;
|
|
128824
|
-
const launch = buildCompoundLaunchArgs(compound.markdownBody ?? "", opts, compound.slug);
|
|
128825
|
-
const child = spawn3(launch.cmd, launch.args, { cwd: launch.cwd, stdio: "inherit", detached: true });
|
|
128826
|
-
const onSignal = () => {
|
|
128827
|
-
if (child.pid) killTree2(child.pid);
|
|
128828
|
-
process.exit(130);
|
|
128829
|
-
};
|
|
128830
|
-
process.on("SIGINT", onSignal);
|
|
128831
|
-
process.on("SIGTERM", onSignal);
|
|
128832
|
-
const exitCode = await new Promise((resolve) => {
|
|
128833
|
-
child.on("exit", (code) => resolve(code ?? 1));
|
|
128834
|
-
child.on("error", () => resolve(1));
|
|
128835
|
-
});
|
|
128836
|
-
process.off("SIGINT", onSignal);
|
|
128837
|
-
process.off("SIGTERM", onSignal);
|
|
128838
|
-
if (child.pid) killTree2(child.pid);
|
|
128839
|
-
return { exitCode };
|
|
128840
|
-
}
|
|
128841
|
-
|
|
128842
|
-
// src/compounds/push.ts
|
|
129007
|
+
// src/tasks/push.ts
|
|
128843
129008
|
init_esm_shims();
|
|
128844
|
-
async function
|
|
129009
|
+
async function pushTaskFromFile(filePath, opts) {
|
|
128845
129010
|
const slug = (opts.slug ?? "").trim();
|
|
128846
129011
|
const descriptionShort = (opts.descriptionShort ?? "").trim();
|
|
128847
|
-
if (!slug) throw new Error("A --slug is required to push a
|
|
128848
|
-
if (!descriptionShort) throw new Error("A --description is required to push a
|
|
129012
|
+
if (!slug) throw new Error("A --slug is required to push a task.");
|
|
129013
|
+
if (!descriptionShort) throw new Error("A --description is required to push a task.");
|
|
128849
129014
|
const markdownBody = readMarkdownFile(filePath);
|
|
128850
129015
|
let graphJson = {};
|
|
128851
129016
|
if (opts.graphJson && opts.graphJson.trim()) {
|
|
@@ -128860,23 +129025,20 @@ async function pushCompoundFromFile(filePath, opts) {
|
|
|
128860
129025
|
if (opts.category && opts.category.trim()) body.category = opts.category.trim();
|
|
128861
129026
|
const { sdk, apiKey } = await getCliSdk();
|
|
128862
129027
|
const id = opts.id && opts.id.trim() ? opts.id.trim() : void 0;
|
|
128863
|
-
const res = id ? await sdk.
|
|
129028
|
+
const res = id ? await sdk.tasks.update(apiKey, id, body) : await sdk.tasks.create(apiKey, body);
|
|
128864
129029
|
if (res.status !== "ok") {
|
|
128865
129030
|
throw new Error(`push failed: ${res.error ?? "unknown error"}${res.code ? ` (${res.code})` : ""}`);
|
|
128866
129031
|
}
|
|
128867
|
-
return { mode: id ? "modified" : "created",
|
|
129032
|
+
return { mode: id ? "modified" : "created", task: res.task };
|
|
128868
129033
|
}
|
|
128869
129034
|
|
|
128870
129035
|
// src/commands/task-verbs.ts
|
|
128871
|
-
|
|
128872
|
-
console.warn(
|
|
128873
|
-
`\u26A0\uFE0F \`ametyst ${oldInvocation}\` is DEPRECATED and still works unchanged \u2014 use \`ametyst ${newInvocation}\`.`
|
|
128874
|
-
);
|
|
128875
|
-
}
|
|
129036
|
+
init_esm_shims();
|
|
128876
129037
|
async function runTaskVerb(id, opts, flavor) {
|
|
128877
|
-
const r = await
|
|
129038
|
+
const r = await runTask(id, {
|
|
128878
129039
|
maxBudgetUsd: opts.maxBudgetUsd,
|
|
128879
|
-
maxConcurrentFires: opts.maxConcurrentFires
|
|
129040
|
+
maxConcurrentFires: opts.maxConcurrentFires,
|
|
129041
|
+
input: opts.input
|
|
128880
129042
|
});
|
|
128881
129043
|
console.log(
|
|
128882
129044
|
r.status === "clean" ? cleanFinishLine(flavor.noun, r.shipBack) : r.status === "skipped" ? "\u23ED\uFE0F nothing launched; the concurrency ceiling is already filled by live fires." : `\u23F8\uFE0F ${flavor.noun} paused; this fire's folder kept for resume at ${r.dir}.`
|
|
@@ -128892,15 +129054,17 @@ function cleanFinishLine(noun, shipBack) {
|
|
|
128892
129054
|
return `\u2705 ${noun} completed; the ship-back did NOT complete (see the error above), and this fire's folder is cleaned up.`;
|
|
128893
129055
|
case "shipped":
|
|
128894
129056
|
return `\u2705 ${noun} completed; improvements shipped back and this fire's folder cleaned up.`;
|
|
129057
|
+
case "skipped-not-owner":
|
|
129058
|
+
return `\u2705 ${noun} completed; ship-back skipped \u2014 this task is owned by ${shipBack.owner ?? "someone else"}, so your learnings stay in the run diary; this fire's folder is cleaned up.`;
|
|
128895
129059
|
}
|
|
128896
129060
|
}
|
|
128897
129061
|
async function showTaskVerb(id) {
|
|
128898
|
-
await
|
|
129062
|
+
await showTask(id);
|
|
128899
129063
|
}
|
|
128900
129064
|
async function pushTaskVerb(path2, opts, flavor) {
|
|
128901
129065
|
try {
|
|
128902
|
-
const r = await
|
|
128903
|
-
const c = r.
|
|
129066
|
+
const r = await pushTaskFromFile(path2, opts);
|
|
129067
|
+
const c = r.task;
|
|
128904
129068
|
console.log(`\u2705 ${flavor.noun} ${r.mode}: ${c?.slug ?? opts.slug}${c?.id ? ` (${c.id})` : ""}`);
|
|
128905
129069
|
} catch (err) {
|
|
128906
129070
|
console.error(`\u274C ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -128912,8 +129076,8 @@ function warnDoubleArm(noun, slug, found) {
|
|
|
128912
129076
|
const where = found.map((f) => `${f.namespace} (${f.label})`).join(", ");
|
|
128913
129077
|
console.warn(
|
|
128914
129078
|
`\u26A0\uFE0F ALREADY SCHEDULED ELSEWHERE \u2014 ${slug} is armed under: ${where}.
|
|
128915
|
-
Arming it here ADDS a SECOND job (xyz.ametyst.${noun}.${slug}) alongside it: launchd's singleton is per label, so the old job keeps firing and ${slug} runs TWICE per interval \u2014
|
|
128916
|
-
Nothing has been removed. To arm it ONCE, run \`ametyst task unschedule ${slug}\` (it removes the slug from EVERY namespace
|
|
129079
|
+
Arming it here ADDS a SECOND job (xyz.ametyst.${noun}.${slug}) alongside it: launchd's singleton is per label, so the old job keeps firing and ${slug} runs TWICE per interval \u2014 each job writing its own launchd.out.log (the legacy one under loops/${slug}/.state/, this one under tasks/${slug}/.state/) with a ledger line that names neither, so the duplicate ticks cannot be attributed.
|
|
129080
|
+
Nothing has been removed. To arm it ONCE, run \`ametyst task unschedule ${slug}\` (it removes the slug from EVERY label namespace, the legacy ones included) and schedule it again.`
|
|
128917
129081
|
);
|
|
128918
129082
|
}
|
|
128919
129083
|
function scheduleTaskVerb(slug, opts, flavor) {
|
|
@@ -128945,14 +129109,17 @@ function scheduleTaskVerb(slug, opts, flavor) {
|
|
|
128945
129109
|
// src/commands/task.ts
|
|
128946
129110
|
var TASK = { noun: "task" };
|
|
128947
129111
|
var taskCommand = new Command("task").description(
|
|
128948
|
-
"Manage and run Ametyst tasks \u2014
|
|
129112
|
+
"Manage and run Ametyst tasks \u2014 publish, run, show, schedule and sync them, and read their memory"
|
|
128949
129113
|
);
|
|
128950
129114
|
taskCommand.command("run <id>").description(
|
|
128951
|
-
"Materialize a task and run it headless, then ship back improvements on clean exit. While it runs, the task's dashboard (if it has one) is served on http://localhost:4477 (base port:
|
|
129115
|
+
"Materialize a task and run it headless, then ship back improvements on clean exit. While it runs, the task's dashboard (if it has one) is served on http://localhost:4477 (base port: AMETYST_TASK_DASHBOARD_PORT, walking up when taken) and opened in the browser once when stdout is a TTY (set AMETYST_DASHBOARD_NO_OPEN=1 to skip)"
|
|
128952
129116
|
).option("--max-budget-usd <x>", "hard spend ceiling in USD", (v) => Number(v)).option(
|
|
128953
129117
|
"--max-concurrent-fires <n>",
|
|
128954
|
-
"ceiling on simultaneously-live fires of this task; 0 = unlimited (default:
|
|
129118
|
+
"ceiling on simultaneously-live fires of this task; 0 = unlimited (default: AMETYST_TASK_MAX_CONCURRENT_FIRES, else 6)",
|
|
128955
129119
|
(v) => Number(v)
|
|
129120
|
+
).option(
|
|
129121
|
+
"--input <text>",
|
|
129122
|
+
`the user's arguments for this run, exactly as they would type them in chat (e.g. "run it on skyfire.com, cap $5"); reaches the task as AMETYST_TASK_INPUT and as a LAUNCH INPUT block in its instructions`
|
|
128956
129123
|
).action((id, opts) => runTaskVerb(id, opts, TASK));
|
|
128957
129124
|
taskCommand.command("show <id>").description(
|
|
128958
129125
|
"Print a task's stored files (SKILL/VISION/CONSTRAINTS/README) from Ametyst without running it"
|
|
@@ -128982,6 +129149,9 @@ taskCommand.command("schedule <slug>").description("Schedule a headless task run
|
|
|
128982
129149
|
).option(
|
|
128983
129150
|
"--model <id>",
|
|
128984
129151
|
"model to pin the scheduled fires to (default: ANTHROPIC_MODEL, else your Claude Code default at schedule time)"
|
|
129152
|
+
).option(
|
|
129153
|
+
"--input <text>",
|
|
129154
|
+
"the user's arguments for EVERY scheduled fire, persisted on the job's command line (same as `task run --input`)"
|
|
128985
129155
|
).option(
|
|
128986
129156
|
"--env <KEY=VALUE>",
|
|
128987
129157
|
"extra environment baked into the scheduled job; repeatable, and merged LAST so it wins over PATH/HOME/ANTHROPIC_MODEL",
|
|
@@ -129002,17 +129172,17 @@ taskCommand.command("schedule <slug>").description("Schedule a headless task run
|
|
|
129002
129172
|
})
|
|
129003
129173
|
);
|
|
129004
129174
|
taskCommand.command("unschedule <slug>").description(
|
|
129005
|
-
"Remove a task's schedule \u2014 from EVERY label namespace it is armed in
|
|
129175
|
+
"Remove a task's schedule \u2014 from EVERY label namespace it is armed in, the legacy ones an older cli used included, so a job scheduled before `ametyst task` existed is still removable"
|
|
129006
129176
|
).action((slug) => {
|
|
129007
129177
|
const removed = unscheduleEverywhere(slug);
|
|
129008
129178
|
if (removed.length === 0) {
|
|
129009
|
-
console.log(`\u2139\uFE0F nothing scheduled for ${slug} \u2014 no
|
|
129179
|
+
console.log(`\u2139\uFE0F nothing scheduled for ${slug} \u2014 no job was armed under any label namespace.`);
|
|
129010
129180
|
return;
|
|
129011
129181
|
}
|
|
129012
129182
|
console.log(`\u{1F5D1}\uFE0F unscheduled ${slug} (${removed.join(", ")})`);
|
|
129013
129183
|
});
|
|
129014
129184
|
taskCommand.command("schedules").description(
|
|
129015
|
-
"List every scheduled job and the environment each armed job carries \u2014 across
|
|
129185
|
+
"List every scheduled job and the environment each armed job carries \u2014 across every label namespace, the legacy ones an older cli used included"
|
|
129016
129186
|
).action(() => {
|
|
129017
129187
|
const entries = listAllScheduleEntries();
|
|
129018
129188
|
console.log(
|
|
@@ -129021,6 +129191,25 @@ taskCommand.command("schedules").description(
|
|
|
129021
129191
|
).join("\n") : "no scheduled tasks"
|
|
129022
129192
|
);
|
|
129023
129193
|
});
|
|
129194
|
+
taskCommand.command("sync-skills").description(
|
|
129195
|
+
"Write a local pointer skill per published task into the host agent's skills directory \u2014 .claude/skills/<slug>/SKILL.md for Claude, .codex/skills/<slug>/SKILL.md for Codex (restores /-slash-command invocation in file-based hosts)"
|
|
129196
|
+
).option("--global", "write to the home-dir root (~/.claude/skills | ~/.codex/skills) instead of the project-local one").option(
|
|
129197
|
+
"--target <target>",
|
|
129198
|
+
"host agent skills dir to sync: claude | codex | both (default: auto \u2014 every host detected as present; none detected falls back to claude)"
|
|
129199
|
+
).action(async (opts) => {
|
|
129200
|
+
try {
|
|
129201
|
+
const targets = resolveSyncSkillsCliTargets(opts.target);
|
|
129202
|
+
for (const target of targets) {
|
|
129203
|
+
const r = await syncSkills({ global: opts.global, target });
|
|
129204
|
+
console.log(
|
|
129205
|
+
`\u2705 sync-skills [${target}]: ${r.written} written, ${r.pruned} pruned${r.skipped.length ? `, ${r.skipped.length} skipped (unmanaged): ${r.skipped.join(", ")}` : ""} \u2192 ${r.root}${r.fallback ? ` (fallback to the home root: ${r.fallback.why}; project-local ${r.fallback.from} was not used)` : ""}`
|
|
129206
|
+
);
|
|
129207
|
+
}
|
|
129208
|
+
} catch (err) {
|
|
129209
|
+
console.error(`\u274C ${err instanceof Error ? err.message : String(err)}`);
|
|
129210
|
+
process.exitCode = 1;
|
|
129211
|
+
}
|
|
129212
|
+
});
|
|
129024
129213
|
function runVerb(fn) {
|
|
129025
129214
|
fn().catch((err) => {
|
|
129026
129215
|
console.error(`\u274C ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -129052,130 +129241,6 @@ memoryCommand.command("attach <slug>", { hidden: true }).allowUnknownOption(true
|
|
|
129052
129241
|
);
|
|
129053
129242
|
taskCommand.addCommand(memoryCommand);
|
|
129054
129243
|
|
|
129055
|
-
// src/commands/loop.ts
|
|
129056
|
-
init_esm_shims();
|
|
129057
|
-
var LOOP = { noun: "loop" };
|
|
129058
|
-
var loopCommand = new Command("loop").description(
|
|
129059
|
-
"DEPRECATED alias of `ametyst task` \u2014 manage and run Ametyst loops (still works unchanged)"
|
|
129060
|
-
);
|
|
129061
|
-
loopCommand.command("run <id>").description(
|
|
129062
|
-
"DEPRECATED \u2014 use `ametyst task run`. Materialize a loop and run it headless, then ship back improvements on clean exit"
|
|
129063
|
-
).option("--max-budget-usd <x>", "hard spend ceiling in USD", (v) => Number(v)).option(
|
|
129064
|
-
"--max-concurrent-fires <n>",
|
|
129065
|
-
"ceiling on simultaneously-live fires of this loop; 0 = unlimited (default: AMETYST_LOOP_MAX_CONCURRENT_FIRES, else 6)",
|
|
129066
|
-
(v) => Number(v)
|
|
129067
|
-
).action((id, opts) => {
|
|
129068
|
-
warnDeprecated("loop run", "task run");
|
|
129069
|
-
return runTaskVerb(id, opts, LOOP);
|
|
129070
|
-
});
|
|
129071
|
-
loopCommand.command("show <id>").description(
|
|
129072
|
-
"DEPRECATED \u2014 use `ametyst task show`. Print a loop's stored files (SKILL/VISION/CONSTRAINTS/README) from Ametyst without running it"
|
|
129073
|
-
).action((id) => {
|
|
129074
|
-
warnDeprecated("loop show", "task show");
|
|
129075
|
-
return showTaskVerb(id);
|
|
129076
|
-
});
|
|
129077
|
-
loopCommand.command("schedule <slug>").description(
|
|
129078
|
-
"DEPRECATED \u2014 use `ametyst task schedule`. Schedule a headless loop run (launchd on macOS, crontab on Linux)"
|
|
129079
|
-
).option("--at <hhmm>", "daily run time, 24h HH:MM (e.g. 02:30)").option("--every <dur>", "recurring interval, e.g. 30m, 1h, 1d").option("--max-budget-usd <x>", "optional hard spend ceiling per run in USD (opt-in; omitted by default)", (v) => Number(v)).option("--model <id>", "model to pin the scheduled fires to (default: ANTHROPIC_MODEL, else your Claude Code default at schedule time)").option(
|
|
129080
|
-
"--env <KEY=VALUE>",
|
|
129081
|
-
"extra environment baked into the scheduled job; repeatable, and merged LAST so it wins over PATH/HOME/ANTHROPIC_MODEL",
|
|
129082
|
-
(v, prev = []) => [...prev, v],
|
|
129083
|
-
[]
|
|
129084
|
-
).action((slug, opts) => {
|
|
129085
|
-
warnDeprecated("loop schedule", "task schedule");
|
|
129086
|
-
scheduleTaskVerb(slug, opts, { ...LOOP, arm: scheduleLoop });
|
|
129087
|
-
});
|
|
129088
|
-
loopCommand.command("unschedule <slug>").description(
|
|
129089
|
-
"DEPRECATED \u2014 use `ametyst task unschedule` (which also removes jobs armed under the loop/compound labels). Remove a loop's schedule"
|
|
129090
|
-
).action((slug) => {
|
|
129091
|
-
warnDeprecated("loop unschedule", "task unschedule");
|
|
129092
|
-
unscheduleLoop(slug);
|
|
129093
|
-
console.log(`\u{1F5D1}\uFE0F unscheduled loop ${slug}`);
|
|
129094
|
-
});
|
|
129095
|
-
loopCommand.command("schedules").description(
|
|
129096
|
-
"DEPRECATED \u2014 use `ametyst task schedules` (which also lists jobs armed under the loop/compound labels). List scheduled loops and the environment each armed job carries"
|
|
129097
|
-
).action(() => {
|
|
129098
|
-
warnDeprecated("loop schedules", "task schedules");
|
|
129099
|
-
const entries = listScheduleEntries();
|
|
129100
|
-
console.log(
|
|
129101
|
-
entries.length ? entries.map((e) => `${e.slug} env: ${e.envKeys.length ? e.envKeys.join(", ") : "\u2014"}`).join("\n") : "no scheduled loops"
|
|
129102
|
-
);
|
|
129103
|
-
});
|
|
129104
|
-
|
|
129105
|
-
// src/commands/compound.ts
|
|
129106
|
-
init_esm_shims();
|
|
129107
|
-
var COMPOUND = { noun: "compound" };
|
|
129108
|
-
var compoundCommand = new Command("compound").description(
|
|
129109
|
-
"DEPRECATED alias-era group \u2014 prefer `ametyst task`. Run Ametyst compound skills (still works unchanged)"
|
|
129110
|
-
);
|
|
129111
|
-
compoundCommand.command("push <path>").description("DEPRECATED \u2014 use `ametyst task push`. Publish a compound whose body is read verbatim (byte-for-byte) from a local markdown file").requiredOption("--slug <slug>", "URL-safe unique slug for the compound").requiredOption("--description <text>", "one-line description of what the compound does").option("--category <category>", "free-text category for browsing/filtering").option("--graph-json <json>", "JSON string of the canvas node graph (defaults to {})").option("--id <id>", "MODIFY the existing compound with this id instead of creating a new one").option(
|
|
129112
|
-
"--draft <bool>",
|
|
129113
|
-
"draft state: 'true' to keep as draft (default on create), 'false' to publish",
|
|
129114
|
-
(v) => v === "true" || v === "1" || v === "yes"
|
|
129115
|
-
).action(
|
|
129116
|
-
(path2, opts) => {
|
|
129117
|
-
warnDeprecated("compound push", "task push");
|
|
129118
|
-
return pushTaskVerb(
|
|
129119
|
-
path2,
|
|
129120
|
-
{
|
|
129121
|
-
slug: opts.slug,
|
|
129122
|
-
descriptionShort: opts.description,
|
|
129123
|
-
category: opts.category,
|
|
129124
|
-
graphJson: opts.graphJson,
|
|
129125
|
-
id: opts.id,
|
|
129126
|
-
draft: opts.draft
|
|
129127
|
-
},
|
|
129128
|
-
COMPOUND
|
|
129129
|
-
);
|
|
129130
|
-
}
|
|
129131
|
-
);
|
|
129132
|
-
compoundCommand.command("sync-skills").description(
|
|
129133
|
-
"Write a local pointer skill per published compound/loop into the host agent's skills directory \u2014 .claude/skills/<slug>/SKILL.md for Claude, .codex/skills/<slug>/SKILL.md for Codex (restores /-slash-command invocation in file-based hosts)"
|
|
129134
|
-
).option("--global", "write to the home-dir root (~/.claude/skills | ~/.codex/skills) instead of the project-local one").option(
|
|
129135
|
-
"--target <target>",
|
|
129136
|
-
"host agent skills dir to sync: claude | codex | both (default: auto \u2014 every host detected as present; none detected falls back to claude)"
|
|
129137
|
-
).action(async (opts) => {
|
|
129138
|
-
try {
|
|
129139
|
-
const targets = resolveSyncSkillsCliTargets(opts.target);
|
|
129140
|
-
for (const target of targets) {
|
|
129141
|
-
const r = await syncSkills({ global: opts.global, target });
|
|
129142
|
-
console.log(
|
|
129143
|
-
`\u2705 sync-skills [${target}]: ${r.written} written, ${r.pruned} pruned${r.skipped.length ? `, ${r.skipped.length} skipped (unmanaged): ${r.skipped.join(", ")}` : ""} \u2192 ${r.root}`
|
|
129144
|
-
);
|
|
129145
|
-
}
|
|
129146
|
-
} catch (err) {
|
|
129147
|
-
console.error(`\u274C ${err instanceof Error ? err.message : String(err)}`);
|
|
129148
|
-
process.exitCode = 1;
|
|
129149
|
-
}
|
|
129150
|
-
});
|
|
129151
|
-
compoundCommand.command("run <id>").description(
|
|
129152
|
-
"DEPRECATED \u2014 prefer `ametyst task run`, which materializes the task's definition files and ships constraints back. This one-shot runner is kept UNCHANGED: it runs the compound body with no folder, no STATUS and no ship-back"
|
|
129153
|
-
).option("--max-budget-usd <x>", "optional hard spend ceiling in USD (opt-in; omitted by default)", (v) => Number(v)).option("--dangerously-skip-permissions", "run fully unattended (already implied for compounds)").action(async (id, opts) => {
|
|
129154
|
-
warnDeprecated("compound run", "task run");
|
|
129155
|
-
const r = await runCompound(id, { maxBudgetUsd: opts.maxBudgetUsd });
|
|
129156
|
-
console.log(
|
|
129157
|
-
r.exitCode === 0 ? "\u2705 compound completed." : `\u26A0\uFE0F compound exited with code ${r.exitCode}.`
|
|
129158
|
-
);
|
|
129159
|
-
});
|
|
129160
|
-
compoundCommand.command("schedule <slug>").description("DEPRECATED \u2014 use `ametyst task schedule`. Schedule a headless compound run (launchd on macOS, crontab on Linux)").option("--at <hhmm>", "daily run time, 24h HH:MM (e.g. 02:30)").option("--every <dur>", "recurring interval, e.g. 30m, 1h, 1d").option("--max-budget-usd <x>", "optional hard spend ceiling per run in USD (opt-in; omitted by default)", (v) => Number(v)).option("--model <id>", "model to pin the scheduled fires to (default: ANTHROPIC_MODEL, else your Claude Code default at schedule time)").action((slug, opts) => {
|
|
129161
|
-
warnDeprecated("compound schedule", "task schedule");
|
|
129162
|
-
scheduleTaskVerb(slug, opts, { ...COMPOUND, arm: scheduleCompound });
|
|
129163
|
-
});
|
|
129164
|
-
compoundCommand.command("unschedule <slug>").description(
|
|
129165
|
-
"DEPRECATED \u2014 use `ametyst task unschedule` (which also removes jobs armed under the task/loop labels). Remove a compound's schedule"
|
|
129166
|
-
).action((slug) => {
|
|
129167
|
-
warnDeprecated("compound unschedule", "task unschedule");
|
|
129168
|
-
unscheduleCompound(slug);
|
|
129169
|
-
console.log(`\u{1F5D1}\uFE0F unscheduled compound ${slug}`);
|
|
129170
|
-
});
|
|
129171
|
-
compoundCommand.command("schedules").description(
|
|
129172
|
-
"DEPRECATED \u2014 use `ametyst task schedules` (which also lists jobs armed under the task/loop labels). List scheduled compounds"
|
|
129173
|
-
).action(() => {
|
|
129174
|
-
warnDeprecated("compound schedules", "task schedules");
|
|
129175
|
-
const s = listCompoundSchedules();
|
|
129176
|
-
console.log(s.length ? s.join("\n") : "no scheduled compounds");
|
|
129177
|
-
});
|
|
129178
|
-
|
|
129179
129244
|
// src/commands/delegate.ts
|
|
129180
129245
|
init_esm_shims();
|
|
129181
129246
|
function toDelegateOptions(task, opts) {
|
|
@@ -129266,8 +129331,6 @@ program2.command("delegate [task]").description(
|
|
|
129266
129331
|
).action(delegateCommand);
|
|
129267
129332
|
program2.addCommand(connectionsCommand);
|
|
129268
129333
|
program2.addCommand(taskCommand);
|
|
129269
|
-
program2.addCommand(loopCommand);
|
|
129270
|
-
program2.addCommand(compoundCommand);
|
|
129271
129334
|
program2.addCommand(walletCommand);
|
|
129272
129335
|
program2.parseAsync().catch((e) => {
|
|
129273
129336
|
console.error(e instanceof Error ? e.message : String(e));
|