@coreplane/switchboard 1.225.0 → 1.226.1
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/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +1 -1
- package/dist/assets/src/core/chatMessage.ts +77 -0
- package/dist/assets/src/{providers/types.ts → core/provider.ts} +21 -71
- package/dist/assets/src/core/reviewVerdict.ts +1 -1
- package/dist/assets/src/core/runEvents.ts +22 -3
- package/dist/assets/src/core/runLedger/sessionLog.ts +1 -1
- package/dist/assets/src/core/runLedger/transcript.ts +1 -1
- package/dist/assets/src/core/runLedger/types.ts +2 -1
- package/dist/assets/web/dist/.vite/manifest.json +20 -20
- package/dist/assets/web/dist/assets/CostsPage-5pl3HB2F.js +2 -0
- package/dist/assets/web/dist/assets/{ResidentDetailPage-Cu4Ve2j4.js → ResidentDetailPage-BBOpejGX.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-_QIDVyOj.js → ResidentsIndexPage-B8kFWHpB.js} +1 -1
- package/dist/assets/web/dist/assets/RunRoutePage-COWeVtIE.js +12 -0
- package/dist/assets/web/dist/assets/{RunsIndexPage-JQvnDmo0.js → RunsIndexPage-BjH93cKx.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-DEVRWqZi.js → ScheduledPage-grlNKvCA.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-BDAKF1Bp.js → StatusDot-BQNoaXO6.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-BFncyNUQ.js → Tooltip-DVCeIzaa.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-DJfQXPjH.js → dist-B5Wfk-oY.js} +1 -1
- package/dist/assets/web/dist/assets/{main-BjhiJxQq.js → main-CN0U7d6s.js} +2 -2
- package/dist/assets/web/dist/assets/main-xLAsdkfB.css +1 -0
- package/dist/cli.js +1228 -930
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/CostsPage-BlPwsI2T.js +0 -3
- package/dist/assets/web/dist/assets/RunRoutePage-DxgcHX-0.js +0 -12
- package/dist/assets/web/dist/assets/main-DymOuq2l.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -11448,25 +11448,7 @@ var init_config3 = __esm({
|
|
|
11448
11448
|
}
|
|
11449
11449
|
});
|
|
11450
11450
|
|
|
11451
|
-
// ../../src/
|
|
11452
|
-
function toolResultText(content) {
|
|
11453
|
-
if (typeof content === "string") return content;
|
|
11454
|
-
return content.map((p) => {
|
|
11455
|
-
if (p.type === "text") return p.text;
|
|
11456
|
-
if (p.type === "image") return `[image ${p.mediaType}, ${base64Bytes(p.data)} bytes]`;
|
|
11457
|
-
return `[document ${p.name ?? "document"} (${p.mediaType}), ${base64Bytes(p.data)} bytes]`;
|
|
11458
|
-
}).join("\n");
|
|
11459
|
-
}
|
|
11460
|
-
function capToolResultContent(content, cap = MAX_TOOL_RESULT_CHARS) {
|
|
11461
|
-
const capText = (text) => text.length > cap ? `${text.slice(0, cap)}
|
|
11462
|
-
\u2026[tool result truncated: ${text.length - cap} of ${text.length} characters cut \u2014 ask for a narrower slice]` : text;
|
|
11463
|
-
if (typeof content === "string") return capText(content);
|
|
11464
|
-
return content.map((p) => p.type === "text" ? { ...p, text: capText(p.text) } : p);
|
|
11465
|
-
}
|
|
11466
|
-
function base64Bytes(b64) {
|
|
11467
|
-
const padding = b64.endsWith("==") ? 2 : b64.endsWith("=") ? 1 : 0;
|
|
11468
|
-
return Math.floor(b64.length * 3 / 4) - padding;
|
|
11469
|
-
}
|
|
11451
|
+
// ../../src/core/provider.ts
|
|
11470
11452
|
function parseModelRef(ref) {
|
|
11471
11453
|
const i = ref.indexOf("/");
|
|
11472
11454
|
if (i === -1) {
|
|
@@ -11474,11 +11456,10 @@ function parseModelRef(ref) {
|
|
|
11474
11456
|
}
|
|
11475
11457
|
return { provider: ref.slice(0, i), model: ref.slice(i + 1) };
|
|
11476
11458
|
}
|
|
11477
|
-
var
|
|
11478
|
-
var
|
|
11479
|
-
"../../src/
|
|
11459
|
+
var ANTHROPIC_API_KEY_ENV;
|
|
11460
|
+
var init_provider = __esm({
|
|
11461
|
+
"../../src/core/provider.ts"() {
|
|
11480
11462
|
"use strict";
|
|
11481
|
-
MAX_TOOL_RESULT_CHARS = 12e4;
|
|
11482
11463
|
ANTHROPIC_API_KEY_ENV = "ANTHROPIC_API_KEY";
|
|
11483
11464
|
}
|
|
11484
11465
|
});
|
|
@@ -11608,7 +11589,7 @@ var init_anthropic = __esm({
|
|
|
11608
11589
|
"../../src/providers/anthropic.ts"() {
|
|
11609
11590
|
"use strict";
|
|
11610
11591
|
init_secrets2();
|
|
11611
|
-
|
|
11592
|
+
init_provider();
|
|
11612
11593
|
AnthropicProvider = class {
|
|
11613
11594
|
name;
|
|
11614
11595
|
client;
|
|
@@ -15954,7 +15935,7 @@ function isRunEvent(v) {
|
|
|
15954
15935
|
case "skill_use":
|
|
15955
15936
|
return typeof o.skill === "string" && typeof o.agent === "string" && typeof o.bodyBytes === "number";
|
|
15956
15937
|
case "artifact":
|
|
15957
|
-
return (o.direction === "in" || o.direction === "out") && typeof o.key === "string" && typeof o.name === "string" && typeof o.size === "number" && typeof o.contentType === "string" && o.url === void 0;
|
|
15938
|
+
return (o.direction === "in" && typeof o.messageId === "string" || o.direction === "out" && typeof o.callId === "string") && typeof o.key === "string" && typeof o.name === "string" && typeof o.size === "number" && typeof o.contentType === "string" && o.url === void 0;
|
|
15958
15939
|
case "review_artifact":
|
|
15959
15940
|
if (o.artifact === "pr_description") {
|
|
15960
15941
|
return (o.origin === "submitted" || o.origin === "parsed") && typeof o.repo === "string" && typeof o.pr === "number" && typeof o.title === "string" && typeof o.body === "string" && Array.isArray(o.tour);
|
|
@@ -16733,7 +16714,7 @@ var init_connect = __esm({
|
|
|
16733
16714
|
|
|
16734
16715
|
// ../../src/mcp/types.ts
|
|
16735
16716
|
var McpError;
|
|
16736
|
-
var
|
|
16717
|
+
var init_types2 = __esm({
|
|
16737
16718
|
"../../src/mcp/types.ts"() {
|
|
16738
16719
|
"use strict";
|
|
16739
16720
|
McpError = class extends Error {
|
|
@@ -16828,7 +16809,7 @@ var MCP_PROTOCOL_VERSION, MCP_REQUEST_TIMEOUT_MS, MCP_MAX_RESPONSE_BYTES, MCP_MA
|
|
|
16828
16809
|
var init_client = __esm({
|
|
16829
16810
|
"../../src/mcp/client.ts"() {
|
|
16830
16811
|
"use strict";
|
|
16831
|
-
|
|
16812
|
+
init_types2();
|
|
16832
16813
|
MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
16833
16814
|
MCP_REQUEST_TIMEOUT_MS = 3e4;
|
|
16834
16815
|
MCP_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
@@ -17543,7 +17524,7 @@ var init_bridge = __esm({
|
|
|
17543
17524
|
"../../src/mcp/bridge.ts"() {
|
|
17544
17525
|
"use strict";
|
|
17545
17526
|
init_commandRegistry();
|
|
17546
|
-
|
|
17527
|
+
init_types2();
|
|
17547
17528
|
init_classify();
|
|
17548
17529
|
MCP_TOOL_NAME_MAX = 64;
|
|
17549
17530
|
MCP_TOOL_PREFIX = "mcp__";
|
|
@@ -18113,23 +18094,31 @@ var init_service = __esm({
|
|
|
18113
18094
|
callbackUrl() {
|
|
18114
18095
|
return `${(this.opts.publicBaseUrl ?? "").replace(/\/+$/, "")}/mcp/oauth/callback`;
|
|
18115
18096
|
}
|
|
18116
|
-
/**
|
|
18097
|
+
/** Refreshes by server id, keyed by the expiry of the credential that
|
|
18098
|
+
* triggered them, so N concurrent runs refresh once. A settled entry stays:
|
|
18099
|
+
* a run whose credential read raced the store write joins it instead of
|
|
18100
|
+
* refreshing again. A failed refresh is dropped so the next run retries; a
|
|
18101
|
+
* newer expiry (the refreshed set aging into the skew) starts a new one. */
|
|
18117
18102
|
refreshing = /* @__PURE__ */ new Map();
|
|
18118
18103
|
/** A usable access token for the run: the stored one while it lives, else
|
|
18119
18104
|
* a refreshed set (stored back, the cached client dropped so it is rebuilt
|
|
18120
18105
|
* with the new header). */
|
|
18121
18106
|
freshOAuth(serverId, cred) {
|
|
18122
18107
|
if (!needsRefresh(cred, this.now())) return Promise.resolve(cred);
|
|
18123
|
-
|
|
18124
|
-
|
|
18125
|
-
|
|
18126
|
-
|
|
18127
|
-
|
|
18128
|
-
|
|
18129
|
-
|
|
18130
|
-
|
|
18131
|
-
}
|
|
18132
|
-
|
|
18108
|
+
const seed = cred.expiresAt ?? 0;
|
|
18109
|
+
const hit = this.refreshing.get(serverId);
|
|
18110
|
+
if (hit && hit.seed >= seed) return hit.done;
|
|
18111
|
+
const done = (async () => {
|
|
18112
|
+
const next = await refreshCredential(this.opts.fetch, cred, this.now());
|
|
18113
|
+
await this.storeOAuth(serverId, next);
|
|
18114
|
+
return next;
|
|
18115
|
+
})();
|
|
18116
|
+
const entry = { seed, done };
|
|
18117
|
+
this.refreshing.set(serverId, entry);
|
|
18118
|
+
done.catch(() => {
|
|
18119
|
+
if (this.refreshing.get(serverId) === entry) this.refreshing.delete(serverId);
|
|
18120
|
+
});
|
|
18121
|
+
return done;
|
|
18133
18122
|
}
|
|
18134
18123
|
// ---- the run-time view ----------------------------------------------------------
|
|
18135
18124
|
/** Servers a run may use: every tier's entries whose agents include the
|
|
@@ -18841,7 +18830,7 @@ var init_authz = __esm({
|
|
|
18841
18830
|
});
|
|
18842
18831
|
|
|
18843
18832
|
// ../../src/core/llmOutput/types.ts
|
|
18844
|
-
var
|
|
18833
|
+
var init_types3 = __esm({
|
|
18845
18834
|
"../../src/core/llmOutput/types.ts"() {
|
|
18846
18835
|
"use strict";
|
|
18847
18836
|
}
|
|
@@ -18900,10 +18889,26 @@ var init_markdown = __esm({
|
|
|
18900
18889
|
});
|
|
18901
18890
|
|
|
18902
18891
|
// ../../src/core/llmOutput/json.ts
|
|
18903
|
-
function
|
|
18904
|
-
const
|
|
18905
|
-
|
|
18906
|
-
|
|
18892
|
+
function extractJson(text) {
|
|
18893
|
+
const start3 = text.search(/[[{]/);
|
|
18894
|
+
if (start3 === -1) return text.trim();
|
|
18895
|
+
let depth = 0;
|
|
18896
|
+
let inString = false;
|
|
18897
|
+
for (let i = start3; i < text.length; i++) {
|
|
18898
|
+
const ch = text[i];
|
|
18899
|
+
if (inString) {
|
|
18900
|
+
if (ch === "\\") i++;
|
|
18901
|
+
else if (ch === '"') inString = false;
|
|
18902
|
+
} else if (ch === '"') {
|
|
18903
|
+
inString = true;
|
|
18904
|
+
} else if (ch === "{" || ch === "[") {
|
|
18905
|
+
depth++;
|
|
18906
|
+
} else if (ch === "}" || ch === "]") {
|
|
18907
|
+
depth--;
|
|
18908
|
+
if (depth === 0) return text.slice(start3, i + 1);
|
|
18909
|
+
}
|
|
18910
|
+
}
|
|
18911
|
+
return text.slice(start3);
|
|
18907
18912
|
}
|
|
18908
18913
|
function jsonValueEquals(a, b) {
|
|
18909
18914
|
if (Object.is(a, b)) return true;
|
|
@@ -18923,7 +18928,7 @@ function jsonOutput(schema, opts = {}) {
|
|
|
18923
18928
|
parse(raw) {
|
|
18924
18929
|
let value;
|
|
18925
18930
|
try {
|
|
18926
|
-
value = JSON.parse(
|
|
18931
|
+
value = JSON.parse(extractJson(raw));
|
|
18927
18932
|
} catch (err2) {
|
|
18928
18933
|
const observed = `not valid JSON: ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
18929
18934
|
return { ok: false, failure: { kind: "syntax", observed } };
|
|
@@ -18956,7 +18961,7 @@ var init_json = __esm({
|
|
|
18956
18961
|
var init_llmOutput = __esm({
|
|
18957
18962
|
"../../src/core/llmOutput/index.ts"() {
|
|
18958
18963
|
"use strict";
|
|
18959
|
-
|
|
18964
|
+
init_types3();
|
|
18960
18965
|
init_markdown();
|
|
18961
18966
|
init_json();
|
|
18962
18967
|
}
|
|
@@ -19085,6 +19090,8 @@ function placeCandidate(cand, actor, origin, keys, scopeOf) {
|
|
|
19085
19090
|
async function reflect(deps) {
|
|
19086
19091
|
const warn = deps.onWarn ?? (() => {
|
|
19087
19092
|
});
|
|
19093
|
+
const info = deps.onInfo ?? (() => {
|
|
19094
|
+
});
|
|
19088
19095
|
try {
|
|
19089
19096
|
const query = `${deps.request} ${deps.answer}`.slice(0, MAX_RETRIEVE_QUERY_CHARS);
|
|
19090
19097
|
const existing = (await Promise.all(
|
|
@@ -19102,11 +19109,18 @@ async function reflect(deps) {
|
|
|
19102
19109
|
const reply = result.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
19103
19110
|
const prov = { sourceThreadKey: deps.sourceThreadKey, sourceRunId: deps.sourceRunId };
|
|
19104
19111
|
const parsed = parseReflection(reply, prov, new Set(existing.map((r) => r.id)));
|
|
19112
|
+
const truncated = result.stopReason === "max_tokens" ? `truncated at ${REFLECTION_MAX_TOKENS} max tokens` : void 0;
|
|
19113
|
+
const stopNote = truncated ?? (result.stopReason === "end_turn" ? void 0 : `stop: ${result.stopReason}`);
|
|
19105
19114
|
if (!parsed.ok) {
|
|
19106
|
-
|
|
19115
|
+
const why = truncated ?? (stopNote ? `${parsed.error}; ${stopNote}` : parsed.error);
|
|
19116
|
+
warn(`reflection output rejected (${why}); nothing written`);
|
|
19117
|
+
return;
|
|
19118
|
+
}
|
|
19119
|
+
const outcomeTail = stopNote ? ` (${stopNote})` : "";
|
|
19120
|
+
if (parsed.candidates.length === 0) {
|
|
19121
|
+
info(`reflection: nothing to write${outcomeTail}`);
|
|
19107
19122
|
return;
|
|
19108
19123
|
}
|
|
19109
|
-
if (parsed.candidates.length === 0) return;
|
|
19110
19124
|
const scopeOf = new Map(existing.map((r) => [r.id, r.scopeKey]));
|
|
19111
19125
|
const origin = deps.originChannelVisibility ?? "unknown";
|
|
19112
19126
|
const byScope = /* @__PURE__ */ new Map();
|
|
@@ -19135,6 +19149,10 @@ async function reflect(deps) {
|
|
|
19135
19149
|
if (dropped.length > 0)
|
|
19136
19150
|
warn(`write denied by policy, ${dropped.length} candidate(s) dropped: ${dropped.join(", ")}`);
|
|
19137
19151
|
for (const [scopeKey, records] of byScope) await deps.store.write(scopeKey, records);
|
|
19152
|
+
const written = [...byScope.values()].flat();
|
|
19153
|
+
const facts2 = written.filter((r) => r.kind === "fact").length;
|
|
19154
|
+
const summary = written.some((r) => r.kind === "summary") ? ", summary" : "";
|
|
19155
|
+
info(`reflection wrote ${facts2} fact(s)${summary}${outcomeTail}`);
|
|
19138
19156
|
} catch (err2) {
|
|
19139
19157
|
warn(`reflection failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
19140
19158
|
}
|
|
@@ -19346,6 +19364,7 @@ var init_buildStore2 = __esm({
|
|
|
19346
19364
|
function scheduleReflection(input) {
|
|
19347
19365
|
if (!input.cfg?.enabled || !shouldReflect(input.gate)) return;
|
|
19348
19366
|
const warn = (m) => console.warn(`[memory] ${input.threadKey} ${m}`);
|
|
19367
|
+
const info = (m) => console.log(`[memory] ${input.threadKey} ${m}`);
|
|
19349
19368
|
let provider;
|
|
19350
19369
|
let model;
|
|
19351
19370
|
try {
|
|
@@ -19369,7 +19388,8 @@ function scheduleReflection(input) {
|
|
|
19369
19388
|
answer: input.answer,
|
|
19370
19389
|
sourceThreadKey: input.threadKey,
|
|
19371
19390
|
sourceRunId: input.runId,
|
|
19372
|
-
onWarn: warn
|
|
19391
|
+
onWarn: warn,
|
|
19392
|
+
onInfo: info
|
|
19373
19393
|
})
|
|
19374
19394
|
);
|
|
19375
19395
|
}
|
|
@@ -19397,7 +19417,7 @@ async function memoryContextBlock(organization, cfg, injected, query, userId, sc
|
|
|
19397
19417
|
var init_memory = __esm({
|
|
19398
19418
|
"../../src/core/memory/index.ts"() {
|
|
19399
19419
|
"use strict";
|
|
19400
|
-
|
|
19420
|
+
init_provider();
|
|
19401
19421
|
init_scorer();
|
|
19402
19422
|
init_scope();
|
|
19403
19423
|
init_stores();
|
|
@@ -22641,6 +22661,7 @@ function durableInboxMessage(msg, text, at, from) {
|
|
|
22641
22661
|
...msg.userName !== void 0 ? { userName: msg.userName } : {},
|
|
22642
22662
|
...msg.sourceUrl !== void 0 ? { sourceUrl: msg.sourceUrl } : {},
|
|
22643
22663
|
...msg.channelName !== void 0 ? { channelName: msg.channelName } : {},
|
|
22664
|
+
...msg.messageId !== void 0 ? { messageId: msg.messageId } : {},
|
|
22644
22665
|
...from !== void 0 ? { fromRunId: from.runId } : {},
|
|
22645
22666
|
// Staged references are metadata (record 0033) — a few hundred bytes each —
|
|
22646
22667
|
// so they ride the base row and survive the restart whatever the
|
|
@@ -22681,6 +22702,7 @@ function messageFromInbox(stored, fallbackAt) {
|
|
|
22681
22702
|
const userName = str7("userName");
|
|
22682
22703
|
const sourceUrl = str7("sourceUrl");
|
|
22683
22704
|
const channelName = str7("channelName");
|
|
22705
|
+
const messageId = str7("messageId");
|
|
22684
22706
|
const fromRunId = str7("fromRunId");
|
|
22685
22707
|
const at = typeof m.at === "number" && Number.isFinite(m.at) ? m.at : fallbackAt;
|
|
22686
22708
|
const images = attachmentsFromInbox(m.images);
|
|
@@ -22697,6 +22719,7 @@ ${note}` : text,
|
|
|
22697
22719
|
...userName !== void 0 ? { userName } : {},
|
|
22698
22720
|
...sourceUrl !== void 0 ? { sourceUrl } : {},
|
|
22699
22721
|
...channelName !== void 0 ? { channelName } : {},
|
|
22722
|
+
...messageId !== void 0 ? { messageId } : {},
|
|
22700
22723
|
...images ? { images } : {},
|
|
22701
22724
|
...documents ? { documents } : {},
|
|
22702
22725
|
...staged ? { staged } : {}
|
|
@@ -22735,6 +22758,9 @@ var init_inboxMessage = __esm({
|
|
|
22735
22758
|
});
|
|
22736
22759
|
|
|
22737
22760
|
// ../../src/core/threadAdmission.ts
|
|
22761
|
+
function followUpMessageId(input) {
|
|
22762
|
+
return input.messageId ?? (input.ledgerSeq !== void 0 ? `inbox-${input.ledgerSeq}` : `at-${input.at}`);
|
|
22763
|
+
}
|
|
22738
22764
|
function decideFollowUp(live, requested) {
|
|
22739
22765
|
if (requested.agent !== void 0 && requested.agent !== live.agent)
|
|
22740
22766
|
return { kind: "refuse", reason: "agent_mismatch", requestedAgent: requested.agent };
|
|
@@ -23127,6 +23153,7 @@ function followUpOf(msg, text, at, opts) {
|
|
|
23127
23153
|
...msg.images !== void 0 ? { images: msg.images } : {},
|
|
23128
23154
|
...msg.documents !== void 0 ? { documents: msg.documents } : {},
|
|
23129
23155
|
...msg.staged !== void 0 ? { staged: msg.staged } : {},
|
|
23156
|
+
...msg.messageId !== void 0 ? { messageId: msg.messageId } : {},
|
|
23130
23157
|
at,
|
|
23131
23158
|
...opts.ledgerSeq !== void 0 ? { ledgerSeq: opts.ledgerSeq } : {},
|
|
23132
23159
|
...opts.from !== void 0 ? { from: opts.from } : {},
|
|
@@ -24798,6 +24825,16 @@ var init_runRegistry = __esm({
|
|
|
24798
24825
|
}
|
|
24799
24826
|
});
|
|
24800
24827
|
|
|
24828
|
+
// ../../src/core/types.ts
|
|
24829
|
+
function messageIdOf(msg, runId3) {
|
|
24830
|
+
return msg.messageId ?? runId3;
|
|
24831
|
+
}
|
|
24832
|
+
var init_types4 = __esm({
|
|
24833
|
+
"../../src/core/types.ts"() {
|
|
24834
|
+
"use strict";
|
|
24835
|
+
}
|
|
24836
|
+
});
|
|
24837
|
+
|
|
24801
24838
|
// ../../src/core/dispatch/fastPath.ts
|
|
24802
24839
|
function isInlineRunCommand(id) {
|
|
24803
24840
|
return id.startsWith("friction.") || id === "memory.forget" || /^repo\.(onboard|offboard|rebuild|reconfigure|test|build)$/.test(id) || /^mcp\.(add|connect|remove)$/.test(id);
|
|
@@ -24884,7 +24921,12 @@ async function runInlineCommandRun(deps, msg, command2, io, execute, ending, tra
|
|
|
24884
24921
|
);
|
|
24885
24922
|
trace.bindRun(run2.id, (e) => registry.publish(run2.id, e));
|
|
24886
24923
|
io.runStarted?.({ id: run2.id });
|
|
24887
|
-
registry.publish(run2.id, {
|
|
24924
|
+
registry.publish(run2.id, {
|
|
24925
|
+
type: "input",
|
|
24926
|
+
text: redactSecrets(msg.text),
|
|
24927
|
+
messageId: messageIdOf(msg, run2.id),
|
|
24928
|
+
at: clock3()
|
|
24929
|
+
});
|
|
24888
24930
|
registry.publish(run2.id, { type: "run_meta", agent: COMMAND_RUN_AGENT, traceId: root.traceId, at: clock3() });
|
|
24889
24931
|
let result;
|
|
24890
24932
|
try {
|
|
@@ -24969,6 +25011,7 @@ var init_fastPath = __esm({
|
|
|
24969
25011
|
init_commandChat();
|
|
24970
25012
|
init_commandSurface();
|
|
24971
25013
|
init_runRegistry();
|
|
25014
|
+
init_types4();
|
|
24972
25015
|
init_record();
|
|
24973
25016
|
init_reply();
|
|
24974
25017
|
}
|
|
@@ -25139,379 +25182,247 @@ var init_resolve = __esm({
|
|
|
25139
25182
|
init_registry();
|
|
25140
25183
|
init_profile();
|
|
25141
25184
|
init_directives();
|
|
25142
|
-
|
|
25185
|
+
init_provider();
|
|
25143
25186
|
init_factory();
|
|
25144
25187
|
init_githubRepoProbe();
|
|
25145
25188
|
init_repoContext();
|
|
25146
25189
|
}
|
|
25147
25190
|
});
|
|
25148
25191
|
|
|
25149
|
-
// ../../src/
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
function redactPrDescription(d) {
|
|
25155
|
-
return mapStringLeaves(d, redactSecrets);
|
|
25192
|
+
// ../../src/artifacts/contentType.ts
|
|
25193
|
+
function contentTypeFor(name) {
|
|
25194
|
+
const dot = name.lastIndexOf(".");
|
|
25195
|
+
if (dot < 0 || dot === name.length - 1) return OCTET_STREAM;
|
|
25196
|
+
return BY_EXTENSION[name.slice(dot + 1).toLowerCase()] ?? OCTET_STREAM;
|
|
25156
25197
|
}
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25198
|
+
var BY_EXTENSION, OCTET_STREAM, INLINE_IMAGE_TYPES;
|
|
25199
|
+
var init_contentType = __esm({
|
|
25200
|
+
"../../src/artifacts/contentType.ts"() {
|
|
25201
|
+
"use strict";
|
|
25202
|
+
BY_EXTENSION = {
|
|
25203
|
+
png: "image/png",
|
|
25204
|
+
jpg: "image/jpeg",
|
|
25205
|
+
jpeg: "image/jpeg",
|
|
25206
|
+
gif: "image/gif",
|
|
25207
|
+
webp: "image/webp",
|
|
25208
|
+
svg: "image/svg+xml",
|
|
25209
|
+
pdf: "application/pdf",
|
|
25210
|
+
mp4: "video/mp4",
|
|
25211
|
+
webm: "video/webm",
|
|
25212
|
+
mov: "video/quicktime",
|
|
25213
|
+
mp3: "audio/mpeg",
|
|
25214
|
+
wav: "audio/wav",
|
|
25215
|
+
txt: "text/plain",
|
|
25216
|
+
md: "text/markdown",
|
|
25217
|
+
csv: "text/csv",
|
|
25218
|
+
json: "application/json",
|
|
25219
|
+
html: "text/html",
|
|
25220
|
+
zip: "application/zip",
|
|
25221
|
+
gz: "application/gzip",
|
|
25222
|
+
tar: "application/x-tar"
|
|
25223
|
+
};
|
|
25224
|
+
OCTET_STREAM = "application/octet-stream";
|
|
25225
|
+
INLINE_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
25162
25226
|
}
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25227
|
+
});
|
|
25228
|
+
|
|
25229
|
+
// ../../src/artifacts/keys.ts
|
|
25230
|
+
function safeBasename(name) {
|
|
25231
|
+
const segments2 = name.split(/[\\/]+/).filter((s) => s.length > 0);
|
|
25232
|
+
const last = segments2[segments2.length - 1] ?? "";
|
|
25233
|
+
const reduced = last.replace(SAFE, "_").replace(/\.{2,}/g, "_").replace(/_+/g, "_");
|
|
25234
|
+
if (reduced === "" || /^[._-]+$/.test(reduced)) return "file";
|
|
25235
|
+
return reduced;
|
|
25167
25236
|
}
|
|
25168
|
-
function
|
|
25169
|
-
return
|
|
25237
|
+
function threadKeySafe(threadKey) {
|
|
25238
|
+
return threadKey.replace(/:/g, "-").replace(SAFE, "_");
|
|
25170
25239
|
}
|
|
25171
|
-
function
|
|
25172
|
-
return
|
|
25240
|
+
function outboundKey(runId3, seq, name) {
|
|
25241
|
+
return `runs/${runId3}/out/${seq}-${safeBasename(name)}`;
|
|
25173
25242
|
}
|
|
25174
|
-
function
|
|
25175
|
-
|
|
25176
|
-
throw new Error(`renderPrDescriptionMarkdown: repo must be owner/name, got "${ctx.repo}"`);
|
|
25177
|
-
if (!SHA_RE2.test(ctx.headSha))
|
|
25178
|
-
throw new Error(`renderPrDescriptionMarkdown: headSha must be a full 40-char lowercase sha, got "${ctx.headSha}"`);
|
|
25179
|
-
const out = [];
|
|
25180
|
-
out.push("## TL;DR", "", desc.tldr, "");
|
|
25181
|
-
out.push("## What & why", "", desc.whatWhy, "");
|
|
25182
|
-
out.push("## Tour", "");
|
|
25183
|
-
desc.tour.forEach((step, i) => {
|
|
25184
|
-
out.push(`### ${i + 1}. ${step.title}`, "", step.description, "");
|
|
25185
|
-
if (step.lookFor) out.push(`**Look for:** ${step.lookFor}`, "");
|
|
25186
|
-
out.push(anchorUrl(ctx, step.anchor), "");
|
|
25187
|
-
});
|
|
25188
|
-
out.push(`### ${desc.tour.length + 1}. Remaining changes`, "");
|
|
25189
|
-
if (desc.remaining.length === 0) out.push("- none \u2014 every touched file is covered by a step above", "");
|
|
25190
|
-
else {
|
|
25191
|
-
for (const r of desc.remaining) out.push(`- \`${r.path}\` \u2014 ${r.note}`);
|
|
25192
|
-
out.push("");
|
|
25193
|
-
}
|
|
25194
|
-
out.push("## Decisions", "");
|
|
25195
|
-
for (const d of desc.decisions) out.push(`- **${d.title.replace(/\.?$/, ".")}** ${d.rationale}`);
|
|
25196
|
-
out.push("");
|
|
25197
|
-
out.push("## Risks & implications", "", desc.risks, "");
|
|
25198
|
-
out.push("## Validation", "");
|
|
25199
|
-
if (desc.validation.summary) out.push(desc.validation.summary, "");
|
|
25200
|
-
out.push("| Criterion | Proof |", "|---|---|");
|
|
25201
|
-
for (const c of desc.validation.criteria) out.push(`| ${cell2(c.criterion)} | ${cell2(c.proof)} |`);
|
|
25202
|
-
out.push("", GENERATED_FOOTER, "");
|
|
25203
|
-
return out.join("\n");
|
|
25243
|
+
function inboundKey(threadKey, messageTs, index, name) {
|
|
25244
|
+
return `threads/${threadKeySafe(threadKey)}/in/${messageTs}/${index}-${safeBasename(name)}`;
|
|
25204
25245
|
}
|
|
25205
|
-
|
|
25206
|
-
|
|
25207
|
-
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
const description = { tour: [] };
|
|
25211
|
-
const tldr = sections.get("tldr");
|
|
25212
|
-
if (tldr) setProse(description, "tldr", tldr, problems);
|
|
25213
|
-
else {
|
|
25214
|
-
problems.push("no `## TL;DR` section \u2014 the tldr is the body's first paragraph");
|
|
25215
|
-
const first = firstParagraph(lines, fenced);
|
|
25216
|
-
if (first) description.tldr = first;
|
|
25217
|
-
}
|
|
25218
|
-
const whatWhy = sections.get("whatWhy");
|
|
25219
|
-
if (whatWhy) setProse(description, "whatWhy", whatWhy, problems);
|
|
25220
|
-
const tour = sections.get("tour");
|
|
25221
|
-
if (tour) {
|
|
25222
|
-
const parsed = parseTour(tour.lines, fenced.slice(tour.start, tour.start + tour.lines.length), problems);
|
|
25223
|
-
description.tour = parsed.tour;
|
|
25224
|
-
if (parsed.remaining) description.remaining = parsed.remaining;
|
|
25225
|
-
}
|
|
25226
|
-
const decisions = sections.get("decisions");
|
|
25227
|
-
if (decisions) description.decisions = parseDecisions(decisions.lines, problems);
|
|
25228
|
-
const risks = sections.get("risks");
|
|
25229
|
-
if (risks) setProse(description, "risks", risks, problems);
|
|
25230
|
-
const validation = sections.get("validation");
|
|
25231
|
-
if (validation) description.validation = parseValidation(validation.lines, problems);
|
|
25232
|
-
for (const [key, heading] of SECTIONS) {
|
|
25233
|
-
if (key === "tldr" || sections.has(key)) continue;
|
|
25234
|
-
problems.push(`no \`## ${heading}\` section`);
|
|
25246
|
+
var SAFE;
|
|
25247
|
+
var init_keys = __esm({
|
|
25248
|
+
"../../src/artifacts/keys.ts"() {
|
|
25249
|
+
"use strict";
|
|
25250
|
+
SAFE = /[^A-Za-z0-9._-]+/g;
|
|
25235
25251
|
}
|
|
25236
|
-
|
|
25252
|
+
});
|
|
25253
|
+
|
|
25254
|
+
// ../../src/tools/attach.ts
|
|
25255
|
+
function transferBudgetMs(size) {
|
|
25256
|
+
return Math.ceil(size / TRANSFER_FLOOR_BYTES_PER_MS) + TRANSFER_SETUP_MS;
|
|
25237
25257
|
}
|
|
25238
|
-
function
|
|
25239
|
-
|
|
25240
|
-
return
|
|
25241
|
-
const m = /^\s*(`{3,}|~{3,})/.exec(l);
|
|
25242
|
-
if (m && (open2 === void 0 || m[1][0] === open2[0])) {
|
|
25243
|
-
const wasOpen = open2 !== void 0;
|
|
25244
|
-
open2 = wasOpen ? void 0 : m[1];
|
|
25245
|
-
return true;
|
|
25246
|
-
}
|
|
25247
|
-
return open2 !== void 0;
|
|
25248
|
-
});
|
|
25258
|
+
function fileNameOf2(path) {
|
|
25259
|
+
const segments2 = path.split("/").filter(Boolean);
|
|
25260
|
+
return segments2[segments2.length - 1] ?? path;
|
|
25249
25261
|
}
|
|
25250
|
-
function
|
|
25251
|
-
|
|
25252
|
-
const out = /* @__PURE__ */ new Map();
|
|
25253
|
-
let current;
|
|
25254
|
-
lines.forEach((line3, i) => {
|
|
25255
|
-
const m = fenced[i] ? null : HEADING_RE.exec(line3);
|
|
25256
|
-
if (m && m[1] === "##") {
|
|
25257
|
-
const key = byHeading.get(m[2].replace(/\s+/g, " ").toLowerCase());
|
|
25258
|
-
if (key === void 0)
|
|
25259
|
-
current = void 0;
|
|
25260
|
-
else if (out.has(key)) {
|
|
25261
|
-
problems.push(`duplicate \`## ${SECTIONS.find(([k]) => k === key)[1]}\` section \u2014 the first one stands`);
|
|
25262
|
-
current = void 0;
|
|
25263
|
-
} else {
|
|
25264
|
-
current = { start: i + 1, lines: [] };
|
|
25265
|
-
out.set(key, current);
|
|
25266
|
-
}
|
|
25267
|
-
return;
|
|
25268
|
-
}
|
|
25269
|
-
current?.lines.push(line3);
|
|
25270
|
-
});
|
|
25271
|
-
return out;
|
|
25262
|
+
function putCommandFor(path, contentType, url) {
|
|
25263
|
+
return `curl -fsS -T ${shellQuote(path)} -H ${shellQuote(`Content-Type: ${contentType}`)} -o /dev/null -w ${shellQuote(PUT_REPORT_FORMAT)} ${shellQuote(url)}`;
|
|
25272
25264
|
}
|
|
25273
|
-
function
|
|
25274
|
-
const
|
|
25275
|
-
|
|
25276
|
-
const l = lines[i];
|
|
25277
|
-
if (l.trim() === "") {
|
|
25278
|
-
if (para.length) break;
|
|
25279
|
-
continue;
|
|
25280
|
-
}
|
|
25281
|
-
if (fenced[i] || para.length === 0 && HEADING_RE.test(l)) continue;
|
|
25282
|
-
para.push(l.trim());
|
|
25283
|
-
}
|
|
25284
|
-
return para.length ? para.join("\n") : void 0;
|
|
25265
|
+
function parsePutReport(output) {
|
|
25266
|
+
const m = /(\d{3}) (\d{1,15})\s*$/.exec(output);
|
|
25267
|
+
return m ? { httpCode: Number(m[1]), sentBytes: Number(m[2]) } : null;
|
|
25285
25268
|
}
|
|
25286
|
-
function
|
|
25287
|
-
|
|
25288
|
-
if (text === "") problems.push(`\`## ${SECTIONS.find(([k]) => k === key)[1]}\` section is empty`);
|
|
25289
|
-
else description[key] = text;
|
|
25269
|
+
function postCommandFor(path, url) {
|
|
25270
|
+
return `curl -fsS --upload-file ${shellQuote(path)} -X POST ${shellQuote(url)}`;
|
|
25290
25271
|
}
|
|
25291
|
-
function
|
|
25292
|
-
const
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
})
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
if (isRemaining(step.title)) {
|
|
25306
|
-
remaining = parseRemaining(step.lines, problems);
|
|
25307
|
-
continue;
|
|
25308
|
-
}
|
|
25309
|
-
const parsed = parseStep(step, problems);
|
|
25310
|
-
if (parsed) tour.push(parsed);
|
|
25272
|
+
async function attachThroughStore(path, name, lead, artifacts, ctx) {
|
|
25273
|
+
const exec = (command2, timeoutMs2) => {
|
|
25274
|
+
const opts = { timeoutMs: timeoutMs2, ...ctx.signal ? { signal: ctx.signal } : {} };
|
|
25275
|
+
return ctx.executor.exec(command2, opts);
|
|
25276
|
+
};
|
|
25277
|
+
const callId = ctx.callId;
|
|
25278
|
+
if (callId === void 0) return "error: attach_file was run without its call id \u2014 the file cannot be recorded";
|
|
25279
|
+
const statOut = await exec(statCommandFor(shellQuote(path)), STAT_TIMEOUT_MS);
|
|
25280
|
+
if (parseExitPrefix(statOut).failed) return `error: could not read ${path}: ${statOut.trim()}`;
|
|
25281
|
+
const size = parseByteSize(statOut);
|
|
25282
|
+
if (size === null) return `error: could not measure ${path}: ${statOut.trim()}`;
|
|
25283
|
+
if (size === 0) return `error: ${path} is empty \u2014 nothing to attach`;
|
|
25284
|
+
if (size > MAX_ARTIFACT_BYTES) {
|
|
25285
|
+
return `error: ${path} is ${size} bytes; attach_file takes files up to ${MAX_ARTIFACT_BYTES} bytes (1 GiB) \u2014 link to the file instead`;
|
|
25311
25286
|
}
|
|
25312
|
-
|
|
25313
|
-
if (
|
|
25314
|
-
|
|
25315
|
-
}
|
|
25316
|
-
|
|
25317
|
-
const where = `step ${step.label} (${step.title})`;
|
|
25318
|
-
let link;
|
|
25319
|
-
const descLines = [];
|
|
25320
|
-
const lookForLines = [];
|
|
25321
|
-
let inLookFor = false;
|
|
25322
|
-
for (const line3 of step.lines) {
|
|
25323
|
-
const m = PERMALINK_RE.exec(line3.trim());
|
|
25324
|
-
if (m) {
|
|
25325
|
-
if (link) problems.push(`${where}: more than one permalink \u2014 the first is the anchor`);
|
|
25326
|
-
else link = m;
|
|
25327
|
-
inLookFor = false;
|
|
25328
|
-
continue;
|
|
25329
|
-
}
|
|
25330
|
-
const lf = LOOK_FOR_RE.exec(line3.trim());
|
|
25331
|
-
if (lf) {
|
|
25332
|
-
lookForLines.push(lf[1]);
|
|
25333
|
-
inLookFor = true;
|
|
25334
|
-
continue;
|
|
25335
|
-
}
|
|
25336
|
-
if (inLookFor) {
|
|
25337
|
-
if (line3.trim() === "") inLookFor = false;
|
|
25338
|
-
else lookForLines.push(line3.trim());
|
|
25339
|
-
continue;
|
|
25340
|
-
}
|
|
25341
|
-
descLines.push(line3);
|
|
25287
|
+
let timeoutMs = BASH_TIMEOUT_MAX_MS;
|
|
25288
|
+
if (ctx.remainingMs) {
|
|
25289
|
+
const budget = bashBudgetWithinRun(BASH_TIMEOUT_MAX_MS, ctx.remainingMs());
|
|
25290
|
+
if (budget.kind === "exhausted") return `error: ${budget.note}`;
|
|
25291
|
+
if (budget.kind === "clipped") timeoutMs = budget.timeoutMs;
|
|
25342
25292
|
}
|
|
25343
|
-
|
|
25344
|
-
|
|
25345
|
-
return
|
|
25293
|
+
const needed = transferBudgetMs(size);
|
|
25294
|
+
if (needed > timeoutMs) {
|
|
25295
|
+
return `error: ${name} is ${size} bytes and needs about ${secs(needed)}s to move at ${TRANSFER_FLOOR_BYTES_PER_MS} KB/s; the run has ${secs(timeoutMs)}s of command budget left \u2014 write up what you have and link to the file instead`;
|
|
25346
25296
|
}
|
|
25347
|
-
|
|
25297
|
+
const contentType = contentTypeFor(name);
|
|
25298
|
+
const key = outboundKey(artifacts.runId, artifacts.nextSeq(), name);
|
|
25299
|
+
const putUrl = await artifacts.store.presignPut(key, contentType);
|
|
25300
|
+
const putOut = await exec(putCommandFor(path, contentType, putUrl), timeoutMs);
|
|
25301
|
+
if (parseExitPrefix(putOut).failed) {
|
|
25302
|
+
return `error: the upload of ${name} to the artifact store failed: ${putOut.trim()}`;
|
|
25303
|
+
}
|
|
25304
|
+
const report2 = parsePutReport(putOut);
|
|
25305
|
+
const curlSaid = report2 ? `; curl reported HTTP ${report2.httpCode} after sending ${report2.sentBytes} bytes` : "";
|
|
25306
|
+
const head = await artifacts.store.head(key);
|
|
25307
|
+
if (!head) {
|
|
25308
|
+
return `error: the artifact store holds nothing under ${key} after the upload of ${name}${curlSaid} \u2014 nothing was posted`;
|
|
25309
|
+
}
|
|
25310
|
+
if (head.size !== size) {
|
|
25311
|
+
return `error: the artifact store holds ${head.size} bytes for ${name}, not the ${size} measured${curlSaid} \u2014 nothing was posted`;
|
|
25312
|
+
}
|
|
25313
|
+
ctx.publish?.({ type: "artifact", direction: "out", key, name, size, contentType, callId });
|
|
25314
|
+
if (!ctx.uploadTicket) {
|
|
25315
|
+
const link = artifacts.artifactUrl?.(key);
|
|
25316
|
+
const where = link ? ` \u2014 ${link}` : ` is on the run page as ${key}`;
|
|
25317
|
+
await artifacts.reply(`${lead}
|
|
25318
|
+
\u{1F4CE} ${name} (${size} bytes)${where}`);
|
|
25319
|
+
return `attached ${name} (${size} bytes) to the run page as ${key}; this conversation's channel takes no file uploads, so the lead and the file's link were posted instead`;
|
|
25320
|
+
}
|
|
25321
|
+
const kept = "the file is kept on the run page";
|
|
25322
|
+
let ticket;
|
|
25348
25323
|
try {
|
|
25349
|
-
|
|
25350
|
-
} catch {
|
|
25351
|
-
|
|
25352
|
-
return void 0;
|
|
25324
|
+
ticket = await ctx.uploadTicket({ name, size });
|
|
25325
|
+
} catch (err2) {
|
|
25326
|
+
return `error: the channel refused an upload ticket for ${name}: ${describe2(err2)}; ${kept}`;
|
|
25353
25327
|
}
|
|
25354
|
-
const
|
|
25355
|
-
|
|
25356
|
-
|
|
25357
|
-
const issue = anchor.error.issues[0];
|
|
25358
|
-
problems.push(`${where}: ${["anchor", ...issue.path].join(".")} ${issue.message} \u2014 dropped`);
|
|
25359
|
-
return void 0;
|
|
25328
|
+
const postOut = await exec(postCommandFor(path, ticket.url), timeoutMs);
|
|
25329
|
+
if (parseExitPrefix(postOut).failed) {
|
|
25330
|
+
return `error: the upload of ${name} to the channel failed: ${postOut.trim()}; ${kept}`;
|
|
25360
25331
|
}
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25364
|
-
|
|
25365
|
-
// permalink): this is a read-back, not a submission the schema gates.
|
|
25366
|
-
description: descLines.join("\n").replace(/\n{3,}/g, "\n\n").trim(),
|
|
25367
|
-
...lookFor ? { lookFor } : {},
|
|
25368
|
-
anchor: { ...anchor.data, sha: link[1] }
|
|
25369
|
-
};
|
|
25370
|
-
}
|
|
25371
|
-
function parseRemaining(lines, problems) {
|
|
25372
|
-
const out = [];
|
|
25373
|
-
for (const raw of lines) {
|
|
25374
|
-
const line3 = raw.trim();
|
|
25375
|
-
if (line3 === "" || REMAINING_NONE_RE.test(line3)) continue;
|
|
25376
|
-
const m = REMAINING_LINE_RE.exec(line3);
|
|
25377
|
-
if (m) out.push({ path: m[1], note: m[2].trim() });
|
|
25378
|
-
else if (!line3.startsWith("-") && out.length) out[out.length - 1].note += `
|
|
25379
|
-
${line3}`;
|
|
25380
|
-
else problems.push(`Remaining changes: unrecognized line \`${line3}\``);
|
|
25332
|
+
try {
|
|
25333
|
+
await ticket.complete(lead);
|
|
25334
|
+
} catch (err2) {
|
|
25335
|
+
return `error: the channel refused to complete the upload of ${name}: ${describe2(err2)}; ${kept}`;
|
|
25381
25336
|
}
|
|
25382
|
-
return
|
|
25337
|
+
return `attached ${name} (${size} bytes) to the conversation and the run page`;
|
|
25383
25338
|
}
|
|
25384
|
-
|
|
25385
|
-
|
|
25386
|
-
|
|
25387
|
-
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
|
|
25393
|
-
|
|
25339
|
+
var MAX_ARTIFACT_BYTES, TRANSFER_FLOOR_BYTES_PER_MS, TRANSFER_SETUP_MS, STAT_TIMEOUT_MS, describe2, secs, PUT_REPORT_FORMAT, attachFileTool;
|
|
25340
|
+
var init_attach = __esm({
|
|
25341
|
+
"../../src/tools/attach.ts"() {
|
|
25342
|
+
"use strict";
|
|
25343
|
+
init_contentType();
|
|
25344
|
+
init_keys();
|
|
25345
|
+
init_runEvents();
|
|
25346
|
+
init_bashTimeout();
|
|
25347
|
+
init_binaryRead();
|
|
25348
|
+
init_shellQuote();
|
|
25349
|
+
MAX_ARTIFACT_BYTES = 1073741824;
|
|
25350
|
+
TRANSFER_FLOOR_BYTES_PER_MS = 1024;
|
|
25351
|
+
TRANSFER_SETUP_MS = 1e4;
|
|
25352
|
+
STAT_TIMEOUT_MS = 3e4;
|
|
25353
|
+
describe2 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
25354
|
+
secs = (ms) => Math.max(0, Math.round(ms / 1e3));
|
|
25355
|
+
PUT_REPORT_FORMAT = "\\n%{http_code} %{size_upload}";
|
|
25356
|
+
attachFileTool = {
|
|
25357
|
+
name: "attach_file",
|
|
25358
|
+
failsInText: true,
|
|
25359
|
+
description: `Post a file from the workspace into the conversation so the person sees it inline \u2014 a screenshot (e.g. from \`playwright screenshot\`), a rendered PDF, a recording, a log. Use it whenever you produce an image worth showing: a link to a file is not a picture. Whole files only: up to ${MAX_ARTIFACT_BYTES} bytes (1 GiB) where the artifact store is configured, ${MAX_READ_BYTES} bytes otherwise \u2014 the result says which.`,
|
|
25360
|
+
inputSchema: {
|
|
25361
|
+
type: "object",
|
|
25362
|
+
properties: {
|
|
25363
|
+
path: { type: "string", description: "Relative path of the file in the workspace" },
|
|
25364
|
+
comment: {
|
|
25365
|
+
type: "string",
|
|
25366
|
+
description: "One line posted with the file saying what it shows (default: the file name)"
|
|
25367
|
+
}
|
|
25368
|
+
},
|
|
25369
|
+
required: ["path"]
|
|
25370
|
+
},
|
|
25371
|
+
async run(input, ctx) {
|
|
25372
|
+
const path = String(input.path ?? "").trim();
|
|
25373
|
+
if (!path) return "error: path is required";
|
|
25374
|
+
const name = fileNameOf2(path);
|
|
25375
|
+
const lead = typeof input.comment === "string" && input.comment.trim() ? input.comment.trim() : name;
|
|
25376
|
+
if (ctx.artifacts) return attachThroughStore(path, name, lead, ctx.artifacts, ctx);
|
|
25377
|
+
if (!ctx.attach) {
|
|
25378
|
+
return "attach_file is not available here: this conversation's channel takes no file uploads \u2014 link to the file instead";
|
|
25379
|
+
}
|
|
25380
|
+
const readBytes = ctx.executor.readBytes?.bind(ctx.executor);
|
|
25381
|
+
if (!readBytes) {
|
|
25382
|
+
return "attach_file is not available here: this workspace cannot hand files over \u2014 link to the file instead";
|
|
25383
|
+
}
|
|
25384
|
+
let bytes;
|
|
25385
|
+
try {
|
|
25386
|
+
bytes = await readBytes(path);
|
|
25387
|
+
} catch (err2) {
|
|
25388
|
+
return `error: could not read ${path}: ${describe2(err2)}`;
|
|
25389
|
+
}
|
|
25390
|
+
if (bytes.byteLength === 0) return `error: ${path} is empty \u2014 nothing to attach`;
|
|
25391
|
+
try {
|
|
25392
|
+
await ctx.attach({ name, bytes, lead });
|
|
25393
|
+
} catch (err2) {
|
|
25394
|
+
return `error: the channel refused the upload of ${name}: ${describe2(err2)}`;
|
|
25395
|
+
}
|
|
25396
|
+
return `attached ${name} (${bytes.byteLength} bytes) to the conversation`;
|
|
25397
|
+
}
|
|
25398
|
+
};
|
|
25394
25399
|
}
|
|
25395
|
-
|
|
25400
|
+
});
|
|
25401
|
+
|
|
25402
|
+
// ../../src/core/diffDigest.ts
|
|
25403
|
+
function basename2(p) {
|
|
25404
|
+
const i = p.lastIndexOf("/");
|
|
25405
|
+
return i === -1 ? p : p.slice(i + 1);
|
|
25396
25406
|
}
|
|
25397
|
-
function
|
|
25398
|
-
const
|
|
25399
|
-
|
|
25400
|
-
|
|
25401
|
-
|
|
25402
|
-
|
|
25403
|
-
|
|
25404
|
-
|
|
25405
|
-
|
|
25407
|
+
function decodeGitQuoted(s) {
|
|
25408
|
+
const bytes = [];
|
|
25409
|
+
for (let i = 0; i < s.length; i++) {
|
|
25410
|
+
if (s[i] === "\\" && i + 1 < s.length) {
|
|
25411
|
+
const oct = s.slice(i + 1).match(/^[0-7]{1,3}/)?.[0];
|
|
25412
|
+
if (oct) {
|
|
25413
|
+
bytes.push(parseInt(oct, 8) & 255);
|
|
25414
|
+
i += oct.length;
|
|
25415
|
+
continue;
|
|
25416
|
+
}
|
|
25417
|
+
const simple = { t: 9, n: 10, r: 13, '"': 34, "\\": 92 };
|
|
25418
|
+
const nx = s[i + 1];
|
|
25419
|
+
bytes.push(nx in simple ? simple[nx] : nx.charCodeAt(0) & 255);
|
|
25420
|
+
i += 1;
|
|
25406
25421
|
continue;
|
|
25407
25422
|
}
|
|
25408
|
-
|
|
25409
|
-
const cells = splitCells(line3);
|
|
25410
|
-
if (cells.length >= 2) criteria.push({ criterion: cells[0], proof: cells[1] });
|
|
25411
|
-
else problems.push(`Validation: unrecognized table line \`${line3}\``);
|
|
25423
|
+
bytes.push(s.charCodeAt(i) & 255);
|
|
25412
25424
|
}
|
|
25413
|
-
|
|
25414
|
-
const text = summary.join("\n").trim();
|
|
25415
|
-
return { ...text ? { summary: text } : {}, criteria };
|
|
25416
|
-
}
|
|
25417
|
-
function splitCells(row) {
|
|
25418
|
-
const cells = [];
|
|
25419
|
-
let cur = "";
|
|
25420
|
-
const inner = row.replace(/^\|/, "").replace(/\|$/, "");
|
|
25421
|
-
for (let i = 0; i < inner.length; i++) {
|
|
25422
|
-
const ch = inner[i];
|
|
25423
|
-
if (ch === "\\" && i + 1 < inner.length) {
|
|
25424
|
-
cur += inner[++i];
|
|
25425
|
-
} else if (ch === "|") {
|
|
25426
|
-
cells.push(cur.trim());
|
|
25427
|
-
cur = "";
|
|
25428
|
-
} else cur += ch;
|
|
25429
|
-
}
|
|
25430
|
-
cells.push(cur.trim());
|
|
25431
|
-
return cells;
|
|
25432
|
-
}
|
|
25433
|
-
var line2, prose, TourAnchorSchema, TourStepSchema, PrDescriptionSchema, REPO_RE, SHA_RE2, GENERATED_FOOTER, SECTIONS, PERMALINK_RE, HEADING_RE, STEP_RE, LOOK_FOR_RE, REMAINING_LINE_RE, REMAINING_NONE_RE, DECISION_RE, TABLE_HEADER_RE, TABLE_RULE_RE;
|
|
25434
|
-
var init_prDescription = __esm({
|
|
25435
|
-
"../../src/core/prDescription.ts"() {
|
|
25436
|
-
"use strict";
|
|
25437
|
-
init_redact();
|
|
25438
|
-
line2 = z20.string().trim().min(1).refine((s) => !/[\r\n]/.test(s), "must be a single line (no embedded newlines)");
|
|
25439
|
-
prose = z20.string().trim().min(1);
|
|
25440
|
-
TourAnchorSchema = z20.object({
|
|
25441
|
-
// Repo-relative: no leading `/`, no `..` SEGMENT (a filename like `a..b.ts` is fine).
|
|
25442
|
-
path: z20.string().trim().min(1).refine((p) => !p.startsWith("/") && !p.split("/").includes(".."), "path must be repo-relative"),
|
|
25443
|
-
from: z20.number().int().positive(),
|
|
25444
|
-
to: z20.number().int().positive()
|
|
25445
|
-
}).refine((a) => a.to >= a.from, "to must be >= from");
|
|
25446
|
-
TourStepSchema = z20.object({
|
|
25447
|
-
title: line2,
|
|
25448
|
-
description: prose,
|
|
25449
|
-
lookFor: prose.optional(),
|
|
25450
|
-
anchor: TourAnchorSchema
|
|
25451
|
-
});
|
|
25452
|
-
PrDescriptionSchema = z20.object({
|
|
25453
|
-
/** The PR title's single source. Metadata for the PR's own title field —
|
|
25454
|
-
* never rendered into the body (GitHub shows the title itself). */
|
|
25455
|
-
title: line2,
|
|
25456
|
-
tldr: prose,
|
|
25457
|
-
whatWhy: prose,
|
|
25458
|
-
tour: z20.array(TourStepSchema).min(1),
|
|
25459
|
-
/** Every touched file the Tour steps did not cover, one line each. */
|
|
25460
|
-
remaining: z20.array(z20.object({ path: line2, note: prose })),
|
|
25461
|
-
decisions: z20.array(z20.object({ title: line2, rationale: prose })).min(1),
|
|
25462
|
-
risks: prose,
|
|
25463
|
-
validation: z20.object({
|
|
25464
|
-
summary: prose.optional(),
|
|
25465
|
-
criteria: z20.array(z20.object({ criterion: prose, proof: prose })).min(1)
|
|
25466
|
-
})
|
|
25467
|
-
});
|
|
25468
|
-
REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
25469
|
-
SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
25470
|
-
GENERATED_FOOTER = "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)";
|
|
25471
|
-
SECTIONS = [
|
|
25472
|
-
["tldr", "TL;DR"],
|
|
25473
|
-
["whatWhy", "What & why"],
|
|
25474
|
-
["tour", "Tour"],
|
|
25475
|
-
["decisions", "Decisions"],
|
|
25476
|
-
["risks", "Risks & implications"],
|
|
25477
|
-
["validation", "Validation"]
|
|
25478
|
-
];
|
|
25479
|
-
PERMALINK_RE = /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/blob\/([0-9a-f]{7,40})\/([^#\s]+)#L(\d+)(?:-L(\d+))?$/;
|
|
25480
|
-
HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
|
|
25481
|
-
STEP_RE = /^###\s+(?:(\d+)\.\s+)?(.+?)\s*$/;
|
|
25482
|
-
LOOK_FOR_RE = /^\*\*Look for:\*\*\s*(.*)$/;
|
|
25483
|
-
REMAINING_LINE_RE = /^-\s+`([^`]+)`\s+—\s*(.*)$/;
|
|
25484
|
-
REMAINING_NONE_RE = /^-\s+none\b/i;
|
|
25485
|
-
DECISION_RE = /^-\s+\*\*(.+?)\*\*\s*(.*)$/;
|
|
25486
|
-
TABLE_HEADER_RE = /^\|\s*criterion\s*\|\s*proof\s*\|$/i;
|
|
25487
|
-
TABLE_RULE_RE = /^\|?\s*:?-{3,}:?\s*\|/;
|
|
25488
|
-
}
|
|
25489
|
-
});
|
|
25490
|
-
|
|
25491
|
-
// ../../src/core/diffDigest.ts
|
|
25492
|
-
function basename2(p) {
|
|
25493
|
-
const i = p.lastIndexOf("/");
|
|
25494
|
-
return i === -1 ? p : p.slice(i + 1);
|
|
25495
|
-
}
|
|
25496
|
-
function decodeGitQuoted(s) {
|
|
25497
|
-
const bytes = [];
|
|
25498
|
-
for (let i = 0; i < s.length; i++) {
|
|
25499
|
-
if (s[i] === "\\" && i + 1 < s.length) {
|
|
25500
|
-
const oct = s.slice(i + 1).match(/^[0-7]{1,3}/)?.[0];
|
|
25501
|
-
if (oct) {
|
|
25502
|
-
bytes.push(parseInt(oct, 8) & 255);
|
|
25503
|
-
i += oct.length;
|
|
25504
|
-
continue;
|
|
25505
|
-
}
|
|
25506
|
-
const simple = { t: 9, n: 10, r: 13, '"': 34, "\\": 92 };
|
|
25507
|
-
const nx = s[i + 1];
|
|
25508
|
-
bytes.push(nx in simple ? simple[nx] : nx.charCodeAt(0) & 255);
|
|
25509
|
-
i += 1;
|
|
25510
|
-
continue;
|
|
25511
|
-
}
|
|
25512
|
-
bytes.push(s.charCodeAt(i) & 255);
|
|
25513
|
-
}
|
|
25514
|
-
return Buffer.from(bytes).toString("utf8");
|
|
25425
|
+
return Buffer.from(bytes).toString("utf8");
|
|
25515
25426
|
}
|
|
25516
25427
|
function unquotePath(raw) {
|
|
25517
25428
|
const s = raw.trim();
|
|
@@ -25646,10 +25557,71 @@ var init_diffDigest = __esm({
|
|
|
25646
25557
|
}
|
|
25647
25558
|
});
|
|
25648
25559
|
|
|
25560
|
+
// ../../src/tools/diffDigest.ts
|
|
25561
|
+
var NAME_STATUS_MARK, NAME_STATUS_MARK_LINE, OUTPUT_CUT_RE, diffDigestTool;
|
|
25562
|
+
var init_diffDigest2 = __esm({
|
|
25563
|
+
"../../src/tools/diffDigest.ts"() {
|
|
25564
|
+
"use strict";
|
|
25565
|
+
init_diffDigest();
|
|
25566
|
+
init_shellQuote();
|
|
25567
|
+
NAME_STATUS_MARK = "@@diff_digest:name-status@@";
|
|
25568
|
+
NAME_STATUS_MARK_LINE = /^@@diff_digest:name-status@@$/m;
|
|
25569
|
+
OUTPUT_CUT_RE = /^\.\.\.\[truncated \d+ chars\]$/m;
|
|
25570
|
+
diffDigestTool = {
|
|
25571
|
+
sideEffectFree: true,
|
|
25572
|
+
name: "diff_digest",
|
|
25573
|
+
description: "Summarize the current branch's whole change against a base ref as a compact digest: per-file +adds/-dels, totals, and risky-file flags (migrations/schema, auth/permission, whole-file deletions, lockfiles, very large files). This is a DISTILLED summary, not the raw diff \u2014 use it to shape a PR description, or to orient before a review. Runs `git diff --numstat` and `--name-status` over the merge-base range `<base>...HEAD` in the workspace, so it covers every file however large the change; base defaults to the repo's default branch (origin/HEAD). The totals it states are the whole change \u2014 a diff you read that shows fewer files or lines was cut short.",
|
|
25574
|
+
inputSchema: {
|
|
25575
|
+
type: "object",
|
|
25576
|
+
properties: {
|
|
25577
|
+
base: {
|
|
25578
|
+
type: "string",
|
|
25579
|
+
description: "Base ref to diff against (e.g. 'main' or a SHA). Defaults to the repo's default branch via origin/HEAD."
|
|
25580
|
+
}
|
|
25581
|
+
}
|
|
25582
|
+
},
|
|
25583
|
+
async run(input, ctx) {
|
|
25584
|
+
const base = String(input.base ?? "").trim();
|
|
25585
|
+
const baseName = base || "origin/HEAD";
|
|
25586
|
+
if (base.startsWith("-")) {
|
|
25587
|
+
return "diff_digest: base ref may not start with '-' (rejected to prevent git option injection).";
|
|
25588
|
+
}
|
|
25589
|
+
const baseExpr = base ? shellQuote(base) : '"$(git rev-parse --abbrev-ref origin/HEAD 2>/dev/null || echo origin/main)"';
|
|
25590
|
+
const range = `--end-of-options ${baseExpr}...HEAD`;
|
|
25591
|
+
const listing = `git diff --numstat ${range} && echo ${NAME_STATUS_MARK} && git diff --name-status ${range}`;
|
|
25592
|
+
let raw = await ctx.executor.exec(listing);
|
|
25593
|
+
const incomplete = () => {
|
|
25594
|
+
const reason = "the file listing exceeded the executor's output cap";
|
|
25595
|
+
ctx.onDigest?.({ complete: false, base: baseName, reason });
|
|
25596
|
+
return `diff_digest: ${reason} \u2014 the digest cannot state totals for this change (base \`${baseName}\`). Read the diff file by file (\`git diff --name-only ${baseName}...HEAD\`).`;
|
|
25597
|
+
};
|
|
25598
|
+
if (OUTPUT_CUT_RE.test(raw)) return incomplete();
|
|
25599
|
+
let marker = NAME_STATUS_MARK_LINE.exec(raw);
|
|
25600
|
+
if (!marker) {
|
|
25601
|
+
const shallow = (await ctx.executor.exec("git rev-parse --is-shallow-repository")).trim() === "true";
|
|
25602
|
+
if (shallow) {
|
|
25603
|
+
await ctx.executor.exec('git fetch --unshallow --quiet origin "+refs/heads/*:refs/remotes/origin/*"');
|
|
25604
|
+
raw = await ctx.executor.exec(listing);
|
|
25605
|
+
if (OUTPUT_CUT_RE.test(raw)) return incomplete();
|
|
25606
|
+
marker = NAME_STATUS_MARK_LINE.exec(raw);
|
|
25607
|
+
}
|
|
25608
|
+
}
|
|
25609
|
+
if (!marker) {
|
|
25610
|
+
return `diff_digest: could not compute the diff (base \`${baseName}\`).
|
|
25611
|
+
${raw.trim()}`;
|
|
25612
|
+
}
|
|
25613
|
+
const digest = distillDiffStats(raw.slice(0, marker.index), raw.slice(marker.index + marker[0].length));
|
|
25614
|
+
ctx.onDigest?.({ complete: true, base: baseName, totals: digest.totals });
|
|
25615
|
+
return digest.text;
|
|
25616
|
+
}
|
|
25617
|
+
};
|
|
25618
|
+
}
|
|
25619
|
+
});
|
|
25620
|
+
|
|
25649
25621
|
// ../../src/tools/github.ts
|
|
25650
25622
|
function repoOf(input) {
|
|
25651
25623
|
const raw = String(input.repo ?? "").trim().replace(/\.git$/i, "");
|
|
25652
|
-
if (!
|
|
25624
|
+
if (!REPO_RE.test(raw))
|
|
25653
25625
|
return {
|
|
25654
25626
|
error: `repo must be an owner/name slug (got ${JSON.stringify(raw)}); github_repos lists the repos you can reach.`
|
|
25655
25627
|
};
|
|
@@ -25697,12 +25669,12 @@ function writeGate(tool, ctx, repo) {
|
|
|
25697
25669
|
return `${tool}: you are not allowed to write to ${repo} (it is restricted and you hold no grant for it) \u2014 say so to the user instead of retrying.`;
|
|
25698
25670
|
return void 0;
|
|
25699
25671
|
}
|
|
25700
|
-
var
|
|
25672
|
+
var REPO_RE, UNAVAILABLE, MAX_TREE_ENTRIES, MAX_ISSUE_BODY_SHOWN, githubReposTool, githubFileTool, githubTreeTool, githubSearchCodeTool, githubIssueListTool, githubIssueGetTool, githubIssueCreateTool, githubIssueUpdateTool, githubIssueCommentTool, githubIssueDeleteTool, GITHUB_READ_TOOLS, GITHUB_ISSUE_WRITE_TOOLS;
|
|
25701
25673
|
var init_github = __esm({
|
|
25702
25674
|
"../../src/tools/github.ts"() {
|
|
25703
25675
|
"use strict";
|
|
25704
25676
|
init_githubApi();
|
|
25705
|
-
|
|
25677
|
+
REPO_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9](?:[A-Za-z0-9._-]{0,98}[A-Za-z0-9])?$/;
|
|
25706
25678
|
UNAVAILABLE = "GitHub tools are not available in this context.";
|
|
25707
25679
|
MAX_TREE_ENTRIES = 300;
|
|
25708
25680
|
MAX_ISSUE_BODY_SHOWN = 6e3;
|
|
@@ -26059,321 +26031,52 @@ ${url}`;
|
|
|
26059
26031
|
}
|
|
26060
26032
|
});
|
|
26061
26033
|
|
|
26062
|
-
// ../../src/
|
|
26063
|
-
|
|
26064
|
-
|
|
26065
|
-
|
|
26066
|
-
|
|
26067
|
-
|
|
26068
|
-
|
|
26069
|
-
|
|
26070
|
-
|
|
26071
|
-
|
|
26072
|
-
|
|
26073
|
-
|
|
26074
|
-
|
|
26075
|
-
|
|
26076
|
-
|
|
26077
|
-
|
|
26078
|
-
|
|
26079
|
-
|
|
26080
|
-
|
|
26034
|
+
// ../../src/core/dispatch/awaitChildren.ts
|
|
26035
|
+
function decideWait(inputs) {
|
|
26036
|
+
const { children, now, budgetEndsAt, timeoutAt, stop, followUpPending } = inputs;
|
|
26037
|
+
if ([...children.values()].every(isTerminal)) return { kind: "end", why: "all_ended" };
|
|
26038
|
+
if (stop !== void 0) return { kind: "end", why: "stop" };
|
|
26039
|
+
if (followUpPending) return { kind: "end", why: "follow_up" };
|
|
26040
|
+
if (now >= budgetEndsAt) return { kind: "end", why: "budget" };
|
|
26041
|
+
if (timeoutAt !== void 0 && now >= timeoutAt) return { kind: "end", why: "timeout" };
|
|
26042
|
+
return { kind: "wait", until: Math.min(budgetEndsAt, timeoutAt ?? Number.POSITIVE_INFINITY) };
|
|
26043
|
+
}
|
|
26044
|
+
function budgetEndOf(now, remainingMs) {
|
|
26045
|
+
return now + remainingMs - RUN_DEADLINE_RESERVE_MS;
|
|
26046
|
+
}
|
|
26047
|
+
function sleepUnlessAborted(ms, signal2) {
|
|
26048
|
+
return new Promise((resolve12) => {
|
|
26049
|
+
if (signal2?.aborted) return resolve12();
|
|
26050
|
+
const done = () => {
|
|
26051
|
+
clearTimeout(timer);
|
|
26052
|
+
signal2?.removeEventListener("abort", done);
|
|
26053
|
+
resolve12();
|
|
26081
26054
|
};
|
|
26082
|
-
|
|
26083
|
-
|
|
26084
|
-
|
|
26085
|
-
|
|
26086
|
-
|
|
26087
|
-
|
|
26088
|
-
|
|
26089
|
-
|
|
26090
|
-
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
|
|
26095
|
-
|
|
26096
|
-
if (!name) return "use_skill: provide a skill `name` (see list_skills).";
|
|
26097
|
-
const skill = ctx.skills.get(name);
|
|
26098
|
-
const inScope = skill && skill.agents.includes(ctx.agentName ?? "");
|
|
26099
|
-
if (!skill || !inScope) {
|
|
26100
|
-
const available = ctx.skills.list(ctx.agentName ?? "").map((m) => m.name);
|
|
26101
|
-
return `use_skill: no skill named "${name}" is available to you.` + (available.length > 0 ? ` Available: ${available.join(", ")}.` : "");
|
|
26055
|
+
const timer = setTimeout(done, ms);
|
|
26056
|
+
signal2?.addEventListener("abort", done, { once: true });
|
|
26057
|
+
});
|
|
26058
|
+
}
|
|
26059
|
+
function waitCapabilityFor(deps) {
|
|
26060
|
+
return {
|
|
26061
|
+
stopRequested: () => deps.control.requested,
|
|
26062
|
+
followUpsPending: () => deps.inbox.size,
|
|
26063
|
+
watch: (ids, onChange) => {
|
|
26064
|
+
let replaying = true;
|
|
26065
|
+
const unsubscribe = deps.registry.subscribeIndex((event) => {
|
|
26066
|
+
if (event.type === "removed") {
|
|
26067
|
+
if (ids.has(event.id)) onChange();
|
|
26068
|
+
return;
|
|
26102
26069
|
}
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
26111
|
-
|
|
26112
|
-
|
|
26113
|
-
|
|
26114
|
-
_(Skill source: ${skill.source})_` : "";
|
|
26115
|
-
return `# Skill: ${skill.name}
|
|
26116
|
-
|
|
26117
|
-
${skill.body}${footer}`;
|
|
26118
|
-
}
|
|
26119
|
-
};
|
|
26120
|
-
}
|
|
26121
|
-
});
|
|
26122
|
-
|
|
26123
|
-
// ../../src/artifacts/contentType.ts
|
|
26124
|
-
function contentTypeFor(name) {
|
|
26125
|
-
const dot = name.lastIndexOf(".");
|
|
26126
|
-
if (dot < 0 || dot === name.length - 1) return OCTET_STREAM;
|
|
26127
|
-
return BY_EXTENSION[name.slice(dot + 1).toLowerCase()] ?? OCTET_STREAM;
|
|
26128
|
-
}
|
|
26129
|
-
var BY_EXTENSION, OCTET_STREAM, INLINE_IMAGE_TYPES;
|
|
26130
|
-
var init_contentType = __esm({
|
|
26131
|
-
"../../src/artifacts/contentType.ts"() {
|
|
26132
|
-
"use strict";
|
|
26133
|
-
BY_EXTENSION = {
|
|
26134
|
-
png: "image/png",
|
|
26135
|
-
jpg: "image/jpeg",
|
|
26136
|
-
jpeg: "image/jpeg",
|
|
26137
|
-
gif: "image/gif",
|
|
26138
|
-
webp: "image/webp",
|
|
26139
|
-
svg: "image/svg+xml",
|
|
26140
|
-
pdf: "application/pdf",
|
|
26141
|
-
mp4: "video/mp4",
|
|
26142
|
-
webm: "video/webm",
|
|
26143
|
-
mov: "video/quicktime",
|
|
26144
|
-
mp3: "audio/mpeg",
|
|
26145
|
-
wav: "audio/wav",
|
|
26146
|
-
txt: "text/plain",
|
|
26147
|
-
md: "text/markdown",
|
|
26148
|
-
csv: "text/csv",
|
|
26149
|
-
json: "application/json",
|
|
26150
|
-
html: "text/html",
|
|
26151
|
-
zip: "application/zip",
|
|
26152
|
-
gz: "application/gzip",
|
|
26153
|
-
tar: "application/x-tar"
|
|
26154
|
-
};
|
|
26155
|
-
OCTET_STREAM = "application/octet-stream";
|
|
26156
|
-
INLINE_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
26157
|
-
}
|
|
26158
|
-
});
|
|
26159
|
-
|
|
26160
|
-
// ../../src/artifacts/keys.ts
|
|
26161
|
-
function safeBasename(name) {
|
|
26162
|
-
const segments2 = name.split(/[\\/]+/).filter((s) => s.length > 0);
|
|
26163
|
-
const last = segments2[segments2.length - 1] ?? "";
|
|
26164
|
-
const reduced = last.replace(SAFE, "_").replace(/\.{2,}/g, "_").replace(/_+/g, "_");
|
|
26165
|
-
if (reduced === "" || /^[._-]+$/.test(reduced)) return "file";
|
|
26166
|
-
return reduced;
|
|
26167
|
-
}
|
|
26168
|
-
function threadKeySafe(threadKey) {
|
|
26169
|
-
return threadKey.replace(/:/g, "-").replace(SAFE, "_");
|
|
26170
|
-
}
|
|
26171
|
-
function outboundKey(runId3, seq, name) {
|
|
26172
|
-
return `runs/${runId3}/out/${seq}-${safeBasename(name)}`;
|
|
26173
|
-
}
|
|
26174
|
-
function inboundKey(threadKey, messageTs, index, name) {
|
|
26175
|
-
return `threads/${threadKeySafe(threadKey)}/in/${messageTs}/${index}-${safeBasename(name)}`;
|
|
26176
|
-
}
|
|
26177
|
-
var SAFE;
|
|
26178
|
-
var init_keys = __esm({
|
|
26179
|
-
"../../src/artifacts/keys.ts"() {
|
|
26180
|
-
"use strict";
|
|
26181
|
-
SAFE = /[^A-Za-z0-9._-]+/g;
|
|
26182
|
-
}
|
|
26183
|
-
});
|
|
26184
|
-
|
|
26185
|
-
// ../../src/tools/attach.ts
|
|
26186
|
-
function transferBudgetMs(size) {
|
|
26187
|
-
return Math.ceil(size / TRANSFER_FLOOR_BYTES_PER_MS) + TRANSFER_SETUP_MS;
|
|
26188
|
-
}
|
|
26189
|
-
function fileNameOf2(path) {
|
|
26190
|
-
const segments2 = path.split("/").filter(Boolean);
|
|
26191
|
-
return segments2[segments2.length - 1] ?? path;
|
|
26192
|
-
}
|
|
26193
|
-
function putCommandFor(path, contentType, url) {
|
|
26194
|
-
return `curl -fsS -T ${shellQuote(path)} -H ${shellQuote(`Content-Type: ${contentType}`)} -o /dev/null -w ${shellQuote(PUT_REPORT_FORMAT)} ${shellQuote(url)}`;
|
|
26195
|
-
}
|
|
26196
|
-
function parsePutReport(output) {
|
|
26197
|
-
const m = /(\d{3}) (\d{1,15})\s*$/.exec(output);
|
|
26198
|
-
return m ? { httpCode: Number(m[1]), sentBytes: Number(m[2]) } : null;
|
|
26199
|
-
}
|
|
26200
|
-
function postCommandFor(path, url) {
|
|
26201
|
-
return `curl -fsS --upload-file ${shellQuote(path)} -X POST ${shellQuote(url)}`;
|
|
26202
|
-
}
|
|
26203
|
-
async function attachThroughStore(path, name, lead, artifacts, ctx) {
|
|
26204
|
-
const exec = (command2, timeoutMs2) => {
|
|
26205
|
-
const opts = { timeoutMs: timeoutMs2, ...ctx.signal ? { signal: ctx.signal } : {} };
|
|
26206
|
-
return ctx.executor.exec(command2, opts);
|
|
26207
|
-
};
|
|
26208
|
-
const statOut = await exec(statCommandFor(shellQuote(path)), STAT_TIMEOUT_MS);
|
|
26209
|
-
if (parseExitPrefix(statOut).failed) return `error: could not read ${path}: ${statOut.trim()}`;
|
|
26210
|
-
const size = parseByteSize(statOut);
|
|
26211
|
-
if (size === null) return `error: could not measure ${path}: ${statOut.trim()}`;
|
|
26212
|
-
if (size === 0) return `error: ${path} is empty \u2014 nothing to attach`;
|
|
26213
|
-
if (size > MAX_ARTIFACT_BYTES) {
|
|
26214
|
-
return `error: ${path} is ${size} bytes; attach_file takes files up to ${MAX_ARTIFACT_BYTES} bytes (1 GiB) \u2014 link to the file instead`;
|
|
26215
|
-
}
|
|
26216
|
-
let timeoutMs = BASH_TIMEOUT_MAX_MS;
|
|
26217
|
-
if (ctx.remainingMs) {
|
|
26218
|
-
const budget = bashBudgetWithinRun(BASH_TIMEOUT_MAX_MS, ctx.remainingMs());
|
|
26219
|
-
if (budget.kind === "exhausted") return `error: ${budget.note}`;
|
|
26220
|
-
if (budget.kind === "clipped") timeoutMs = budget.timeoutMs;
|
|
26221
|
-
}
|
|
26222
|
-
const needed = transferBudgetMs(size);
|
|
26223
|
-
if (needed > timeoutMs) {
|
|
26224
|
-
return `error: ${name} is ${size} bytes and needs about ${secs(needed)}s to move at ${TRANSFER_FLOOR_BYTES_PER_MS} KB/s; the run has ${secs(timeoutMs)}s of command budget left \u2014 write up what you have and link to the file instead`;
|
|
26225
|
-
}
|
|
26226
|
-
const contentType = contentTypeFor(name);
|
|
26227
|
-
const key = outboundKey(artifacts.runId, artifacts.nextSeq(), name);
|
|
26228
|
-
const putUrl = await artifacts.store.presignPut(key, contentType);
|
|
26229
|
-
const putOut = await exec(putCommandFor(path, contentType, putUrl), timeoutMs);
|
|
26230
|
-
if (parseExitPrefix(putOut).failed) {
|
|
26231
|
-
return `error: the upload of ${name} to the artifact store failed: ${putOut.trim()}`;
|
|
26232
|
-
}
|
|
26233
|
-
const report2 = parsePutReport(putOut);
|
|
26234
|
-
const curlSaid = report2 ? `; curl reported HTTP ${report2.httpCode} after sending ${report2.sentBytes} bytes` : "";
|
|
26235
|
-
const head = await artifacts.store.head(key);
|
|
26236
|
-
if (!head) {
|
|
26237
|
-
return `error: the artifact store holds nothing under ${key} after the upload of ${name}${curlSaid} \u2014 nothing was posted`;
|
|
26238
|
-
}
|
|
26239
|
-
if (head.size !== size) {
|
|
26240
|
-
return `error: the artifact store holds ${head.size} bytes for ${name}, not the ${size} measured${curlSaid} \u2014 nothing was posted`;
|
|
26241
|
-
}
|
|
26242
|
-
ctx.publish?.({ type: "artifact", direction: "out", key, name, size, contentType });
|
|
26243
|
-
if (!ctx.uploadTicket) {
|
|
26244
|
-
const link = artifacts.artifactUrl?.(key);
|
|
26245
|
-
const where = link ? ` \u2014 ${link}` : ` is on the run page as ${key}`;
|
|
26246
|
-
await artifacts.reply(`${lead}
|
|
26247
|
-
\u{1F4CE} ${name} (${size} bytes)${where}`);
|
|
26248
|
-
return `attached ${name} (${size} bytes) to the run page as ${key}; this conversation's channel takes no file uploads, so the lead and the file's link were posted instead`;
|
|
26249
|
-
}
|
|
26250
|
-
const kept = "the file is kept on the run page";
|
|
26251
|
-
let ticket;
|
|
26252
|
-
try {
|
|
26253
|
-
ticket = await ctx.uploadTicket({ name, size });
|
|
26254
|
-
} catch (err2) {
|
|
26255
|
-
return `error: the channel refused an upload ticket for ${name}: ${describe2(err2)}; ${kept}`;
|
|
26256
|
-
}
|
|
26257
|
-
const postOut = await exec(postCommandFor(path, ticket.url), timeoutMs);
|
|
26258
|
-
if (parseExitPrefix(postOut).failed) {
|
|
26259
|
-
return `error: the upload of ${name} to the channel failed: ${postOut.trim()}; ${kept}`;
|
|
26260
|
-
}
|
|
26261
|
-
try {
|
|
26262
|
-
await ticket.complete(lead);
|
|
26263
|
-
} catch (err2) {
|
|
26264
|
-
return `error: the channel refused to complete the upload of ${name}: ${describe2(err2)}; ${kept}`;
|
|
26265
|
-
}
|
|
26266
|
-
return `attached ${name} (${size} bytes) to the conversation and the run page`;
|
|
26267
|
-
}
|
|
26268
|
-
var MAX_ARTIFACT_BYTES, TRANSFER_FLOOR_BYTES_PER_MS, TRANSFER_SETUP_MS, STAT_TIMEOUT_MS, describe2, secs, PUT_REPORT_FORMAT, attachFileTool;
|
|
26269
|
-
var init_attach = __esm({
|
|
26270
|
-
"../../src/tools/attach.ts"() {
|
|
26271
|
-
"use strict";
|
|
26272
|
-
init_contentType();
|
|
26273
|
-
init_keys();
|
|
26274
|
-
init_runEvents();
|
|
26275
|
-
init_bashTimeout();
|
|
26276
|
-
init_binaryRead();
|
|
26277
|
-
init_shellQuote();
|
|
26278
|
-
MAX_ARTIFACT_BYTES = 1073741824;
|
|
26279
|
-
TRANSFER_FLOOR_BYTES_PER_MS = 1024;
|
|
26280
|
-
TRANSFER_SETUP_MS = 1e4;
|
|
26281
|
-
STAT_TIMEOUT_MS = 3e4;
|
|
26282
|
-
describe2 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
26283
|
-
secs = (ms) => Math.max(0, Math.round(ms / 1e3));
|
|
26284
|
-
PUT_REPORT_FORMAT = "\\n%{http_code} %{size_upload}";
|
|
26285
|
-
attachFileTool = {
|
|
26286
|
-
name: "attach_file",
|
|
26287
|
-
failsInText: true,
|
|
26288
|
-
description: `Post a file from the workspace into the conversation so the person sees it inline \u2014 a screenshot (e.g. from \`playwright screenshot\`), a rendered PDF, a recording, a log. Use it whenever you produce an image worth showing: a link to a file is not a picture. Whole files only: up to ${MAX_ARTIFACT_BYTES} bytes (1 GiB) where the artifact store is configured, ${MAX_READ_BYTES} bytes otherwise \u2014 the result says which.`,
|
|
26289
|
-
inputSchema: {
|
|
26290
|
-
type: "object",
|
|
26291
|
-
properties: {
|
|
26292
|
-
path: { type: "string", description: "Relative path of the file in the workspace" },
|
|
26293
|
-
comment: {
|
|
26294
|
-
type: "string",
|
|
26295
|
-
description: "One line posted with the file saying what it shows (default: the file name)"
|
|
26296
|
-
}
|
|
26297
|
-
},
|
|
26298
|
-
required: ["path"]
|
|
26299
|
-
},
|
|
26300
|
-
async run(input, ctx) {
|
|
26301
|
-
const path = String(input.path ?? "").trim();
|
|
26302
|
-
if (!path) return "error: path is required";
|
|
26303
|
-
const name = fileNameOf2(path);
|
|
26304
|
-
const lead = typeof input.comment === "string" && input.comment.trim() ? input.comment.trim() : name;
|
|
26305
|
-
if (ctx.artifacts) return attachThroughStore(path, name, lead, ctx.artifacts, ctx);
|
|
26306
|
-
if (!ctx.attach) {
|
|
26307
|
-
return "attach_file is not available here: this conversation's channel takes no file uploads \u2014 link to the file instead";
|
|
26308
|
-
}
|
|
26309
|
-
const readBytes = ctx.executor.readBytes?.bind(ctx.executor);
|
|
26310
|
-
if (!readBytes) {
|
|
26311
|
-
return "attach_file is not available here: this workspace cannot hand files over \u2014 link to the file instead";
|
|
26312
|
-
}
|
|
26313
|
-
let bytes;
|
|
26314
|
-
try {
|
|
26315
|
-
bytes = await readBytes(path);
|
|
26316
|
-
} catch (err2) {
|
|
26317
|
-
return `error: could not read ${path}: ${describe2(err2)}`;
|
|
26318
|
-
}
|
|
26319
|
-
if (bytes.byteLength === 0) return `error: ${path} is empty \u2014 nothing to attach`;
|
|
26320
|
-
try {
|
|
26321
|
-
await ctx.attach({ name, bytes, lead });
|
|
26322
|
-
} catch (err2) {
|
|
26323
|
-
return `error: the channel refused the upload of ${name}: ${describe2(err2)}`;
|
|
26324
|
-
}
|
|
26325
|
-
return `attached ${name} (${bytes.byteLength} bytes) to the conversation`;
|
|
26326
|
-
}
|
|
26327
|
-
};
|
|
26328
|
-
}
|
|
26329
|
-
});
|
|
26330
|
-
|
|
26331
|
-
// ../../src/core/dispatch/awaitChildren.ts
|
|
26332
|
-
function decideWait(inputs) {
|
|
26333
|
-
const { children, now, budgetEndsAt, timeoutAt, stop, followUpPending } = inputs;
|
|
26334
|
-
if ([...children.values()].every(isTerminal)) return { kind: "end", why: "all_ended" };
|
|
26335
|
-
if (stop !== void 0) return { kind: "end", why: "stop" };
|
|
26336
|
-
if (followUpPending) return { kind: "end", why: "follow_up" };
|
|
26337
|
-
if (now >= budgetEndsAt) return { kind: "end", why: "budget" };
|
|
26338
|
-
if (timeoutAt !== void 0 && now >= timeoutAt) return { kind: "end", why: "timeout" };
|
|
26339
|
-
return { kind: "wait", until: Math.min(budgetEndsAt, timeoutAt ?? Number.POSITIVE_INFINITY) };
|
|
26340
|
-
}
|
|
26341
|
-
function budgetEndOf(now, remainingMs) {
|
|
26342
|
-
return now + remainingMs - RUN_DEADLINE_RESERVE_MS;
|
|
26343
|
-
}
|
|
26344
|
-
function sleepUnlessAborted(ms, signal2) {
|
|
26345
|
-
return new Promise((resolve12) => {
|
|
26346
|
-
if (signal2?.aborted) return resolve12();
|
|
26347
|
-
const done = () => {
|
|
26348
|
-
clearTimeout(timer);
|
|
26349
|
-
signal2?.removeEventListener("abort", done);
|
|
26350
|
-
resolve12();
|
|
26351
|
-
};
|
|
26352
|
-
const timer = setTimeout(done, ms);
|
|
26353
|
-
signal2?.addEventListener("abort", done, { once: true });
|
|
26354
|
-
});
|
|
26355
|
-
}
|
|
26356
|
-
function waitCapabilityFor(deps) {
|
|
26357
|
-
return {
|
|
26358
|
-
stopRequested: () => deps.control.requested,
|
|
26359
|
-
followUpsPending: () => deps.inbox.size,
|
|
26360
|
-
watch: (ids, onChange) => {
|
|
26361
|
-
let replaying = true;
|
|
26362
|
-
const unsubscribe = deps.registry.subscribeIndex((event) => {
|
|
26363
|
-
if (event.type === "removed") {
|
|
26364
|
-
if (ids.has(event.id)) onChange();
|
|
26365
|
-
return;
|
|
26366
|
-
}
|
|
26367
|
-
if (!event.run.finished) return;
|
|
26368
|
-
if (ids.has(event.run.id)) onChange();
|
|
26369
|
-
else if (!replaying && deps.runId !== void 0 && event.run.parentRunId === deps.runId) onChange();
|
|
26370
|
-
});
|
|
26371
|
-
replaying = false;
|
|
26372
|
-
return unsubscribe;
|
|
26373
|
-
},
|
|
26374
|
-
now: deps.clock,
|
|
26375
|
-
sleep: deps.sleep ?? sleepUnlessAborted
|
|
26376
|
-
};
|
|
26070
|
+
if (!event.run.finished) return;
|
|
26071
|
+
if (ids.has(event.run.id)) onChange();
|
|
26072
|
+
else if (!replaying && deps.runId !== void 0 && event.run.parentRunId === deps.runId) onChange();
|
|
26073
|
+
});
|
|
26074
|
+
replaying = false;
|
|
26075
|
+
return unsubscribe;
|
|
26076
|
+
},
|
|
26077
|
+
now: deps.clock,
|
|
26078
|
+
sleep: deps.sleep ?? sleepUnlessAborted
|
|
26079
|
+
};
|
|
26377
26080
|
}
|
|
26378
26081
|
var AWAIT_POLL_MS, isTerminal, ChildrenWatch;
|
|
26379
26082
|
var init_awaitChildren = __esm({
|
|
@@ -27093,7 +26796,8 @@ async function copyStaged(files, deps) {
|
|
|
27093
26796
|
key,
|
|
27094
26797
|
name: file.name,
|
|
27095
26798
|
size: file.size,
|
|
27096
|
-
contentType: file.type
|
|
26799
|
+
contentType: file.type,
|
|
26800
|
+
messageId: file.messageId
|
|
27097
26801
|
});
|
|
27098
26802
|
return { file, basename: basename4, key };
|
|
27099
26803
|
} catch (err2) {
|
|
@@ -27152,7 +26856,8 @@ function stageThreadArtifacts(artifacts, deps) {
|
|
|
27152
26856
|
key: a.key,
|
|
27153
26857
|
name: a.name,
|
|
27154
26858
|
size: a.size,
|
|
27155
|
-
contentType: a.contentType
|
|
26859
|
+
contentType: a.contentType,
|
|
26860
|
+
messageId: deps.messageId
|
|
27156
26861
|
});
|
|
27157
26862
|
outcomes.push({ file, basename: basename4, key: a.key, earlier: true });
|
|
27158
26863
|
}
|
|
@@ -27458,145 +27163,444 @@ var init_session = __esm({
|
|
|
27458
27163
|
}
|
|
27459
27164
|
});
|
|
27460
27165
|
|
|
27461
|
-
// ../../src/tools/
|
|
27462
|
-
|
|
27463
|
-
var
|
|
27464
|
-
|
|
27465
|
-
"../../src/tools/workspace.ts"() {
|
|
27166
|
+
// ../../src/tools/skills.ts
|
|
27167
|
+
var listSkillsTool, useSkillTool;
|
|
27168
|
+
var init_skills = __esm({
|
|
27169
|
+
"../../src/tools/skills.ts"() {
|
|
27466
27170
|
"use strict";
|
|
27467
|
-
|
|
27468
|
-
init_handoff();
|
|
27469
|
-
init_reviewVerdict();
|
|
27470
|
-
init_bashTimeout();
|
|
27471
|
-
init_executor();
|
|
27472
|
-
init_shellQuote();
|
|
27473
|
-
init_diffDigest();
|
|
27474
|
-
init_web();
|
|
27475
|
-
init_github();
|
|
27476
|
-
init_skills();
|
|
27477
|
-
init_attach();
|
|
27478
|
-
init_runs2();
|
|
27479
|
-
init_session();
|
|
27480
|
-
bashTool = {
|
|
27481
|
-
name: "bash",
|
|
27482
|
-
description: "Run a bash command in the workspace directory. Use for git, gh, tests, builds, and inspecting files. Commands time out after 5 minutes (300000 ms) by default; pass timeoutMs when a command legitimately needs longer \u2014 a full test suite, a large build \u2014 up to the 1200000 ms (20 minute) maximum; a command is also clipped to the run's own remaining time, so it can never outlive the run. Output is truncated at 30k characters.",
|
|
27483
|
-
inputSchema: {
|
|
27484
|
-
type: "object",
|
|
27485
|
-
properties: {
|
|
27486
|
-
command: { type: "string", description: "The bash command to run" },
|
|
27487
|
-
timeoutMs: {
|
|
27488
|
-
type: "integer",
|
|
27489
|
-
description: "Optional timeout in milliseconds for this command (default 300000 = 5 min; max 1200000 = 20 min). Values outside [1000, 1200000] are clamped."
|
|
27490
|
-
}
|
|
27491
|
-
},
|
|
27492
|
-
required: ["command"]
|
|
27493
|
-
},
|
|
27494
|
-
async run(input, ctx) {
|
|
27495
|
-
const requested = input.timeoutMs;
|
|
27496
|
-
let timeoutMs = typeof requested === "number" && Number.isFinite(requested) ? clampBashTimeout(requested) : void 0;
|
|
27497
|
-
let clipNote = "";
|
|
27498
|
-
if (ctx.remainingMs) {
|
|
27499
|
-
const budget = bashBudgetWithinRun(timeoutMs ?? BASH_TIMEOUT_MS, ctx.remainingMs());
|
|
27500
|
-
if (budget.kind === "exhausted") return `exit 124:
|
|
27501
|
-
${budget.note}`;
|
|
27502
|
-
if (budget.kind === "clipped") {
|
|
27503
|
-
timeoutMs = budget.timeoutMs;
|
|
27504
|
-
clipNote = budget.note;
|
|
27505
|
-
}
|
|
27506
|
-
}
|
|
27507
|
-
const opts = {
|
|
27508
|
-
...ctx.signal ? { signal: ctx.signal } : {},
|
|
27509
|
-
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
27510
|
-
};
|
|
27511
|
-
const out = await ctx.executor.exec(
|
|
27512
|
-
String(input.command ?? ""),
|
|
27513
|
-
opts.signal !== void 0 || opts.timeoutMs !== void 0 ? opts : void 0
|
|
27514
|
-
);
|
|
27515
|
-
return clipNote ? `${out}
|
|
27516
|
-
${clipNote}` : out;
|
|
27517
|
-
}
|
|
27518
|
-
};
|
|
27519
|
-
readFileTool = {
|
|
27171
|
+
listSkillsTool = {
|
|
27520
27172
|
sideEffectFree: true,
|
|
27521
|
-
name: "
|
|
27522
|
-
description: "
|
|
27173
|
+
name: "list_skills",
|
|
27174
|
+
description: "List the skills available to you: each is a reusable methodology you can load into context with use_skill. Returns each skill's name and a one-line description. The same list is also in your system prompt.",
|
|
27523
27175
|
inputSchema: {
|
|
27524
27176
|
type: "object",
|
|
27525
|
-
properties: {
|
|
27526
|
-
path: { type: "string", description: "Relative path to the file" }
|
|
27527
|
-
},
|
|
27528
|
-
required: ["path"]
|
|
27177
|
+
properties: {}
|
|
27529
27178
|
},
|
|
27530
|
-
run(
|
|
27531
|
-
|
|
27179
|
+
async run(_input, ctx) {
|
|
27180
|
+
if (!ctx.skills) return "Skills are not available in this context.";
|
|
27181
|
+
const metas = ctx.skills.list(ctx.agentName ?? "");
|
|
27182
|
+
if (metas.length === 0) return "No skills are available to you.";
|
|
27183
|
+
return metas.map((m) => `- ${m.name}: ${m.description}`).join("\n");
|
|
27532
27184
|
}
|
|
27533
27185
|
};
|
|
27534
|
-
|
|
27535
|
-
|
|
27536
|
-
|
|
27186
|
+
useSkillTool = {
|
|
27187
|
+
sideEffectFree: true,
|
|
27188
|
+
name: "use_skill",
|
|
27189
|
+
description: "Load a skill's full instructions into your context by name (get names from list_skills or your system prompt). Returns the skill body \u2014 read it and follow it for the work at hand.",
|
|
27537
27190
|
inputSchema: {
|
|
27538
27191
|
type: "object",
|
|
27539
27192
|
properties: {
|
|
27540
|
-
|
|
27541
|
-
content: { type: "string", description: "Full file contents" }
|
|
27193
|
+
name: { type: "string", description: "The skill name to load (e.g. code-review-and-quality)" }
|
|
27542
27194
|
},
|
|
27543
|
-
required: ["
|
|
27195
|
+
required: ["name"]
|
|
27544
27196
|
},
|
|
27545
|
-
run(input, ctx) {
|
|
27546
|
-
|
|
27197
|
+
async run(input, ctx) {
|
|
27198
|
+
if (!ctx.skills) return "Skills are not available in this context.";
|
|
27199
|
+
const name = String(input.name ?? "").trim();
|
|
27200
|
+
if (!name) return "use_skill: provide a skill `name` (see list_skills).";
|
|
27201
|
+
const skill = ctx.skills.get(name);
|
|
27202
|
+
const inScope = skill && skill.agents.includes(ctx.agentName ?? "");
|
|
27203
|
+
if (!skill || !inScope) {
|
|
27204
|
+
const available = ctx.skills.list(ctx.agentName ?? "").map((m) => m.name);
|
|
27205
|
+
return `use_skill: no skill named "${name}" is available to you.` + (available.length > 0 ? ` Available: ${available.join(", ")}.` : "");
|
|
27206
|
+
}
|
|
27207
|
+
ctx.publish?.({
|
|
27208
|
+
type: "skill_use",
|
|
27209
|
+
skill: skill.name,
|
|
27210
|
+
description: skill.description,
|
|
27211
|
+
agent: ctx.agentName ?? "",
|
|
27212
|
+
...skill.source ? { source: skill.source } : {},
|
|
27213
|
+
...skill.upstream ? { upstream: { repo: skill.upstream.repo, commit: skill.upstream.commit } } : {},
|
|
27214
|
+
bodyBytes: Buffer.byteLength(skill.body, "utf8")
|
|
27215
|
+
});
|
|
27216
|
+
const footer = skill.source ? `
|
|
27217
|
+
|
|
27218
|
+
_(Skill source: ${skill.source})_` : "";
|
|
27219
|
+
return `# Skill: ${skill.name}
|
|
27220
|
+
|
|
27221
|
+
${skill.body}${footer}`;
|
|
27547
27222
|
}
|
|
27548
27223
|
};
|
|
27549
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
27554
|
-
|
|
27555
|
-
|
|
27224
|
+
}
|
|
27225
|
+
});
|
|
27226
|
+
|
|
27227
|
+
// ../../src/tools/status.ts
|
|
27228
|
+
var updateStatusTool;
|
|
27229
|
+
var init_status2 = __esm({
|
|
27230
|
+
"../../src/tools/status.ts"() {
|
|
27231
|
+
"use strict";
|
|
27232
|
+
updateStatusTool = {
|
|
27233
|
+
name: "update_status",
|
|
27234
|
+
description: "Update the short user-facing status checklist shown while you work. Call it right after planning (all items pending) and again whenever an item's state changes. Format: one item per line, prefixed with a state marker: \u2713 done, \u2731 in progress, \u25CB pending. Keep it to 3-6 short outcome-oriented items (what, not how \u2014 never raw commands).",
|
|
27556
27235
|
inputSchema: {
|
|
27557
27236
|
type: "object",
|
|
27558
27237
|
properties: {
|
|
27559
|
-
|
|
27238
|
+
checklist: {
|
|
27560
27239
|
type: "string",
|
|
27561
|
-
description: "
|
|
27240
|
+
description: "The full checklist, one '\u2713|\u2731|\u25CB item' per line (replaces the previous one)"
|
|
27562
27241
|
}
|
|
27563
|
-
}
|
|
27242
|
+
},
|
|
27243
|
+
required: ["checklist"]
|
|
27564
27244
|
},
|
|
27565
27245
|
async run(input, ctx) {
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
if (base.startsWith("-")) {
|
|
27569
|
-
return "diff_digest: base ref may not start with '-' (rejected to prevent git option injection).";
|
|
27570
|
-
}
|
|
27571
|
-
const baseExpr = base ? shellQuote(base) : '"$(git rev-parse --abbrev-ref origin/HEAD 2>/dev/null || echo origin/main)"';
|
|
27572
|
-
const range = `--end-of-options ${baseExpr}...HEAD`;
|
|
27573
|
-
const listing = `git diff --numstat ${range} && echo ${NAME_STATUS_MARK} && git diff --name-status ${range}`;
|
|
27574
|
-
let raw = await ctx.executor.exec(listing);
|
|
27575
|
-
const incomplete = () => {
|
|
27576
|
-
const reason = "the file listing exceeded the executor's output cap";
|
|
27577
|
-
ctx.onDigest?.({ complete: false, base: baseName, reason });
|
|
27578
|
-
return `diff_digest: ${reason} \u2014 the digest cannot state totals for this change (base \`${baseName}\`). Read the diff file by file (\`git diff --name-only ${baseName}...HEAD\`).`;
|
|
27579
|
-
};
|
|
27580
|
-
if (OUTPUT_CUT_RE.test(raw)) return incomplete();
|
|
27581
|
-
let marker = NAME_STATUS_MARK_LINE.exec(raw);
|
|
27582
|
-
if (!marker) {
|
|
27583
|
-
const shallow = (await ctx.executor.exec("git rev-parse --is-shallow-repository")).trim() === "true";
|
|
27584
|
-
if (shallow) {
|
|
27585
|
-
await ctx.executor.exec('git fetch --unshallow --quiet origin "+refs/heads/*:refs/remotes/origin/*"');
|
|
27586
|
-
raw = await ctx.executor.exec(listing);
|
|
27587
|
-
if (OUTPUT_CUT_RE.test(raw)) return incomplete();
|
|
27588
|
-
marker = NAME_STATUS_MARK_LINE.exec(raw);
|
|
27589
|
-
}
|
|
27590
|
-
}
|
|
27591
|
-
if (!marker) {
|
|
27592
|
-
return `diff_digest: could not compute the diff (base \`${baseName}\`).
|
|
27593
|
-
${raw.trim()}`;
|
|
27594
|
-
}
|
|
27595
|
-
const digest = distillDiffStats(raw.slice(0, marker.index), raw.slice(marker.index + marker[0].length));
|
|
27596
|
-
ctx.onDigest?.({ complete: true, base: baseName, totals: digest.totals });
|
|
27597
|
-
return digest.text;
|
|
27246
|
+
ctx.reportProgress?.(String(input.checklist ?? ""));
|
|
27247
|
+
return "status updated";
|
|
27598
27248
|
}
|
|
27599
27249
|
};
|
|
27250
|
+
}
|
|
27251
|
+
});
|
|
27252
|
+
|
|
27253
|
+
// ../../src/core/prDescription.ts
|
|
27254
|
+
import { z as z20 } from "zod";
|
|
27255
|
+
function parsePrDescription(input) {
|
|
27256
|
+
return PrDescriptionSchema.parse(input);
|
|
27257
|
+
}
|
|
27258
|
+
function redactPrDescription(d) {
|
|
27259
|
+
return mapStringLeaves(d, redactSecrets);
|
|
27260
|
+
}
|
|
27261
|
+
function mapStringLeaves(value, fn) {
|
|
27262
|
+
if (typeof value === "string") return fn(value);
|
|
27263
|
+
if (Array.isArray(value)) return value.map((v) => mapStringLeaves(v, fn));
|
|
27264
|
+
if (typeof value === "object" && value !== null) {
|
|
27265
|
+
return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, mapStringLeaves(v, fn)]));
|
|
27266
|
+
}
|
|
27267
|
+
return value;
|
|
27268
|
+
}
|
|
27269
|
+
function encodeGithubPathSegments(path) {
|
|
27270
|
+
return path.split("/").map(encodeURIComponent).join("/");
|
|
27271
|
+
}
|
|
27272
|
+
function anchorUrl(ctx, a) {
|
|
27273
|
+
return `https://github.com/${ctx.repo}/blob/${ctx.headSha}/${encodeGithubPathSegments(a.path)}#L${a.from}-L${a.to}`;
|
|
27274
|
+
}
|
|
27275
|
+
function cell2(s) {
|
|
27276
|
+
return s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\s*\n\s*/g, " ");
|
|
27277
|
+
}
|
|
27278
|
+
function renderPrDescriptionMarkdown(desc, ctx) {
|
|
27279
|
+
if (!REPO_RE2.test(ctx.repo))
|
|
27280
|
+
throw new Error(`renderPrDescriptionMarkdown: repo must be owner/name, got "${ctx.repo}"`);
|
|
27281
|
+
if (!SHA_RE2.test(ctx.headSha))
|
|
27282
|
+
throw new Error(`renderPrDescriptionMarkdown: headSha must be a full 40-char lowercase sha, got "${ctx.headSha}"`);
|
|
27283
|
+
const out = [];
|
|
27284
|
+
out.push("## TL;DR", "", desc.tldr, "");
|
|
27285
|
+
out.push("## What & why", "", desc.whatWhy, "");
|
|
27286
|
+
out.push("## Tour", "");
|
|
27287
|
+
desc.tour.forEach((step, i) => {
|
|
27288
|
+
out.push(`### ${i + 1}. ${step.title}`, "", step.description, "");
|
|
27289
|
+
if (step.lookFor) out.push(`**Look for:** ${step.lookFor}`, "");
|
|
27290
|
+
out.push(anchorUrl(ctx, step.anchor), "");
|
|
27291
|
+
});
|
|
27292
|
+
out.push(`### ${desc.tour.length + 1}. Remaining changes`, "");
|
|
27293
|
+
if (desc.remaining.length === 0) out.push("- none \u2014 every touched file is covered by a step above", "");
|
|
27294
|
+
else {
|
|
27295
|
+
for (const r of desc.remaining) out.push(`- \`${r.path}\` \u2014 ${r.note}`);
|
|
27296
|
+
out.push("");
|
|
27297
|
+
}
|
|
27298
|
+
out.push("## Decisions", "");
|
|
27299
|
+
for (const d of desc.decisions) out.push(`- **${d.title.replace(/\.?$/, ".")}** ${d.rationale}`);
|
|
27300
|
+
out.push("");
|
|
27301
|
+
out.push("## Risks & implications", "", desc.risks, "");
|
|
27302
|
+
out.push("## Validation", "");
|
|
27303
|
+
if (desc.validation.summary) out.push(desc.validation.summary, "");
|
|
27304
|
+
out.push("| Criterion | Proof |", "|---|---|");
|
|
27305
|
+
for (const c of desc.validation.criteria) out.push(`| ${cell2(c.criterion)} | ${cell2(c.proof)} |`);
|
|
27306
|
+
out.push("", GENERATED_FOOTER, "");
|
|
27307
|
+
return out.join("\n");
|
|
27308
|
+
}
|
|
27309
|
+
function parsePrDescriptionMarkdown(body) {
|
|
27310
|
+
const problems = [];
|
|
27311
|
+
const lines = body.replace(/\r\n?/g, "\n").split("\n").filter((l) => l.trim() !== GENERATED_FOOTER);
|
|
27312
|
+
const fenced = fenceMask(lines);
|
|
27313
|
+
const sections = splitSections(lines, fenced, problems);
|
|
27314
|
+
const description = { tour: [] };
|
|
27315
|
+
const tldr = sections.get("tldr");
|
|
27316
|
+
if (tldr) setProse(description, "tldr", tldr, problems);
|
|
27317
|
+
else {
|
|
27318
|
+
problems.push("no `## TL;DR` section \u2014 the tldr is the body's first paragraph");
|
|
27319
|
+
const first = firstParagraph(lines, fenced);
|
|
27320
|
+
if (first) description.tldr = first;
|
|
27321
|
+
}
|
|
27322
|
+
const whatWhy = sections.get("whatWhy");
|
|
27323
|
+
if (whatWhy) setProse(description, "whatWhy", whatWhy, problems);
|
|
27324
|
+
const tour = sections.get("tour");
|
|
27325
|
+
if (tour) {
|
|
27326
|
+
const parsed = parseTour(tour.lines, fenced.slice(tour.start, tour.start + tour.lines.length), problems);
|
|
27327
|
+
description.tour = parsed.tour;
|
|
27328
|
+
if (parsed.remaining) description.remaining = parsed.remaining;
|
|
27329
|
+
}
|
|
27330
|
+
const decisions = sections.get("decisions");
|
|
27331
|
+
if (decisions) description.decisions = parseDecisions(decisions.lines, problems);
|
|
27332
|
+
const risks = sections.get("risks");
|
|
27333
|
+
if (risks) setProse(description, "risks", risks, problems);
|
|
27334
|
+
const validation = sections.get("validation");
|
|
27335
|
+
if (validation) description.validation = parseValidation(validation.lines, problems);
|
|
27336
|
+
for (const [key, heading] of SECTIONS) {
|
|
27337
|
+
if (key === "tldr" || sections.has(key)) continue;
|
|
27338
|
+
problems.push(`no \`## ${heading}\` section`);
|
|
27339
|
+
}
|
|
27340
|
+
return { description, complete: problems.length === 0, problems };
|
|
27341
|
+
}
|
|
27342
|
+
function fenceMask(lines) {
|
|
27343
|
+
let open2;
|
|
27344
|
+
return lines.map((l) => {
|
|
27345
|
+
const m = /^\s*(`{3,}|~{3,})/.exec(l);
|
|
27346
|
+
if (m && (open2 === void 0 || m[1][0] === open2[0])) {
|
|
27347
|
+
const wasOpen = open2 !== void 0;
|
|
27348
|
+
open2 = wasOpen ? void 0 : m[1];
|
|
27349
|
+
return true;
|
|
27350
|
+
}
|
|
27351
|
+
return open2 !== void 0;
|
|
27352
|
+
});
|
|
27353
|
+
}
|
|
27354
|
+
function splitSections(lines, fenced, problems) {
|
|
27355
|
+
const byHeading = new Map(SECTIONS.map(([key, heading]) => [heading.toLowerCase(), key]));
|
|
27356
|
+
const out = /* @__PURE__ */ new Map();
|
|
27357
|
+
let current;
|
|
27358
|
+
lines.forEach((line3, i) => {
|
|
27359
|
+
const m = fenced[i] ? null : HEADING_RE.exec(line3);
|
|
27360
|
+
if (m && m[1] === "##") {
|
|
27361
|
+
const key = byHeading.get(m[2].replace(/\s+/g, " ").toLowerCase());
|
|
27362
|
+
if (key === void 0)
|
|
27363
|
+
current = void 0;
|
|
27364
|
+
else if (out.has(key)) {
|
|
27365
|
+
problems.push(`duplicate \`## ${SECTIONS.find(([k]) => k === key)[1]}\` section \u2014 the first one stands`);
|
|
27366
|
+
current = void 0;
|
|
27367
|
+
} else {
|
|
27368
|
+
current = { start: i + 1, lines: [] };
|
|
27369
|
+
out.set(key, current);
|
|
27370
|
+
}
|
|
27371
|
+
return;
|
|
27372
|
+
}
|
|
27373
|
+
current?.lines.push(line3);
|
|
27374
|
+
});
|
|
27375
|
+
return out;
|
|
27376
|
+
}
|
|
27377
|
+
function firstParagraph(lines, fenced) {
|
|
27378
|
+
const para = [];
|
|
27379
|
+
for (let i = 0; i < lines.length; i++) {
|
|
27380
|
+
const l = lines[i];
|
|
27381
|
+
if (l.trim() === "") {
|
|
27382
|
+
if (para.length) break;
|
|
27383
|
+
continue;
|
|
27384
|
+
}
|
|
27385
|
+
if (fenced[i] || para.length === 0 && HEADING_RE.test(l)) continue;
|
|
27386
|
+
para.push(l.trim());
|
|
27387
|
+
}
|
|
27388
|
+
return para.length ? para.join("\n") : void 0;
|
|
27389
|
+
}
|
|
27390
|
+
function setProse(description, key, section, problems) {
|
|
27391
|
+
const text = section.lines.join("\n").trim();
|
|
27392
|
+
if (text === "") problems.push(`\`## ${SECTIONS.find(([k]) => k === key)[1]}\` section is empty`);
|
|
27393
|
+
else description[key] = text;
|
|
27394
|
+
}
|
|
27395
|
+
function parseTour(lines, fenced, problems) {
|
|
27396
|
+
const steps = [];
|
|
27397
|
+
let preamble = "";
|
|
27398
|
+
lines.forEach((line3, i) => {
|
|
27399
|
+
const m = fenced[i] ? null : STEP_RE.exec(line3);
|
|
27400
|
+
if (m) steps.push({ label: m[1] ?? String(steps.length + 1), title: m[2], lines: [] });
|
|
27401
|
+
else if (steps.length) steps[steps.length - 1].lines.push(line3);
|
|
27402
|
+
else preamble += line3;
|
|
27403
|
+
});
|
|
27404
|
+
if (preamble.trim() !== "") problems.push("Tour: text before the first `### N.` step is ignored");
|
|
27405
|
+
const isRemaining = (title) => /^remaining changes$/i.test(title);
|
|
27406
|
+
const tour = [];
|
|
27407
|
+
let remaining;
|
|
27408
|
+
for (const step of steps) {
|
|
27409
|
+
if (isRemaining(step.title)) {
|
|
27410
|
+
remaining = parseRemaining(step.lines, problems);
|
|
27411
|
+
continue;
|
|
27412
|
+
}
|
|
27413
|
+
const parsed = parseStep(step, problems);
|
|
27414
|
+
if (parsed) tour.push(parsed);
|
|
27415
|
+
}
|
|
27416
|
+
if (!steps.some((s) => !isRemaining(s.title))) problems.push("Tour has no steps");
|
|
27417
|
+
if (remaining === void 0) problems.push("Tour has no `Remaining changes` step");
|
|
27418
|
+
return { tour, ...remaining ? { remaining } : {} };
|
|
27419
|
+
}
|
|
27420
|
+
function parseStep(step, problems) {
|
|
27421
|
+
const where = `step ${step.label} (${step.title})`;
|
|
27422
|
+
let link;
|
|
27423
|
+
const descLines = [];
|
|
27424
|
+
const lookForLines = [];
|
|
27425
|
+
let inLookFor = false;
|
|
27426
|
+
for (const line3 of step.lines) {
|
|
27427
|
+
const m = PERMALINK_RE.exec(line3.trim());
|
|
27428
|
+
if (m) {
|
|
27429
|
+
if (link) problems.push(`${where}: more than one permalink \u2014 the first is the anchor`);
|
|
27430
|
+
else link = m;
|
|
27431
|
+
inLookFor = false;
|
|
27432
|
+
continue;
|
|
27433
|
+
}
|
|
27434
|
+
const lf = LOOK_FOR_RE.exec(line3.trim());
|
|
27435
|
+
if (lf) {
|
|
27436
|
+
lookForLines.push(lf[1]);
|
|
27437
|
+
inLookFor = true;
|
|
27438
|
+
continue;
|
|
27439
|
+
}
|
|
27440
|
+
if (inLookFor) {
|
|
27441
|
+
if (line3.trim() === "") inLookFor = false;
|
|
27442
|
+
else lookForLines.push(line3.trim());
|
|
27443
|
+
continue;
|
|
27444
|
+
}
|
|
27445
|
+
descLines.push(line3);
|
|
27446
|
+
}
|
|
27447
|
+
if (!link) {
|
|
27448
|
+
problems.push(`${where}: no permalink line \u2014 dropped`);
|
|
27449
|
+
return void 0;
|
|
27450
|
+
}
|
|
27451
|
+
let path;
|
|
27452
|
+
try {
|
|
27453
|
+
path = link[2].split("/").map(decodeURIComponent).join("/");
|
|
27454
|
+
} catch {
|
|
27455
|
+
problems.push(`${where}: permalink path is not valid percent-encoding \u2014 dropped`);
|
|
27456
|
+
return void 0;
|
|
27457
|
+
}
|
|
27458
|
+
const from = Number(link[3]);
|
|
27459
|
+
const anchor = TourAnchorSchema.safeParse({ path, from, to: link[4] === void 0 ? from : Number(link[4]) });
|
|
27460
|
+
if (!anchor.success) {
|
|
27461
|
+
const issue = anchor.error.issues[0];
|
|
27462
|
+
problems.push(`${where}: ${["anchor", ...issue.path].join(".")} ${issue.message} \u2014 dropped`);
|
|
27463
|
+
return void 0;
|
|
27464
|
+
}
|
|
27465
|
+
const lookFor = lookForLines.join("\n").trim();
|
|
27466
|
+
return {
|
|
27467
|
+
title: step.title,
|
|
27468
|
+
// A parsed step may carry an empty description (a human's heading +
|
|
27469
|
+
// permalink): this is a read-back, not a submission the schema gates.
|
|
27470
|
+
description: descLines.join("\n").replace(/\n{3,}/g, "\n\n").trim(),
|
|
27471
|
+
...lookFor ? { lookFor } : {},
|
|
27472
|
+
anchor: { ...anchor.data, sha: link[1] }
|
|
27473
|
+
};
|
|
27474
|
+
}
|
|
27475
|
+
function parseRemaining(lines, problems) {
|
|
27476
|
+
const out = [];
|
|
27477
|
+
for (const raw of lines) {
|
|
27478
|
+
const line3 = raw.trim();
|
|
27479
|
+
if (line3 === "" || REMAINING_NONE_RE.test(line3)) continue;
|
|
27480
|
+
const m = REMAINING_LINE_RE.exec(line3);
|
|
27481
|
+
if (m) out.push({ path: m[1], note: m[2].trim() });
|
|
27482
|
+
else if (!line3.startsWith("-") && out.length) out[out.length - 1].note += `
|
|
27483
|
+
${line3}`;
|
|
27484
|
+
else problems.push(`Remaining changes: unrecognized line \`${line3}\``);
|
|
27485
|
+
}
|
|
27486
|
+
return out;
|
|
27487
|
+
}
|
|
27488
|
+
function parseDecisions(lines, problems) {
|
|
27489
|
+
const out = [];
|
|
27490
|
+
for (const raw of lines) {
|
|
27491
|
+
const line3 = raw.trim();
|
|
27492
|
+
if (line3 === "") continue;
|
|
27493
|
+
const m = DECISION_RE.exec(line3);
|
|
27494
|
+
if (m) out.push({ title: m[1].replace(/\.$/, ""), rationale: m[2].trim() });
|
|
27495
|
+
else if (!line3.startsWith("-") && out.length) out[out.length - 1].rationale += `
|
|
27496
|
+
${line3}`;
|
|
27497
|
+
else problems.push(`Decisions: unrecognized line \`${line3}\``);
|
|
27498
|
+
}
|
|
27499
|
+
return out;
|
|
27500
|
+
}
|
|
27501
|
+
function parseValidation(lines, problems) {
|
|
27502
|
+
const summary = [];
|
|
27503
|
+
const criteria = [];
|
|
27504
|
+
let header4 = false;
|
|
27505
|
+
for (const raw of lines) {
|
|
27506
|
+
const line3 = raw.trim();
|
|
27507
|
+
if (!header4) {
|
|
27508
|
+
if (TABLE_HEADER_RE.test(line3)) header4 = true;
|
|
27509
|
+
else summary.push(raw);
|
|
27510
|
+
continue;
|
|
27511
|
+
}
|
|
27512
|
+
if (line3 === "" || TABLE_RULE_RE.test(line3)) continue;
|
|
27513
|
+
const cells = splitCells(line3);
|
|
27514
|
+
if (cells.length >= 2) criteria.push({ criterion: cells[0], proof: cells[1] });
|
|
27515
|
+
else problems.push(`Validation: unrecognized table line \`${line3}\``);
|
|
27516
|
+
}
|
|
27517
|
+
if (!header4) problems.push("Validation: no `| Criterion | Proof |` table");
|
|
27518
|
+
const text = summary.join("\n").trim();
|
|
27519
|
+
return { ...text ? { summary: text } : {}, criteria };
|
|
27520
|
+
}
|
|
27521
|
+
function splitCells(row) {
|
|
27522
|
+
const cells = [];
|
|
27523
|
+
let cur = "";
|
|
27524
|
+
const inner = row.replace(/^\|/, "").replace(/\|$/, "");
|
|
27525
|
+
for (let i = 0; i < inner.length; i++) {
|
|
27526
|
+
const ch = inner[i];
|
|
27527
|
+
if (ch === "\\" && i + 1 < inner.length) {
|
|
27528
|
+
cur += inner[++i];
|
|
27529
|
+
} else if (ch === "|") {
|
|
27530
|
+
cells.push(cur.trim());
|
|
27531
|
+
cur = "";
|
|
27532
|
+
} else cur += ch;
|
|
27533
|
+
}
|
|
27534
|
+
cells.push(cur.trim());
|
|
27535
|
+
return cells;
|
|
27536
|
+
}
|
|
27537
|
+
var line2, prose, TourAnchorSchema, TourStepSchema, PrDescriptionSchema, REPO_RE2, SHA_RE2, GENERATED_FOOTER, SECTIONS, PERMALINK_RE, HEADING_RE, STEP_RE, LOOK_FOR_RE, REMAINING_LINE_RE, REMAINING_NONE_RE, DECISION_RE, TABLE_HEADER_RE, TABLE_RULE_RE;
|
|
27538
|
+
var init_prDescription = __esm({
|
|
27539
|
+
"../../src/core/prDescription.ts"() {
|
|
27540
|
+
"use strict";
|
|
27541
|
+
init_redact();
|
|
27542
|
+
line2 = z20.string().trim().min(1).refine((s) => !/[\r\n]/.test(s), "must be a single line (no embedded newlines)");
|
|
27543
|
+
prose = z20.string().trim().min(1);
|
|
27544
|
+
TourAnchorSchema = z20.object({
|
|
27545
|
+
// Repo-relative: no leading `/`, no `..` SEGMENT (a filename like `a..b.ts` is fine).
|
|
27546
|
+
path: z20.string().trim().min(1).refine((p) => !p.startsWith("/") && !p.split("/").includes(".."), "path must be repo-relative"),
|
|
27547
|
+
from: z20.number().int().positive(),
|
|
27548
|
+
to: z20.number().int().positive()
|
|
27549
|
+
}).refine((a) => a.to >= a.from, "to must be >= from");
|
|
27550
|
+
TourStepSchema = z20.object({
|
|
27551
|
+
title: line2,
|
|
27552
|
+
description: prose,
|
|
27553
|
+
lookFor: prose.optional(),
|
|
27554
|
+
anchor: TourAnchorSchema
|
|
27555
|
+
});
|
|
27556
|
+
PrDescriptionSchema = z20.object({
|
|
27557
|
+
/** The PR title's single source. Metadata for the PR's own title field —
|
|
27558
|
+
* never rendered into the body (GitHub shows the title itself). */
|
|
27559
|
+
title: line2,
|
|
27560
|
+
tldr: prose,
|
|
27561
|
+
whatWhy: prose,
|
|
27562
|
+
tour: z20.array(TourStepSchema).min(1),
|
|
27563
|
+
/** Every touched file the Tour steps did not cover, one line each. */
|
|
27564
|
+
remaining: z20.array(z20.object({ path: line2, note: prose })),
|
|
27565
|
+
decisions: z20.array(z20.object({ title: line2, rationale: prose })).min(1),
|
|
27566
|
+
risks: prose,
|
|
27567
|
+
validation: z20.object({
|
|
27568
|
+
summary: prose.optional(),
|
|
27569
|
+
criteria: z20.array(z20.object({ criterion: prose, proof: prose })).min(1)
|
|
27570
|
+
})
|
|
27571
|
+
});
|
|
27572
|
+
REPO_RE2 = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
27573
|
+
SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
27574
|
+
GENERATED_FOOTER = "\u{1F916} Generated with [Claude Code](https://claude.com/claude-code)";
|
|
27575
|
+
SECTIONS = [
|
|
27576
|
+
["tldr", "TL;DR"],
|
|
27577
|
+
["whatWhy", "What & why"],
|
|
27578
|
+
["tour", "Tour"],
|
|
27579
|
+
["decisions", "Decisions"],
|
|
27580
|
+
["risks", "Risks & implications"],
|
|
27581
|
+
["validation", "Validation"]
|
|
27582
|
+
];
|
|
27583
|
+
PERMALINK_RE = /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/blob\/([0-9a-f]{7,40})\/([^#\s]+)#L(\d+)(?:-L(\d+))?$/;
|
|
27584
|
+
HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
|
|
27585
|
+
STEP_RE = /^###\s+(?:(\d+)\.\s+)?(.+?)\s*$/;
|
|
27586
|
+
LOOK_FOR_RE = /^\*\*Look for:\*\*\s*(.*)$/;
|
|
27587
|
+
REMAINING_LINE_RE = /^-\s+`([^`]+)`\s+—\s*(.*)$/;
|
|
27588
|
+
REMAINING_NONE_RE = /^-\s+none\b/i;
|
|
27589
|
+
DECISION_RE = /^-\s+\*\*(.+?)\*\*\s*(.*)$/;
|
|
27590
|
+
TABLE_HEADER_RE = /^\|\s*criterion\s*\|\s*proof\s*\|$/i;
|
|
27591
|
+
TABLE_RULE_RE = /^\|?\s*:?-{3,}:?\s*\|/;
|
|
27592
|
+
}
|
|
27593
|
+
});
|
|
27594
|
+
|
|
27595
|
+
// ../../src/tools/submit.ts
|
|
27596
|
+
import { z as z21 } from "zod";
|
|
27597
|
+
var submitVerdictTool, submitDispositionsTool, submitPrDescriptionTool, handoffEntry, submitHandoffTool;
|
|
27598
|
+
var init_submit = __esm({
|
|
27599
|
+
"../../src/tools/submit.ts"() {
|
|
27600
|
+
"use strict";
|
|
27601
|
+
init_prDescription();
|
|
27602
|
+
init_reviewVerdict();
|
|
27603
|
+
init_handoff();
|
|
27600
27604
|
submitVerdictTool = {
|
|
27601
27605
|
name: "submit_verdict",
|
|
27602
27606
|
failsInText: true,
|
|
@@ -27817,22 +27821,92 @@ ${raw.trim()}`;
|
|
|
27817
27821
|
return `handoff recorded: ${count(h.deviations.length, "deviation", "deviations")}, ${count(h.followUps.length, "follow-up", "follow-ups")}, ${h.unproven.length} unproven; a later call replaces this one`;
|
|
27818
27822
|
}
|
|
27819
27823
|
};
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27824
|
+
}
|
|
27825
|
+
});
|
|
27826
|
+
|
|
27827
|
+
// ../../src/tools/workspace.ts
|
|
27828
|
+
var bashTool, readFileTool, writeFileTool, TOOLSETS;
|
|
27829
|
+
var init_workspace = __esm({
|
|
27830
|
+
"../../src/tools/workspace.ts"() {
|
|
27831
|
+
"use strict";
|
|
27832
|
+
init_bashTimeout();
|
|
27833
|
+
init_executor();
|
|
27834
|
+
init_attach();
|
|
27835
|
+
init_diffDigest2();
|
|
27836
|
+
init_github();
|
|
27837
|
+
init_runs2();
|
|
27838
|
+
init_session();
|
|
27839
|
+
init_skills();
|
|
27840
|
+
init_status2();
|
|
27841
|
+
init_submit();
|
|
27842
|
+
init_web();
|
|
27843
|
+
bashTool = {
|
|
27844
|
+
name: "bash",
|
|
27845
|
+
description: "Run a bash command in the workspace directory. Use for git, gh, tests, builds, and inspecting files. Commands time out after 5 minutes (300000 ms) by default; pass timeoutMs when a command legitimately needs longer \u2014 a full test suite, a large build \u2014 up to the 1200000 ms (20 minute) maximum; a command is also clipped to the run's own remaining time, so it can never outlive the run. Output is truncated at 30k characters.",
|
|
27823
27846
|
inputSchema: {
|
|
27824
27847
|
type: "object",
|
|
27825
27848
|
properties: {
|
|
27826
|
-
|
|
27827
|
-
|
|
27828
|
-
|
|
27849
|
+
command: { type: "string", description: "The bash command to run" },
|
|
27850
|
+
timeoutMs: {
|
|
27851
|
+
type: "integer",
|
|
27852
|
+
description: "Optional timeout in milliseconds for this command (default 300000 = 5 min; max 1200000 = 20 min). Values outside [1000, 1200000] are clamped."
|
|
27829
27853
|
}
|
|
27830
27854
|
},
|
|
27831
|
-
required: ["
|
|
27855
|
+
required: ["command"]
|
|
27832
27856
|
},
|
|
27833
27857
|
async run(input, ctx) {
|
|
27834
|
-
|
|
27835
|
-
|
|
27858
|
+
const requested = input.timeoutMs;
|
|
27859
|
+
let timeoutMs = typeof requested === "number" && Number.isFinite(requested) ? clampBashTimeout(requested) : void 0;
|
|
27860
|
+
let clipNote = "";
|
|
27861
|
+
if (ctx.remainingMs) {
|
|
27862
|
+
const budget = bashBudgetWithinRun(timeoutMs ?? BASH_TIMEOUT_MS, ctx.remainingMs());
|
|
27863
|
+
if (budget.kind === "exhausted") return `exit 124:
|
|
27864
|
+
${budget.note}`;
|
|
27865
|
+
if (budget.kind === "clipped") {
|
|
27866
|
+
timeoutMs = budget.timeoutMs;
|
|
27867
|
+
clipNote = budget.note;
|
|
27868
|
+
}
|
|
27869
|
+
}
|
|
27870
|
+
const opts = {
|
|
27871
|
+
...ctx.signal ? { signal: ctx.signal } : {},
|
|
27872
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
27873
|
+
};
|
|
27874
|
+
const out = await ctx.executor.exec(
|
|
27875
|
+
String(input.command ?? ""),
|
|
27876
|
+
opts.signal !== void 0 || opts.timeoutMs !== void 0 ? opts : void 0
|
|
27877
|
+
);
|
|
27878
|
+
return clipNote ? `${out}
|
|
27879
|
+
${clipNote}` : out;
|
|
27880
|
+
}
|
|
27881
|
+
};
|
|
27882
|
+
readFileTool = {
|
|
27883
|
+
sideEffectFree: true,
|
|
27884
|
+
name: "read_file",
|
|
27885
|
+
description: "Read a file from the workspace. Path is relative to the workspace root.",
|
|
27886
|
+
inputSchema: {
|
|
27887
|
+
type: "object",
|
|
27888
|
+
properties: {
|
|
27889
|
+
path: { type: "string", description: "Relative path to the file" }
|
|
27890
|
+
},
|
|
27891
|
+
required: ["path"]
|
|
27892
|
+
},
|
|
27893
|
+
run(input, ctx) {
|
|
27894
|
+
return ctx.executor.readFile(String(input.path ?? ""));
|
|
27895
|
+
}
|
|
27896
|
+
};
|
|
27897
|
+
writeFileTool = {
|
|
27898
|
+
name: "write_file",
|
|
27899
|
+
description: "Write a file in the workspace (creates parent directories). Path is relative to the workspace root.",
|
|
27900
|
+
inputSchema: {
|
|
27901
|
+
type: "object",
|
|
27902
|
+
properties: {
|
|
27903
|
+
path: { type: "string", description: "Relative path to the file" },
|
|
27904
|
+
content: { type: "string", description: "Full file contents" }
|
|
27905
|
+
},
|
|
27906
|
+
required: ["path", "content"]
|
|
27907
|
+
},
|
|
27908
|
+
run(input, ctx) {
|
|
27909
|
+
return ctx.executor.writeFile(String(input.path ?? ""), String(input.content ?? ""));
|
|
27836
27910
|
}
|
|
27837
27911
|
};
|
|
27838
27912
|
TOOLSETS = {
|
|
@@ -28292,7 +28366,7 @@ var init_route = __esm({
|
|
|
28292
28366
|
init_registry();
|
|
28293
28367
|
init_workspace();
|
|
28294
28368
|
init_config2();
|
|
28295
|
-
|
|
28369
|
+
init_provider();
|
|
28296
28370
|
init_redact();
|
|
28297
28371
|
init_statusCardFrame();
|
|
28298
28372
|
init_spawn();
|
|
@@ -28309,6 +28383,33 @@ var init_route = __esm({
|
|
|
28309
28383
|
}
|
|
28310
28384
|
});
|
|
28311
28385
|
|
|
28386
|
+
// ../../src/core/chatMessage.ts
|
|
28387
|
+
function toolResultText(content) {
|
|
28388
|
+
if (typeof content === "string") return content;
|
|
28389
|
+
return content.map((p) => {
|
|
28390
|
+
if (p.type === "text") return p.text;
|
|
28391
|
+
if (p.type === "image") return `[image ${p.mediaType}, ${base64Bytes(p.data)} bytes]`;
|
|
28392
|
+
return `[document ${p.name ?? "document"} (${p.mediaType}), ${base64Bytes(p.data)} bytes]`;
|
|
28393
|
+
}).join("\n");
|
|
28394
|
+
}
|
|
28395
|
+
function capToolResultContent(content, cap = MAX_TOOL_RESULT_CHARS) {
|
|
28396
|
+
const capText = (text) => text.length > cap ? `${text.slice(0, cap)}
|
|
28397
|
+
\u2026[tool result truncated: ${text.length - cap} of ${text.length} characters cut \u2014 ask for a narrower slice]` : text;
|
|
28398
|
+
if (typeof content === "string") return capText(content);
|
|
28399
|
+
return content.map((p) => p.type === "text" ? { ...p, text: capText(p.text) } : p);
|
|
28400
|
+
}
|
|
28401
|
+
function base64Bytes(b64) {
|
|
28402
|
+
const padding = b64.endsWith("==") ? 2 : b64.endsWith("=") ? 1 : 0;
|
|
28403
|
+
return Math.floor(b64.length * 3 / 4) - padding;
|
|
28404
|
+
}
|
|
28405
|
+
var MAX_TOOL_RESULT_CHARS;
|
|
28406
|
+
var init_chatMessage = __esm({
|
|
28407
|
+
"../../src/core/chatMessage.ts"() {
|
|
28408
|
+
"use strict";
|
|
28409
|
+
MAX_TOOL_RESULT_CHARS = 12e4;
|
|
28410
|
+
}
|
|
28411
|
+
});
|
|
28412
|
+
|
|
28312
28413
|
// ../../src/execution/tracingExecutor.ts
|
|
28313
28414
|
var TracingExecutor;
|
|
28314
28415
|
var init_tracingExecutor = __esm({
|
|
@@ -28507,7 +28608,12 @@ async function runLoop(opts, now, note, emit, agentSpan) {
|
|
|
28507
28608
|
...input.userName ? { user: input.userName } : {},
|
|
28508
28609
|
...input.from ? { run: input.from.runId } : {}
|
|
28509
28610
|
};
|
|
28510
|
-
emit({
|
|
28611
|
+
emit({
|
|
28612
|
+
type: "input",
|
|
28613
|
+
text: redactSecrets(input.text),
|
|
28614
|
+
messageId: followUpMessageId(input),
|
|
28615
|
+
...Object.keys(source2).length > 0 ? { source: source2 } : {}
|
|
28616
|
+
});
|
|
28511
28617
|
note("follow_up", `follow-up folded in: ${redactSecrets(followUpSnippet(input))}`);
|
|
28512
28618
|
}
|
|
28513
28619
|
const prompt = followUpPrompt(inputs, { superseded });
|
|
@@ -28554,13 +28660,14 @@ ${stagedLine}` : prompt }];
|
|
|
28554
28660
|
}
|
|
28555
28661
|
const ctx = callSpan ? {
|
|
28556
28662
|
...toolContext,
|
|
28663
|
+
callId: tu.id,
|
|
28557
28664
|
span: callSpan,
|
|
28558
28665
|
executor: new TracingExecutor(execTracker, callSpan, opts.backend),
|
|
28559
28666
|
publish: (e) => emit(withSpanId(e, callSpan.id)),
|
|
28560
28667
|
// The GitHub client as a view under this call: its requests and any
|
|
28561
28668
|
// token mint become `github.*` children (docs/reference/specs/tracing.md item 23).
|
|
28562
28669
|
...toolContext.github?.api.withSpan ? { github: { ...toolContext.github, api: toolContext.github.api.withSpan(callSpan) } } : {}
|
|
28563
|
-
} : toolContext;
|
|
28670
|
+
} : { ...toolContext, callId: tu.id };
|
|
28564
28671
|
try {
|
|
28565
28672
|
const output = await untilHardStop(tool.run(tu.input ?? {}, ctx));
|
|
28566
28673
|
const text2 = toolResultText(output);
|
|
@@ -28899,7 +29006,7 @@ var MAX_CONSECUTIVE_INFRA_FAILURES, HardStopError, HARD_STOP_MESSAGE, wrapUpNote
|
|
|
28899
29006
|
var init_runner = __esm({
|
|
28900
29007
|
"../../src/runner.ts"() {
|
|
28901
29008
|
"use strict";
|
|
28902
|
-
|
|
29009
|
+
init_chatMessage();
|
|
28903
29010
|
init_runEvents();
|
|
28904
29011
|
init_threadAdmission();
|
|
28905
29012
|
init_executor();
|
|
@@ -29295,42 +29402,42 @@ async function settle(input, span) {
|
|
|
29295
29402
|
}
|
|
29296
29403
|
verdict = void 0;
|
|
29297
29404
|
reviewHead = current;
|
|
29298
|
-
const
|
|
29405
|
+
const followUpText = rereviewFollowUp({
|
|
29406
|
+
where,
|
|
29407
|
+
reviewed: expected,
|
|
29408
|
+
current,
|
|
29409
|
+
move,
|
|
29410
|
+
before: classified.before,
|
|
29411
|
+
after: classified.after,
|
|
29412
|
+
worktreeMoved
|
|
29413
|
+
});
|
|
29299
29414
|
input.messages.push(
|
|
29300
29415
|
{ role: "assistant", content: [{ type: "text", text: answer }] },
|
|
29301
|
-
{
|
|
29302
|
-
role: "user",
|
|
29303
|
-
content: [
|
|
29304
|
-
{
|
|
29305
|
-
type: "text",
|
|
29306
|
-
text: rereviewFollowUp({
|
|
29307
|
-
where,
|
|
29308
|
-
reviewed: expected,
|
|
29309
|
-
current,
|
|
29310
|
-
move,
|
|
29311
|
-
before: classified.before,
|
|
29312
|
-
after: classified.after,
|
|
29313
|
-
worktreeMoved
|
|
29314
|
-
})
|
|
29315
|
-
}
|
|
29316
|
-
]
|
|
29317
|
-
}
|
|
29416
|
+
{ role: "user", content: [{ type: "text", text: followUpText }] }
|
|
29318
29417
|
);
|
|
29319
|
-
|
|
29418
|
+
const toolContext = {
|
|
29419
|
+
...turn.toolContext,
|
|
29420
|
+
onVerdict: (v) => {
|
|
29421
|
+
verdict = v;
|
|
29422
|
+
}
|
|
29423
|
+
};
|
|
29424
|
+
answer = turn.followUp ? await turn.followUp({
|
|
29425
|
+
text: followUpText,
|
|
29426
|
+
maxTurns: turn.agent.maxTurns,
|
|
29427
|
+
maxMinutes: turn.agent.maxMinutes,
|
|
29428
|
+
toolContext,
|
|
29429
|
+
...span ? { span } : {}
|
|
29430
|
+
}) : await runAgent({
|
|
29320
29431
|
provider: turn.provider,
|
|
29321
29432
|
model: turn.model,
|
|
29322
29433
|
agent: turn.agent,
|
|
29323
29434
|
messages: input.messages,
|
|
29324
|
-
|
|
29435
|
+
// Recomposed at the new head, so the prompt does not contradict the follow-up.
|
|
29436
|
+
system: input.composeSystem({ sha: current, verified: worktreeMoved }),
|
|
29325
29437
|
effort: turn.effort,
|
|
29326
29438
|
...span ? { span } : {},
|
|
29327
29439
|
...turn.backend ? { backend: turn.backend } : {},
|
|
29328
|
-
toolContext
|
|
29329
|
-
...turn.toolContext,
|
|
29330
|
-
onVerdict: (v) => {
|
|
29331
|
-
verdict = v;
|
|
29332
|
-
}
|
|
29333
|
-
},
|
|
29440
|
+
toolContext,
|
|
29334
29441
|
...turn.extraTools && turn.extraTools.length > 0 ? { extraTools: turn.extraTools } : {},
|
|
29335
29442
|
onProgress: turn.onProgress,
|
|
29336
29443
|
onEvent: turn.onEvent,
|
|
@@ -30390,7 +30497,8 @@ async function registerRun(deps, ctx) {
|
|
|
30390
30497
|
trace.bindRun(run2.id, (e) => registry.publish(run2.id, e));
|
|
30391
30498
|
const publishText = (type, text, source3, raw) => {
|
|
30392
30499
|
const redacted = redactSecrets(text);
|
|
30393
|
-
const
|
|
30500
|
+
const body = { text: redacted, ...source3 ? { source: source3 } : {}, at: clock3() };
|
|
30501
|
+
const event = type === "input" ? { type, messageId: messageIdOf(msg, run2.id), ...body } : { type, ...body };
|
|
30394
30502
|
const withRaw = raw !== void 0 ? { ...event, raw: redactSecrets(raw) } : event;
|
|
30395
30503
|
registry.publish(run2.id, utf8ByteLength(JSON.stringify(withRaw)) <= MAX_EVENT_BYTES ? withRaw : event);
|
|
30396
30504
|
console.log(`[event] ${msg.threadKey} type=${type} bytes=${utf8ByteLength(redacted)}`);
|
|
@@ -30689,7 +30797,7 @@ var init_provision = __esm({
|
|
|
30689
30797
|
init_resident();
|
|
30690
30798
|
init_memory();
|
|
30691
30799
|
init_runBearers();
|
|
30692
|
-
|
|
30800
|
+
init_provider();
|
|
30693
30801
|
init_skills2();
|
|
30694
30802
|
init_source();
|
|
30695
30803
|
init_configAwareness();
|
|
@@ -30701,6 +30809,7 @@ var init_provision = __esm({
|
|
|
30701
30809
|
init_inboxMessage();
|
|
30702
30810
|
init_statusCardFrame();
|
|
30703
30811
|
init_statusCoalescer();
|
|
30812
|
+
init_types4();
|
|
30704
30813
|
init_record();
|
|
30705
30814
|
init_reply();
|
|
30706
30815
|
init_messages();
|
|
@@ -31665,6 +31774,7 @@ var init_bridge2 = __esm({
|
|
|
31665
31774
|
PiBridge = class {
|
|
31666
31775
|
constructor(deps) {
|
|
31667
31776
|
this.deps = deps;
|
|
31777
|
+
this.agentSpan = deps.agentSpan;
|
|
31668
31778
|
}
|
|
31669
31779
|
deps;
|
|
31670
31780
|
/** Model turns that did work, the guard's count — the loop's rule: a turn whose every call is `update_status` is bookkeeping. */
|
|
@@ -31686,6 +31796,24 @@ var init_bridge2 = __esm({
|
|
|
31686
31796
|
* were vetted by the bot generation that died, and this one never heard. */
|
|
31687
31797
|
judgeGate = true;
|
|
31688
31798
|
summarizationRetries = 0;
|
|
31799
|
+
/** The span a starting call's `tool.<name>` opens under: the run's
|
|
31800
|
+
* `run.agent` for the loop, a follow-up turn's own for its duration
|
|
31801
|
+
* (`under`; harness-pi item 14). */
|
|
31802
|
+
agentSpan;
|
|
31803
|
+
/** From here on, new tool spans hang under `span` — a follow-up turn's
|
|
31804
|
+
* `run.agent`, opened under the caller's span the way the native loop's
|
|
31805
|
+
* would be (tracing.md item 17); `undefined` opens none. */
|
|
31806
|
+
under(span) {
|
|
31807
|
+
this.agentSpan = span;
|
|
31808
|
+
}
|
|
31809
|
+
/** A new prompt is about to be sent on the session (harness-pi item 14): the
|
|
31810
|
+
* reply pi settled the last loop on is not this turn's answer, so the
|
|
31811
|
+
* answer state starts over — what pi settles on next is the turn's. */
|
|
31812
|
+
newPrompt() {
|
|
31813
|
+
this.answerText = void 0;
|
|
31814
|
+
this.heldAnswer = void 0;
|
|
31815
|
+
this.assistantStartedAt = void 0;
|
|
31816
|
+
}
|
|
31689
31817
|
/** The gate saw this call: the extension's `tool_call` hook asked the bot
|
|
31690
31818
|
* for it, whatever the answer. Called from the authorize route, before pi
|
|
31691
31819
|
* runs the tool — so it always precedes the call's `tool_execution_end`. */
|
|
@@ -31794,7 +31922,7 @@ var init_bridge2 = __esm({
|
|
|
31794
31922
|
onToolStart(event) {
|
|
31795
31923
|
const tool = str5(event.toolName);
|
|
31796
31924
|
const callId = str5(event.toolCallId);
|
|
31797
|
-
const span = this.
|
|
31925
|
+
const span = this.agentSpan?.start(`tool.${tool}`);
|
|
31798
31926
|
this.openTools.set(callId, { span, tool, judged: this.judgeGate });
|
|
31799
31927
|
this.toolCalls++;
|
|
31800
31928
|
const input = isRecord3(event.args) ? event.args : void 0;
|
|
@@ -32317,7 +32445,7 @@ function ledgerTailOf(resume) {
|
|
|
32317
32445
|
function settlementAnswer(s) {
|
|
32318
32446
|
return { content: [{ type: "text", text: settlementText(s) }], isError: true };
|
|
32319
32447
|
}
|
|
32320
|
-
async function
|
|
32448
|
+
async function runPiHarnessOpen(deps, run2) {
|
|
32321
32449
|
const { container, clock: clock3 } = deps;
|
|
32322
32450
|
const now = () => clock3();
|
|
32323
32451
|
const agentSpan = run2.span?.start("run.agent");
|
|
@@ -32412,6 +32540,26 @@ async function runPiHarness(deps, run2) {
|
|
|
32412
32540
|
save();
|
|
32413
32541
|
};
|
|
32414
32542
|
let catchingUp = false;
|
|
32543
|
+
const clearWriteUp = () => {
|
|
32544
|
+
writeUp = void 0;
|
|
32545
|
+
writeUpAt = void 0;
|
|
32546
|
+
};
|
|
32547
|
+
let followUps = 0;
|
|
32548
|
+
let sessionEnded = false;
|
|
32549
|
+
const end = async () => {
|
|
32550
|
+
if (sessionEnded) return;
|
|
32551
|
+
sessionEnded = true;
|
|
32552
|
+
transport?.close();
|
|
32553
|
+
bridge.closeOpenSpans(
|
|
32554
|
+
hardStopped ? "the run was hard-stopped" : bypass ? "the run was stopped: a tool call bypassed the gate" : "the run ended"
|
|
32555
|
+
);
|
|
32556
|
+
save();
|
|
32557
|
+
forget();
|
|
32558
|
+
if (pid !== void 0) await container.kill(pid).catch(() => {
|
|
32559
|
+
});
|
|
32560
|
+
if (paths !== void 0) await container.remove(paths).catch(() => {
|
|
32561
|
+
});
|
|
32562
|
+
};
|
|
32415
32563
|
try {
|
|
32416
32564
|
const recorded = run2.resume?.facts;
|
|
32417
32565
|
let reattached = false;
|
|
@@ -32551,7 +32699,12 @@ async function runPiHarness(deps, run2) {
|
|
|
32551
32699
|
...input.userName ? { user: input.userName } : {},
|
|
32552
32700
|
...input.from ? { run: input.from.runId } : {}
|
|
32553
32701
|
};
|
|
32554
|
-
emit({
|
|
32702
|
+
emit({
|
|
32703
|
+
type: "input",
|
|
32704
|
+
text: redactSecrets(input.text),
|
|
32705
|
+
messageId: followUpMessageId(input),
|
|
32706
|
+
...Object.keys(source2).length > 0 ? { source: source2 } : {}
|
|
32707
|
+
});
|
|
32555
32708
|
note("follow_up", `follow-up folded in: ${redactSecrets(followUpSnippet(input))}`);
|
|
32556
32709
|
}
|
|
32557
32710
|
const images = inputs.flatMap(
|
|
@@ -32689,33 +32842,137 @@ ${stagedLine}` : prompt,
|
|
|
32689
32842
|
check();
|
|
32690
32843
|
if (hardStopped) break;
|
|
32691
32844
|
}
|
|
32845
|
+
let answer;
|
|
32692
32846
|
if (hardStopped) {
|
|
32693
32847
|
note("stopped", hardStopNote(), "hard");
|
|
32694
|
-
|
|
32695
|
-
}
|
|
32696
|
-
|
|
32697
|
-
|
|
32698
|
-
|
|
32699
|
-
|
|
32700
|
-
|
|
32701
|
-
|
|
32702
|
-
|
|
32703
|
-
|
|
32704
|
-
|
|
32705
|
-
|
|
32706
|
-
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
|
|
32711
|
-
|
|
32712
|
-
|
|
32713
|
-
|
|
32714
|
-
|
|
32715
|
-
|
|
32716
|
-
|
|
32717
|
-
|
|
32718
|
-
|
|
32848
|
+
answer = HARD_STOP_MESSAGE;
|
|
32849
|
+
} else {
|
|
32850
|
+
if (bypass) throw bypass;
|
|
32851
|
+
if (!settled) {
|
|
32852
|
+
const tail2 = await container.tail(paths.errLog, 2e3);
|
|
32853
|
+
throw new Error(`pi exited before the run settled${tail2.trim() ? `: ${redactAndCap(tail2.trim(), 400)}` : ""}`);
|
|
32854
|
+
}
|
|
32855
|
+
if (providerError !== void 0) throw new Error(`the model call failed: ${providerError}`);
|
|
32856
|
+
const text = bridge.answer() ?? "";
|
|
32857
|
+
answer = writeUp?.kind === "time" ? timeBudgetAnswer(text, run2.agent.maxMinutes) : writeUp?.kind === "turns" ? turnGuardAnswer(text, writeUp.pace) : writeUp?.kind === "soft" || stopMode === "soft" ? softStopAnswer(text) : text || "_(no response)_";
|
|
32858
|
+
}
|
|
32859
|
+
agentSpan?.end(hardStopped ? "error" : "ok");
|
|
32860
|
+
const rpc = transport;
|
|
32861
|
+
const root = paths;
|
|
32862
|
+
const followUp = async (input) => {
|
|
32863
|
+
if (sessionEnded) throw new Error("the pi session has ended: no follow-up turn can run on it");
|
|
32864
|
+
const turnSpan = input.span?.start("run.agent");
|
|
32865
|
+
if (turnSpan) deps.bearers?.reparent(run2.runId, turnSpan);
|
|
32866
|
+
bridge.under(turnSpan);
|
|
32867
|
+
bridge.newPrompt();
|
|
32868
|
+
const runContext = live.toolContext;
|
|
32869
|
+
live.toolContext = input.toolContext;
|
|
32870
|
+
const turnDeadline = now() + input.maxMinutes * 6e4;
|
|
32871
|
+
input.toolContext.remainingMs = () => turnDeadline - now();
|
|
32872
|
+
const turnsBefore = bridge.turns;
|
|
32873
|
+
clearWriteUp();
|
|
32874
|
+
const id = `${ids.prompt}:follow-up:${++followUps}`;
|
|
32875
|
+
let turnSettled = false;
|
|
32876
|
+
let turnError;
|
|
32877
|
+
let turnFailed = false;
|
|
32878
|
+
const turnCheck = () => {
|
|
32879
|
+
if (run2.control?.requested === "hard") {
|
|
32880
|
+
if (!hardStopped) {
|
|
32881
|
+
hardStopped = true;
|
|
32882
|
+
rpc.send({ type: "abort" });
|
|
32883
|
+
}
|
|
32884
|
+
return;
|
|
32885
|
+
}
|
|
32886
|
+
if (writeUp) {
|
|
32887
|
+
if (writeUpAt !== void 0 && now() - writeUpAt >= (deps.finaleTimeoutMs ?? FINALE_TIMEOUT_MS2)) {
|
|
32888
|
+
writeUpAt = void 0;
|
|
32889
|
+
run2.onProgress?.("finale timed out \u2014 closing the turn without a write-up");
|
|
32890
|
+
rpc.send({ type: "abort" });
|
|
32891
|
+
}
|
|
32892
|
+
return;
|
|
32893
|
+
}
|
|
32894
|
+
if (run2.control?.requested === "soft") {
|
|
32895
|
+
note("stopped", softStopNote(), "soft");
|
|
32896
|
+
startWriteUp({ kind: "soft" }, SOFT_STOP_INSTRUCTION);
|
|
32897
|
+
return;
|
|
32898
|
+
}
|
|
32899
|
+
if (now() >= turnDeadline) {
|
|
32900
|
+
note("time_budget_exhausted", timeBudgetNote());
|
|
32901
|
+
startWriteUp({ kind: "time" }, timeBudgetInstruction());
|
|
32902
|
+
return;
|
|
32903
|
+
}
|
|
32904
|
+
const turns = bridge.turns - turnsBefore;
|
|
32905
|
+
if (turns >= input.maxTurns) {
|
|
32906
|
+
const pace = turnGuardPace(turns, input.maxMinutes * 6e4 - (turnDeadline - now()));
|
|
32907
|
+
note("turn_budget_exhausted", turnGuardNote(pace));
|
|
32908
|
+
startWriteUp({ kind: "turns", pace }, turnGuardInstruction(pace));
|
|
32909
|
+
}
|
|
32910
|
+
};
|
|
32911
|
+
try {
|
|
32912
|
+
rpc.send({ id, type: "prompt", message: input.text });
|
|
32913
|
+
turnCheck();
|
|
32914
|
+
for (; ; ) {
|
|
32915
|
+
pending2 ??= iterator.next();
|
|
32916
|
+
const tick = deps.sleep(deps.tickMs ?? 1e3).then(() => "tick");
|
|
32917
|
+
const next = await Promise.race([pending2, tick]);
|
|
32918
|
+
if (next === "tick") {
|
|
32919
|
+
turnCheck();
|
|
32920
|
+
if (hardStopped) break;
|
|
32921
|
+
continue;
|
|
32922
|
+
}
|
|
32923
|
+
pending2 = void 0;
|
|
32924
|
+
if (next.done) break;
|
|
32925
|
+
const event = parsePiLine(next.value);
|
|
32926
|
+
if (!event) continue;
|
|
32927
|
+
const obs = bridge.observe(event);
|
|
32928
|
+
for (const reply of obs.replies) rpc.send(reply);
|
|
32929
|
+
if (obs.gateBypassed) {
|
|
32930
|
+
bypass = new GateBypassed(obs.gateBypassed.tool, obs.gateBypassed.callId);
|
|
32931
|
+
note("harness_error", `${bypass.message} \u2014 the turn is stopped`);
|
|
32932
|
+
rpc.send({ type: "abort" });
|
|
32933
|
+
break;
|
|
32934
|
+
}
|
|
32935
|
+
if (obs.response?.id === id && obs.response.success === false)
|
|
32936
|
+
throw new PromptRefused(String(obs.response.error ?? "no reason"));
|
|
32937
|
+
if (obs.providerError !== void 0) turnError = obs.providerError;
|
|
32938
|
+
if (obs.settled) {
|
|
32939
|
+
turnSettled = true;
|
|
32940
|
+
break;
|
|
32941
|
+
}
|
|
32942
|
+
turnCheck();
|
|
32943
|
+
if (hardStopped) break;
|
|
32944
|
+
}
|
|
32945
|
+
if (hardStopped) {
|
|
32946
|
+
note("stopped", hardStopNote(), "hard");
|
|
32947
|
+
return HARD_STOP_MESSAGE;
|
|
32948
|
+
}
|
|
32949
|
+
if (bypass) throw bypass;
|
|
32950
|
+
if (!turnSettled) {
|
|
32951
|
+
const tail2 = await container.tail(root.errLog, 2e3);
|
|
32952
|
+
throw new Error(
|
|
32953
|
+
`pi exited before the turn settled${tail2.trim() ? `: ${redactAndCap(tail2.trim(), 400)}` : ""}`
|
|
32954
|
+
);
|
|
32955
|
+
}
|
|
32956
|
+
if (turnError !== void 0) throw new Error(`the model call failed: ${turnError}`);
|
|
32957
|
+
const text = bridge.answer() ?? "";
|
|
32958
|
+
if (writeUp?.kind === "time") return timeBudgetAnswer(text, input.maxMinutes);
|
|
32959
|
+
if (writeUp?.kind === "turns") return turnGuardAnswer(text, writeUp.pace);
|
|
32960
|
+
if (writeUp?.kind === "soft") return softStopAnswer(text);
|
|
32961
|
+
return text || "_(no response)_";
|
|
32962
|
+
} catch (err2) {
|
|
32963
|
+
turnFailed = true;
|
|
32964
|
+
throw err2;
|
|
32965
|
+
} finally {
|
|
32966
|
+
live.toolContext = runContext;
|
|
32967
|
+
bridge.under(void 0);
|
|
32968
|
+
turnSpan?.end(hardStopped || bypass || turnFailed ? "error" : "ok");
|
|
32969
|
+
}
|
|
32970
|
+
};
|
|
32971
|
+
return { answer, followUp, end };
|
|
32972
|
+
} catch (err2) {
|
|
32973
|
+
agentSpan?.end("error");
|
|
32974
|
+
await end();
|
|
32975
|
+
throw err2;
|
|
32719
32976
|
}
|
|
32720
32977
|
}
|
|
32721
32978
|
var NATIVE_WORKSPACE_TOOLS, FINALE_TIMEOUT_MS2, CALL_SEEN_WAIT_MS, CALL_SEEN_TICK_MS, CONTINUE_PROMPT, PromptRefused, GateBypassed;
|
|
@@ -33346,37 +33603,49 @@ async function runDescriptionTurn(input) {
|
|
|
33346
33603
|
turn.onEvent({ type: "run_note", kind: "description_turn", summary, at: systemClock() });
|
|
33347
33604
|
turn.onProgress(`re-evaluating the description of ${t.repo}#${t.pr.number} at ${t.headSha.slice(0, 7)}`);
|
|
33348
33605
|
let submitted;
|
|
33606
|
+
const followUpText = descriptionFollowUp(t);
|
|
33349
33607
|
input.messages.push(
|
|
33350
33608
|
{ role: "assistant", content: [{ type: "text", text: input.answer }] },
|
|
33351
|
-
{ role: "user", content: [{ type: "text", text:
|
|
33609
|
+
{ role: "user", content: [{ type: "text", text: followUpText }] }
|
|
33352
33610
|
);
|
|
33353
33611
|
const agent = {
|
|
33354
33612
|
...turn.agent,
|
|
33355
33613
|
maxTurns: Math.min(turn.agent.maxTurns, DESCRIPTION_TURN_MAX_TURNS),
|
|
33356
33614
|
maxMinutes: Math.min(turn.agent.maxMinutes, DESCRIPTION_TURN_MAX_MINUTES)
|
|
33357
33615
|
};
|
|
33616
|
+
const toolContext = {
|
|
33617
|
+
...turn.toolContext,
|
|
33618
|
+
onPrDescription: (d) => {
|
|
33619
|
+
submitted = d;
|
|
33620
|
+
turn.toolContext.onPrDescription?.(d);
|
|
33621
|
+
}
|
|
33622
|
+
};
|
|
33358
33623
|
try {
|
|
33359
|
-
|
|
33360
|
-
|
|
33361
|
-
|
|
33362
|
-
|
|
33363
|
-
|
|
33364
|
-
|
|
33365
|
-
|
|
33366
|
-
|
|
33367
|
-
|
|
33368
|
-
|
|
33369
|
-
|
|
33370
|
-
|
|
33371
|
-
|
|
33372
|
-
|
|
33373
|
-
}
|
|
33374
|
-
|
|
33375
|
-
|
|
33376
|
-
|
|
33377
|
-
|
|
33378
|
-
|
|
33379
|
-
|
|
33624
|
+
if (turn.followUp) {
|
|
33625
|
+
await turn.followUp({
|
|
33626
|
+
text: followUpText,
|
|
33627
|
+
maxTurns: agent.maxTurns,
|
|
33628
|
+
maxMinutes: agent.maxMinutes,
|
|
33629
|
+
toolContext,
|
|
33630
|
+
...input.span ? { span: input.span } : {}
|
|
33631
|
+
});
|
|
33632
|
+
} else {
|
|
33633
|
+
await runAgent({
|
|
33634
|
+
provider: turn.provider,
|
|
33635
|
+
model: turn.model,
|
|
33636
|
+
agent,
|
|
33637
|
+
messages: input.messages,
|
|
33638
|
+
...input.system !== void 0 ? { system: input.system } : {},
|
|
33639
|
+
...turn.effort !== void 0 ? { effort: turn.effort } : {},
|
|
33640
|
+
...input.span ? { span: input.span } : {},
|
|
33641
|
+
...turn.backend ? { backend: turn.backend } : {},
|
|
33642
|
+
toolContext,
|
|
33643
|
+
...turn.extraTools && turn.extraTools.length > 0 ? { extraTools: turn.extraTools } : {},
|
|
33644
|
+
onProgress: turn.onProgress,
|
|
33645
|
+
onEvent: turn.onEvent,
|
|
33646
|
+
control: turn.control
|
|
33647
|
+
});
|
|
33648
|
+
}
|
|
33380
33649
|
} catch (err2) {
|
|
33381
33650
|
console.error(`[description-turn] ${logKey} turn failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
33382
33651
|
}
|
|
@@ -33574,6 +33843,7 @@ async function runLoop2(deps, ctx) {
|
|
|
33574
33843
|
let carried;
|
|
33575
33844
|
let runFailed = false;
|
|
33576
33845
|
let runDiagnosis;
|
|
33846
|
+
let piSession;
|
|
33577
33847
|
const releaseWorkspace = (span) => round2.release({ hardStopped: run2.control.requested === "hard", ...span ? { span } : {} });
|
|
33578
33848
|
const attachFile = io.attachFile?.bind(io);
|
|
33579
33849
|
const uploadTicket = io.uploadTicket?.bind(io);
|
|
@@ -33676,7 +33946,7 @@ async function runLoop2(deps, ctx) {
|
|
|
33676
33946
|
)
|
|
33677
33947
|
)
|
|
33678
33948
|
];
|
|
33679
|
-
|
|
33949
|
+
piSession = await runPiHarnessOpen(
|
|
33680
33950
|
{
|
|
33681
33951
|
container: deps.harness.containerFor?.(executor, profile.machine) ?? piContainerFor(executor, profile.machine),
|
|
33682
33952
|
bearer: ctx.bearer,
|
|
@@ -33728,6 +33998,7 @@ async function runLoop2(deps, ctx) {
|
|
|
33728
33998
|
} : {}
|
|
33729
33999
|
}
|
|
33730
34000
|
);
|
|
34001
|
+
answer = piSession.answer;
|
|
33731
34002
|
} else {
|
|
33732
34003
|
answer = await runAgent({
|
|
33733
34004
|
provider,
|
|
@@ -33784,7 +34055,8 @@ async function runLoop2(deps, ctx) {
|
|
|
33784
34055
|
onProgress,
|
|
33785
34056
|
onEvent,
|
|
33786
34057
|
control: run2.control,
|
|
33787
|
-
...round2.selection.backend ? { backend: round2.selection.backend } : {}
|
|
34058
|
+
...round2.selection.backend ? { backend: round2.selection.backend } : {},
|
|
34059
|
+
...piSession ? { followUp: piSession.followUp } : {}
|
|
33788
34060
|
},
|
|
33789
34061
|
fetchPrHead: deps.fetchPrHead ?? currentPrHeadSha,
|
|
33790
34062
|
fetchPrCommits: deps.fetchPrCommits ?? prCommitsSince,
|
|
@@ -33864,7 +34136,8 @@ async function runLoop2(deps, ctx) {
|
|
|
33864
34136
|
onProgress,
|
|
33865
34137
|
onEvent,
|
|
33866
34138
|
control: run2.control,
|
|
33867
|
-
...round2.selection.backend ? { backend: round2.selection.backend } : {}
|
|
34139
|
+
...round2.selection.backend ? { backend: round2.selection.backend } : {},
|
|
34140
|
+
...piSession ? { followUp: piSession.followUp } : {}
|
|
33868
34141
|
},
|
|
33869
34142
|
logKey: msg.threadKey
|
|
33870
34143
|
})
|
|
@@ -33872,6 +34145,7 @@ async function runLoop2(deps, ctx) {
|
|
|
33872
34145
|
if (!run2.control.hardSignal.aborted) await observeWorkspaceNow();
|
|
33873
34146
|
}
|
|
33874
34147
|
}
|
|
34148
|
+
await piSession?.end();
|
|
33875
34149
|
if (prDescription) {
|
|
33876
34150
|
registry.publish(run2.id, {
|
|
33877
34151
|
type: "pr_description",
|
|
@@ -33932,6 +34206,7 @@ async function runLoop2(deps, ctx) {
|
|
|
33932
34206
|
);
|
|
33933
34207
|
} catch (err2) {
|
|
33934
34208
|
runFailed = true;
|
|
34209
|
+
await piSession?.end();
|
|
33935
34210
|
await root.span("post.workspace_release", (span) => releaseWorkspace(span));
|
|
33936
34211
|
throw err2;
|
|
33937
34212
|
} finally {
|
|
@@ -34022,7 +34297,7 @@ var init_runLoop = __esm({
|
|
|
34022
34297
|
"use strict";
|
|
34023
34298
|
init_profile();
|
|
34024
34299
|
init_runner();
|
|
34025
|
-
|
|
34300
|
+
init_provider();
|
|
34026
34301
|
init_workspace();
|
|
34027
34302
|
init_select();
|
|
34028
34303
|
init_botHostContainer();
|
|
@@ -35099,7 +35374,11 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
35099
35374
|
io.runStarted?.({ id: run2.id });
|
|
35100
35375
|
const publishText = (type, text, source3) => {
|
|
35101
35376
|
const redacted = redactSecrets(text);
|
|
35102
|
-
|
|
35377
|
+
const body = { text: redacted, ...source3 ? { source: source3 } : {}, at: clock3() };
|
|
35378
|
+
registry.publish(
|
|
35379
|
+
run2.id,
|
|
35380
|
+
type === "input" ? { type, messageId: messageIdOf(msg, run2.id), ...body } : { type, ...body }
|
|
35381
|
+
);
|
|
35103
35382
|
console.log(`[event] ${msg.threadKey} type=${type} bytes=${utf8ByteLength(redacted)}`);
|
|
35104
35383
|
};
|
|
35105
35384
|
const humanize = isMrkdwnChannel(msg.channelId);
|
|
@@ -35297,6 +35576,7 @@ var init_ship = __esm({
|
|
|
35297
35576
|
init_run2();
|
|
35298
35577
|
init_runRegistry();
|
|
35299
35578
|
init_statusCardFrame();
|
|
35579
|
+
init_types4();
|
|
35300
35580
|
}
|
|
35301
35581
|
});
|
|
35302
35582
|
|
|
@@ -35571,6 +35851,16 @@ function sessionSeed(input) {
|
|
|
35571
35851
|
const summary = compactions.length ? compactions[compactions.length - 1].entry.summary : void 0;
|
|
35572
35852
|
const logFrom = start3 < 0 ? from + transcript.turns : from + start3 + compactions.length;
|
|
35573
35853
|
const kept = start3 < 0 ? [] : transcript.messages.slice(start3).map(withoutThinking);
|
|
35854
|
+
if (kept.length > 0 && kept[0].role === "user") {
|
|
35855
|
+
const first = kept[0];
|
|
35856
|
+
const orphans = first.content.filter((p) => p.type === "tool_result").length;
|
|
35857
|
+
if (orphans > 0) {
|
|
35858
|
+
kept[0] = { ...first, content: first.content.filter((p) => p.type !== "tool_result") };
|
|
35859
|
+
notes.push(
|
|
35860
|
+
orphans === 1 ? "session seed: one tool result answering a call before the cut was dropped from the tail's first turn" : `session seed: ${orphans} tool results answering calls before the cut were dropped from the tail's first turn`
|
|
35861
|
+
);
|
|
35862
|
+
}
|
|
35863
|
+
}
|
|
35574
35864
|
if (kept.length === 0) {
|
|
35575
35865
|
notes.push(
|
|
35576
35866
|
transcript.turns === 0 ? "session seed: the log's newest turn alone is over the seed budget \u2014 the run starts from the request" : "session seed: no user text turn in the tail within the seed budget \u2014 the run starts from the request"
|
|
@@ -36036,7 +36326,11 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
36036
36326
|
const earlierStaged = threadAssets && agent.machine !== "none" ? threadAssets.then(
|
|
36037
36327
|
(assets) => stageThreadArtifacts(
|
|
36038
36328
|
assets.filter((a) => a.direction === "in"),
|
|
36039
|
-
{
|
|
36329
|
+
{
|
|
36330
|
+
nextIndex: nextStagedIndex,
|
|
36331
|
+
messageId: messageIdOf(msg, runId3),
|
|
36332
|
+
publish: (e) => registry.publish(runId3, e)
|
|
36333
|
+
}
|
|
36040
36334
|
)
|
|
36041
36335
|
) : void 0;
|
|
36042
36336
|
const reservation = await reserveRun(deps, {
|
|
@@ -36446,6 +36740,7 @@ var init_dispatcher = __esm({
|
|
|
36446
36740
|
init_runsService();
|
|
36447
36741
|
init_runRegistry();
|
|
36448
36742
|
init_runEnding();
|
|
36743
|
+
init_types4();
|
|
36449
36744
|
activeRuns = 0;
|
|
36450
36745
|
}
|
|
36451
36746
|
});
|
|
@@ -37583,7 +37878,7 @@ var OpenAICompatProvider;
|
|
|
37583
37878
|
var init_openaiCompat = __esm({
|
|
37584
37879
|
"../../src/providers/openaiCompat.ts"() {
|
|
37585
37880
|
"use strict";
|
|
37586
|
-
|
|
37881
|
+
init_chatMessage();
|
|
37587
37882
|
init_secrets2();
|
|
37588
37883
|
OpenAICompatProvider = class {
|
|
37589
37884
|
name;
|
|
@@ -37827,7 +38122,7 @@ var ANTHROPIC_BASE_URL, ZERO_RATES, ZERO_USAGE, CONTEXT_WINDOW, PiAiProviders, P
|
|
|
37827
38122
|
var init_piAi = __esm({
|
|
37828
38123
|
"../../src/core/harness/piAi.ts"() {
|
|
37829
38124
|
"use strict";
|
|
37830
|
-
|
|
38125
|
+
init_provider();
|
|
37831
38126
|
init_secrets2();
|
|
37832
38127
|
init_clock();
|
|
37833
38128
|
ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
@@ -38114,7 +38409,7 @@ var init_mcp2 = __esm({
|
|
|
38114
38409
|
init_sealed();
|
|
38115
38410
|
init_secretStore();
|
|
38116
38411
|
init_service();
|
|
38117
|
-
|
|
38412
|
+
init_types2();
|
|
38118
38413
|
init_registry2();
|
|
38119
38414
|
init_client();
|
|
38120
38415
|
init_fake();
|
|
@@ -39232,6 +39527,7 @@ async function receiveSlackMessage(client, ev, span, policy) {
|
|
|
39232
39527
|
userId: `${PLATFORM}:${ev.user}`,
|
|
39233
39528
|
threadKey: `${PLATFORM}:${ev.channel}:${ev.threadTs}`,
|
|
39234
39529
|
text: ev.text + note,
|
|
39530
|
+
messageId: ev.ts,
|
|
39235
39531
|
channelName,
|
|
39236
39532
|
userName,
|
|
39237
39533
|
sourceUrl: team ? slackPermalink(team, ev.channel, ev.ts, ev.threadTs) : void 0,
|
|
@@ -43022,7 +43318,7 @@ var init_modelProxy = __esm({
|
|
|
43022
43318
|
"use strict";
|
|
43023
43319
|
init_anthropic();
|
|
43024
43320
|
init_openaiCompat();
|
|
43025
|
-
|
|
43321
|
+
init_provider();
|
|
43026
43322
|
init_http();
|
|
43027
43323
|
ANTHROPIC_MESSAGES_PATH = "/v1/messages";
|
|
43028
43324
|
OPENAI_CHAT_COMPLETIONS_PATH = "/v1/chat/completions";
|
|
@@ -43351,7 +43647,9 @@ async function runRelayedTool(harness, ask, opts = {}) {
|
|
|
43351
43647
|
...base.github?.api.withSpan ? { github: { ...base.github, api: base.github.api.withSpan(span) } } : {}
|
|
43352
43648
|
} : { ...base, publish: (e) => harness.emit(e) },
|
|
43353
43649
|
...opts.signal && !base.signal ? { signal: opts.signal } : {},
|
|
43354
|
-
...readConversation && harness.callSeen ? { conversation: () => harness.callSeen(ask.toolCallId).then(() => readConversation()) } : {}
|
|
43650
|
+
...readConversation && harness.callSeen ? { conversation: () => harness.callSeen(ask.toolCallId).then(() => readConversation()) } : {},
|
|
43651
|
+
// After the spreads, so the call's own id can never be shadowed by one on the base context.
|
|
43652
|
+
callId: ask.toolCallId
|
|
43355
43653
|
};
|
|
43356
43654
|
try {
|
|
43357
43655
|
const input = typeof ask.input === "object" && ask.input !== null ? ask.input : {};
|
|
@@ -43385,7 +43683,7 @@ var init_relay = __esm({
|
|
|
43385
43683
|
"../../src/core/harness/pi/relay.ts"() {
|
|
43386
43684
|
"use strict";
|
|
43387
43685
|
init_tracingExecutor();
|
|
43388
|
-
|
|
43686
|
+
init_chatMessage();
|
|
43389
43687
|
init_awaitChildren();
|
|
43390
43688
|
init_runEvents();
|
|
43391
43689
|
init_toolRules();
|