@rallycry/conveyor-agent 11.0.23 → 11.0.25
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/{boot-S73ZNWBJ.js → boot-65PTSADA.js} +2 -2
- package/dist/{chunk-LBB7DXOC.js → chunk-7WO6NTOS.js} +1 -1
- package/dist/{chunk-I7QYTA2V.js → chunk-BZZ4B37Q.js} +34 -6
- package/dist/{chunk-EIHZRSYI.js → chunk-EITUTXXJ.js} +1 -1
- package/dist/{chunk-XJSW42TM.js → chunk-RLROQPPT.js} +334 -36
- package/dist/{chunk-W5INK3NE.js → chunk-WCJ6HRVU.js} +3 -0
- package/dist/cli.js +7 -7
- package/dist/index.d.ts +22 -2
- package/dist/index.js +3 -3
- package/dist/{serve-boot-ODH57W6I.js → serve-boot-T7MNWASE.js} +2 -2
- package/dist/{server-L5CQ5EJB.js → server-E6IYHSMN.js} +2 -2
- package/package.json +3 -3
- package/skills/conveyor-plan/SKILL.md +12 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startWorkbenchServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EITUTXXJ.js";
|
|
4
4
|
import {
|
|
5
5
|
TOMBSTONE_MESSAGE,
|
|
6
6
|
isLegacyEntrypointLaunch
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
refreshSkillsAfterCheckout,
|
|
13
13
|
runPreReadyBinds
|
|
14
14
|
} from "./chunk-37J5MMQT.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-WCJ6HRVU.js";
|
|
16
16
|
import {
|
|
17
17
|
GitPrepJob,
|
|
18
18
|
defaultGit,
|
|
@@ -1446,14 +1446,14 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
|
|
|
1446
1446
|
}
|
|
1447
1447
|
};
|
|
1448
1448
|
|
|
1449
|
-
// ../shared/dist/chunk-
|
|
1449
|
+
// ../shared/dist/chunk-U3HGG5NX.js
|
|
1450
1450
|
import { z } from "zod";
|
|
1451
1451
|
var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
|
|
1452
|
-
var DEFAULT_OPUS_MODEL = "claude-opus-5";
|
|
1452
|
+
var DEFAULT_OPUS_MODEL = "claude-opus-5-5";
|
|
1453
1453
|
var DEFAULT_HAIKU_MODEL = "claude-haiku-4-5-20251001";
|
|
1454
1454
|
var FABLE_MODEL = "claude-fable-5-1";
|
|
1455
1455
|
var PREVIOUS_SONNET_MODEL = "claude-sonnet-4-6";
|
|
1456
|
-
var PREVIOUS_OPUS_MODEL = "claude-opus-
|
|
1456
|
+
var PREVIOUS_OPUS_MODEL = "claude-opus-5";
|
|
1457
1457
|
var PTY_STREAM_PORT_BASE = 7420;
|
|
1458
1458
|
var PTY_STREAM_PORT_ATTEMPTS = 8;
|
|
1459
1459
|
function encodePtyStreamFrame(frame) {
|
|
@@ -1806,6 +1806,13 @@ var TurnEndToolCallSchema = z22.object({
|
|
|
1806
1806
|
output: z22.string().optional(),
|
|
1807
1807
|
timestamp: z22.string().optional()
|
|
1808
1808
|
}).passthrough();
|
|
1809
|
+
var AgentTurnUsageSchema = z22.object({
|
|
1810
|
+
inputTokens: z22.number(),
|
|
1811
|
+
outputTokens: z22.number(),
|
|
1812
|
+
cacheReadInputTokens: z22.number(),
|
|
1813
|
+
cacheCreationInputTokens: z22.number(),
|
|
1814
|
+
totalTokens: z22.number()
|
|
1815
|
+
});
|
|
1809
1816
|
var KnownAgentEventSchema = z22.discriminatedUnion("type", [
|
|
1810
1817
|
// ── Lifecycle / connection ────────────────────────────────────────────
|
|
1811
1818
|
z22.object({
|
|
@@ -1845,7 +1852,11 @@ var KnownAgentEventSchema = z22.discriminatedUnion("type", [
|
|
|
1845
1852
|
z22.object({
|
|
1846
1853
|
type: z22.literal("completed"),
|
|
1847
1854
|
summary: z22.string().optional(),
|
|
1848
|
-
durationMs: z22.number().optional()
|
|
1855
|
+
durationMs: z22.number().optional(),
|
|
1856
|
+
/** Tokens this turn consumed, summed from the transcript's assistant
|
|
1857
|
+
* records. Optional: an older agent build sends none, and the server
|
|
1858
|
+
* then records nothing rather than zeroing the session's totals. */
|
|
1859
|
+
usage: AgentTurnUsageSchema.optional()
|
|
1849
1860
|
}).passthrough(),
|
|
1850
1861
|
z22.object({ type: z22.literal("error"), message: z22.string() }).passthrough(),
|
|
1851
1862
|
z22.object({ type: z22.literal("agent_typing_start") }).passthrough(),
|
|
@@ -3296,6 +3307,10 @@ var CancelDecisionRequestSchema = z10.object({
|
|
|
3296
3307
|
/** Why it is being dropped. Posted to the decision thread. */
|
|
3297
3308
|
reason: z10.string().max(DECISION_RESOLUTION_MAX).optional()
|
|
3298
3309
|
});
|
|
3310
|
+
var BulkDecisionIdsRequestSchema = z10.object({
|
|
3311
|
+
projectId: z10.string().cuid("Invalid project ID"),
|
|
3312
|
+
decisionIds: z10.array(z10.string().cuid()).min(1).max(200)
|
|
3313
|
+
});
|
|
3299
3314
|
var ReopenDecisionRequestSchema = z10.object({
|
|
3300
3315
|
decisionId: decisionIdSchema,
|
|
3301
3316
|
deadlineHours: z10.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional()
|
|
@@ -3352,6 +3367,10 @@ var CreateProjectDecisionRequestSchema = z11.object({
|
|
|
3352
3367
|
sessionId: z11.string().optional(),
|
|
3353
3368
|
tagNames: z11.array(z11.string().min(1)).max(10).optional(),
|
|
3354
3369
|
raisedByPr: z11.number().int().positive().optional(),
|
|
3370
|
+
/** Slug or id of the card that raised the question. A link back, not a block. */
|
|
3371
|
+
raisedByCard: z11.string().min(1).optional(),
|
|
3372
|
+
/** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
|
|
3373
|
+
deadlineAt: z11.string().datetime({ offset: true }).optional(),
|
|
3355
3374
|
requestingUserId: z11.string().optional()
|
|
3356
3375
|
});
|
|
3357
3376
|
var ListProjectDecisionsRequestSchema = z11.object({
|
|
@@ -3387,7 +3406,16 @@ var ResolveProjectDecisionRequestSchema = z11.object({
|
|
|
3387
3406
|
var UpdateProjectDecisionRequestSchema = z11.object({
|
|
3388
3407
|
projectId: z11.string(),
|
|
3389
3408
|
decision: z11.string().min(1),
|
|
3409
|
+
question: z11.string().min(1).max(200).optional(),
|
|
3410
|
+
summary: z11.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
3411
|
+
context: z11.string().min(1).optional(),
|
|
3412
|
+
/** Accepted only while nobody has voted. */
|
|
3413
|
+
options: optionsInput.optional(),
|
|
3414
|
+
defaultOption: z11.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
|
|
3415
|
+
tagNames: z11.array(z11.string().min(1)).max(10).optional(),
|
|
3390
3416
|
deadlineHours,
|
|
3417
|
+
/** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
|
|
3418
|
+
deadlineAt: z11.string().datetime({ offset: true }).optional(),
|
|
3391
3419
|
addImpactedCards: z11.array(z11.string().min(1)).max(50).optional(),
|
|
3392
3420
|
removeImpactedCards: z11.array(z11.string().min(1)).max(50).optional(),
|
|
3393
3421
|
status: z11.enum(["Cancelled", "Open"]).optional(),
|
|
@@ -3548,8 +3576,8 @@ function anthropicEntry(model, label, inputPerMillion, outputPerMillion, opts =
|
|
|
3548
3576
|
};
|
|
3549
3577
|
}
|
|
3550
3578
|
var ANTHROPIC_CATALOG = [
|
|
3551
|
-
anthropicEntry(DEFAULT_OPUS_MODEL, "Opus 5 Latest",
|
|
3552
|
-
anthropicEntry(PREVIOUS_OPUS_MODEL, "Opus
|
|
3579
|
+
anthropicEntry(DEFAULT_OPUS_MODEL, "Opus 5.5 Latest", 4, 20),
|
|
3580
|
+
anthropicEntry(PREVIOUS_OPUS_MODEL, "Opus 5", 5, 25),
|
|
3553
3581
|
anthropicEntry(DEFAULT_SONNET_MODEL, "Sonnet 5 Latest", 3, 15),
|
|
3554
3582
|
anthropicEntry(PREVIOUS_SONNET_MODEL, "Sonnet 4.6", 3, 15),
|
|
3555
3583
|
// The Haiku line (4.5 and older) predates the tuning surface and 400s on it.
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
spawnOptionsFingerprint,
|
|
30
30
|
transcriptSize,
|
|
31
31
|
turnOptionsFrom
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-WCJ6HRVU.js";
|
|
33
33
|
import {
|
|
34
34
|
AGENT_STATUS_REASON_USER_QUESTION,
|
|
35
35
|
AgentConnection,
|
|
@@ -91,7 +91,7 @@ import {
|
|
|
91
91
|
statWorkspacePath,
|
|
92
92
|
updateRemoteToken,
|
|
93
93
|
verifyGitCredential
|
|
94
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-BZZ4B37Q.js";
|
|
95
95
|
import {
|
|
96
96
|
registerBootMilestoneSocketFallback,
|
|
97
97
|
reportBootMilestone
|
|
@@ -831,9 +831,11 @@ function mapUsage(message) {
|
|
|
831
831
|
if (!isRecord(usage)) return void 0;
|
|
832
832
|
const result = {};
|
|
833
833
|
const input = numberField(usage, "input_tokens");
|
|
834
|
+
const output = numberField(usage, "output_tokens");
|
|
834
835
|
const cacheRead = numberField(usage, "cache_read_input_tokens");
|
|
835
836
|
const cacheCreation = numberField(usage, "cache_creation_input_tokens");
|
|
836
837
|
if (input !== void 0) result.input_tokens = input;
|
|
838
|
+
if (output !== void 0) result.output_tokens = output;
|
|
837
839
|
if (cacheRead !== void 0) result.cache_read_input_tokens = cacheRead;
|
|
838
840
|
if (cacheCreation !== void 0) result.cache_creation_input_tokens = cacheCreation;
|
|
839
841
|
return result;
|
|
@@ -1807,8 +1809,13 @@ async function writeHookSettings(dir, opts = {}) {
|
|
|
1807
1809
|
// `--dangerously-skip-permissions`.
|
|
1808
1810
|
// NOTE: a bare "*" rule is INVALID (the CLI rejects it and parks the TUI on
|
|
1809
1811
|
// a Settings Warning dialog at boot) — hence the explicit ALLOW_RULES list.
|
|
1812
|
+
// `deny` carries the tools no pod agent may use (Artifact publishes to an
|
|
1813
|
+
// off-platform claude.ai URL; deliverables belong on the card). Bare tool
|
|
1814
|
+
// names are the valid rule form. The key is omitted when empty — see
|
|
1815
|
+
// HookSettingsOptions.disallowedTools for why that matters.
|
|
1810
1816
|
permissions: {
|
|
1811
|
-
allow: ALLOW_RULES
|
|
1817
|
+
allow: ALLOW_RULES,
|
|
1818
|
+
...opts.disallowedTools && opts.disallowedTools.length > 0 ? { deny: opts.disallowedTools } : {}
|
|
1812
1819
|
},
|
|
1813
1820
|
hooks: {
|
|
1814
1821
|
PreToolUse: buildPreToolUseHooks(helperPath, opts.gateAllTools === true),
|
|
@@ -2606,6 +2613,7 @@ var ClaudeTuiAdapter = class {
|
|
|
2606
2613
|
permissionMode: options.permissionMode,
|
|
2607
2614
|
settingsPath: input.settingsPath ?? "",
|
|
2608
2615
|
...options.appendSystemPrompt ? { appendSystemPrompt: options.appendSystemPrompt } : {},
|
|
2616
|
+
...options.disallowedTools?.length ? { disallowedTools: options.disallowedTools } : {},
|
|
2609
2617
|
...input.mcpConfigPath ? { mcpConfigPath: input.mcpConfigPath, strictMcpConfig: true } : {}
|
|
2610
2618
|
});
|
|
2611
2619
|
return {
|
|
@@ -3155,7 +3163,8 @@ var PtySession = class {
|
|
|
3155
3163
|
);
|
|
3156
3164
|
await this.socket.listen();
|
|
3157
3165
|
const { settingsPath } = await writeHookSettings(this.tempDir, {
|
|
3158
|
-
gateAllTools: this.options.permissionMode === "plan"
|
|
3166
|
+
gateAllTools: this.options.permissionMode === "plan",
|
|
3167
|
+
...this.options.disallowedTools?.length ? { disallowedTools: this.options.disallowedTools } : {}
|
|
3159
3168
|
});
|
|
3160
3169
|
await this.setupToolServers();
|
|
3161
3170
|
const transcriptPath = sessionTranscriptPath(this.options.cwd, sessionId);
|
|
@@ -4903,6 +4912,9 @@ var CodexTuiAdapter = class {
|
|
|
4903
4912
|
}
|
|
4904
4913
|
const config = {
|
|
4905
4914
|
cli_auth_credentials_store: "file",
|
|
4915
|
+
// The pod image owns CLI upgrades. An interactive update would try to
|
|
4916
|
+
// replace the root-owned global package from the conveyor user account.
|
|
4917
|
+
check_for_update_on_startup: false,
|
|
4906
4918
|
sqlite_home: codexSqliteHome(this.env),
|
|
4907
4919
|
approval_policy: "never",
|
|
4908
4920
|
// Always full access, plan mode included. Codex's own read-only mode is
|
|
@@ -5641,7 +5653,8 @@ function buildDiscoveryPrompt(context, runnerMode) {
|
|
|
5641
5653
|
] : [],
|
|
5642
5654
|
``,
|
|
5643
5655
|
`### Contracts this environment enforces`,
|
|
5644
|
-
`- You are READ-ONLY. File writes are denied outside \`.claude/plans/\`, and so are the commands that would start the work: git commit/push/merge/rebase/checkout, git apply, gh pr create, package publish. Everything else runs without an approval prompt \u2014 the pod is a sandbox, so investigate freely (build, test, query the dev DB, read logs).`,
|
|
5656
|
+
`- You are READ-ONLY. File writes are denied outside \`.claude/plans/\` and \`/tmp/\`, and so are the commands that would start the work: git commit/push/merge/rebase/checkout, git apply, gh pr create, package publish. Everything else runs without an approval prompt \u2014 the pod is a sandbox, so investigate freely (build, test, query the dev DB, read logs).`,
|
|
5657
|
+
`- Research deliverables the team should keep (a diagram, an HTML report, a screenshot, a data export) go on the card with \`mcp__conveyor__upload_attachment\`: write the file under \`/tmp/\` first, and pass \`title\` as the caption so it shows in the thread. Never publish one as a Claude artifact.`,
|
|
5645
5658
|
`- A plan file on disk is NOT the plan. Only \`update_task\` saves it to the card.`,
|
|
5646
5659
|
`- **ExitPlanMode is the LAST step and it validates.** It fails until the card has a plan, story points, risk, and a title \u2014 the plan comes from \`update_task\`, and story points, risk and title from \`update_task_properties\`. Calling it early wastes a turn on a denial.`,
|
|
5647
5660
|
`- ExitPlanMode does NOT start building. It parks this session for human review; the team decides when to build.`,
|
|
@@ -6340,7 +6353,7 @@ Working rules:`,
|
|
|
6340
6353
|
`- When a build/lint/test run fails, capture its output to a file once and grep the file \u2014 never re-run the suite just to re-filter the same output.`,
|
|
6341
6354
|
`- App servers (including development and production servers) keep running after startup. Start them with the harness background/session facility, then run a separate bounded HTTP readiness check and continue verification. Never wait for server exit or a completion notification to mean the app is ready. If readiness times out, inspect build output and process/resource state before restarting.`,
|
|
6342
6355
|
runtimeTui === "codex" ? `- Waiting on long-running commands: retain and resume the command session until it returns an exit result. Finish each required gate before ending the task. Never pretend a completion notification will resume you, and never start another gate while the current one is running.` : `- Waiting on long-running commands: if a gate finishes in under ~2 minutes, run it in the foreground with a timeout. For a longer one, launch it with run_in_background and STOP; a completion notification arrives when it finishes, and tracked background work holds the workspace awake for up to 45 minutes. Inspect any gate approaching that cap. For the final pre-PR gate a bounded foreground run (\`timeout 590 <gate>\` with Bash \`timeout: 600000\`) is still preferred as defense in depth. A pod restart loses both foreground and background processes. Never busy-wait with sleep/pgrep/tail loops, and never re-run the suite to escape a wait that looks stalled.`,
|
|
6343
|
-
runtimeTui === "codex" ? `- Do not end your turn to wait on a required gate. Resume its command session, inspect its exit result, and then continue the checklist.` : `- For a finite background gate, end your turn and its completion notification re-invokes you within the bounded liveness window. Never emit filler commands (\`echo waiting\`, \`true\`, \`sleep N; echo done\`) to "stay alive" \u2014 they are detected and blocked. The proven long-wait shape: start the job with run_in_background, then end the turn.${agentMode === "review" ? "" : " For CI on a pushed commit the shape is `wait_for_checks`, then end the turn (the pod may sleep; the GitHub result wakes it)."} Arm a ScheduleWakeup (delaySeconds 900-1500, prompt restating your next steps) only when nothing will notify you \u2014 a deploy, a remote queue \u2014 never for CI, and never as insurance against a background job's own notification, which does fire.`,
|
|
6356
|
+
runtimeTui === "codex" ? `- Do not end your turn to wait on a required gate. Resume its command session, inspect its exit result, and then continue the checklist.` : `- For a finite background gate, end your turn and its completion notification re-invokes you within the bounded liveness window. Never emit filler commands (\`echo waiting\`, \`true\`, \`sleep N; echo done\`) to "stay alive" \u2014 they are detected and blocked. The proven long-wait shape: start the job with run_in_background, then end the turn.${agentMode === "review" ? "" : " For CI on a pushed commit the shape is `wait_for_checks`, then end the turn (the pod may sleep; the GitHub result wakes it)."} Arm a ScheduleWakeup (delaySeconds 900-1500, prompt restating your next steps) only when nothing will notify you \u2014 a deploy, a remote queue \u2014 never for CI, and never as insurance against a background job's own notification, which does fire. One armed wakeup keeps this workspace awake until it fires (at most one hour), so arm exactly one and end the turn \u2014 a second one "as insurance" buys nothing.`,
|
|
6344
6357
|
`
|
|
6345
6358
|
Git:`,
|
|
6346
6359
|
`- Stay on \`${context.githubBranch}\` for the whole task: do not check out another branch and do not create one. It was cut from \`${context.baseBranch}\`, and PRs target that automatically.`,
|
|
@@ -7502,6 +7515,11 @@ var uploadAttachmentContract = defineToolContract({
|
|
|
7502
7515
|
title: f.optional(
|
|
7503
7516
|
f.string({ desc: "Short caption posted with the file (defaults to the file name)" })
|
|
7504
7517
|
),
|
|
7518
|
+
comment: f.optional(
|
|
7519
|
+
f.string({
|
|
7520
|
+
desc: "Alias of `title` (the external conveyor-mcp surface names this field `comment`). Provide at most one of the two."
|
|
7521
|
+
})
|
|
7522
|
+
),
|
|
7505
7523
|
tags: attachmentTags
|
|
7506
7524
|
}
|
|
7507
7525
|
},
|
|
@@ -7514,6 +7532,11 @@ var uploadAttachmentContract = defineToolContract({
|
|
|
7514
7532
|
comment: f.optional(
|
|
7515
7533
|
f.string({ desc: "When set, also posts the attachment to the task chat with this text" })
|
|
7516
7534
|
),
|
|
7535
|
+
title: f.optional(
|
|
7536
|
+
f.string({
|
|
7537
|
+
desc: "Alias of `comment` (the in-pod agent surface names this field `title`). Provide at most one of the two."
|
|
7538
|
+
})
|
|
7539
|
+
),
|
|
7517
7540
|
tags: attachmentTags,
|
|
7518
7541
|
mimeType: f.optional(
|
|
7519
7542
|
f.string({ desc: "Override the mime type inferred from the file extension" })
|
|
@@ -7573,7 +7596,7 @@ var agentOption = f.object(
|
|
|
7573
7596
|
var createDecisionContract = defineToolContract({
|
|
7574
7597
|
name: "create_decision",
|
|
7575
7598
|
agent: {
|
|
7576
|
-
description: `Raise a product decision: a fork the plan cannot be right without. Two or three options, one default, a deadline. Run list_decisions FIRST \u2014 an answered decision is the answer, and re-asking it wastes everybody's time. Do NOT file a decision for an assumption you can simply state: an assumed behaviour belongs in the plan as a GIVEN / WHEN / THEN line under "Assumed behaviours". Returns the decision
|
|
7599
|
+
description: `Raise a product decision: a fork the plan cannot be right without. Two or three options, one default, a deadline. Run list_decisions FIRST \u2014 an answered decision is the answer, and re-asking it wastes everybody's time. Do NOT file a decision for an assumption you can simply state: an assumed behaviour belongs in the plan as a GIVEN / WHEN / THEN line under "Assumed behaviours". Returns the decision in full (the same JSON as get_decision, with option ids and the deadline) plus which cards were linked.`,
|
|
7577
7600
|
fields: {
|
|
7578
7601
|
question: f.string({
|
|
7579
7602
|
min: 1,
|
|
@@ -7610,6 +7633,16 @@ var createDecisionContract = defineToolContract({
|
|
|
7610
7633
|
),
|
|
7611
7634
|
raised_by_pr: f.optional(
|
|
7612
7635
|
f.number({ int: true, min: 1, desc: "PR number that surfaced the question" })
|
|
7636
|
+
),
|
|
7637
|
+
raised_by_card: f.optional(
|
|
7638
|
+
f.string({
|
|
7639
|
+
desc: "Slug or id of the card that raised the question. A link back, not a block \u2014 use impacted_cards to block a card."
|
|
7640
|
+
})
|
|
7641
|
+
),
|
|
7642
|
+
deadline_at: f.optional(
|
|
7643
|
+
f.string({
|
|
7644
|
+
desc: "Absolute deadline as an ISO 8601 timestamp, e.g. 2026-09-25T17:00:00Z. Wins over deadline_hours when both are given; must be in the future and within 720 hours."
|
|
7645
|
+
})
|
|
7613
7646
|
)
|
|
7614
7647
|
}
|
|
7615
7648
|
},
|
|
@@ -7633,6 +7666,16 @@ var createDecisionContract = defineToolContract({
|
|
|
7633
7666
|
tagNames: f.optional(f.array(f.string(), { desc: "Tag names to categorize the decision" })),
|
|
7634
7667
|
raisedByPr: f.optional(
|
|
7635
7668
|
f.number({ int: true, min: 1, desc: "PR number that surfaced the question" })
|
|
7669
|
+
),
|
|
7670
|
+
raisedByCard: f.optional(
|
|
7671
|
+
f.string({
|
|
7672
|
+
desc: "Slug or id of the card that raised the question. A link back, not a block \u2014 use impactedCards to block a card."
|
|
7673
|
+
})
|
|
7674
|
+
),
|
|
7675
|
+
deadlineAt: f.optional(
|
|
7676
|
+
f.string({
|
|
7677
|
+
desc: "Absolute deadline (ISO 8601). Wins over deadlineHours when both are given; must be in the future and within 720 hours."
|
|
7678
|
+
})
|
|
7636
7679
|
)
|
|
7637
7680
|
}
|
|
7638
7681
|
}
|
|
@@ -7640,7 +7683,7 @@ var createDecisionContract = defineToolContract({
|
|
|
7640
7683
|
var listDecisionsContract = defineToolContract({
|
|
7641
7684
|
name: "list_decisions",
|
|
7642
7685
|
agent: {
|
|
7643
|
-
description: `Search this project's decisions. Run this BEFORE planning anything in an area: "have we already decided how X behaves?"
|
|
7686
|
+
description: `Search this project's decisions. Run this BEFORE planning anything in an area: "have we already decided how X behaves?" Each row carries the summary, who raised it (and from which card or PR), when, and \u2014 once settled \u2014 the resolution, so the list doubles as the decision log. Filter by card to see what is blocking a specific card.`,
|
|
7644
7687
|
fields: {
|
|
7645
7688
|
status: f.optional(
|
|
7646
7689
|
f.enum(STATUS_LABELS, {
|
|
@@ -7672,7 +7715,7 @@ var listDecisionsContract = defineToolContract({
|
|
|
7672
7715
|
var getDecisionContract = defineToolContract({
|
|
7673
7716
|
name: "get_decision",
|
|
7674
7717
|
agent: {
|
|
7675
|
-
description: "Read one decision in full: its context, every option with its GIVEN / WHEN / THEN, every vote with who cast it and why, the running tally, the cards it blocks, and the resolution once there is one. Use read_task_chat with the same id for the discussion thread.",
|
|
7718
|
+
description: "Read one decision in full: its summary and context, who raised it and from where, every option with its GIVEN / WHEN / THEN, every vote with who cast it and why, the running tally, the cards it blocks, and the resolution once there is one. Use read_task_chat with the same id for the discussion thread.",
|
|
7676
7719
|
fields: {
|
|
7677
7720
|
decision: f.string({ min: 1, desc: "Decision slug or id" })
|
|
7678
7721
|
}
|
|
@@ -7737,10 +7780,32 @@ var resolveDecisionContract = defineToolContract({
|
|
|
7737
7780
|
var updateDecisionContract = defineToolContract({
|
|
7738
7781
|
name: "update_decision",
|
|
7739
7782
|
agent: {
|
|
7740
|
-
description: "Change an open decision:
|
|
7783
|
+
description: "Change an open decision: fix the question, summary, context or tags, rewrite the options while nobody has voted, move the deadline, link or unlink the cards it blocks, or cancel it. Cancelling un-blocks the impacted cards without answering the question, so only do it when the question stopped mattering.",
|
|
7741
7784
|
fields: {
|
|
7742
7785
|
decision: f.string({ min: 1, desc: "Decision slug or id" }),
|
|
7786
|
+
question: f.optional(f.string({ min: 1, desc: "New question, one plain sentence" })),
|
|
7787
|
+
summary: f.optional(
|
|
7788
|
+
f.string({ desc: cardDescriptionDesc("New one- or two-sentence summary") })
|
|
7789
|
+
),
|
|
7790
|
+
context: f.optional(f.string({ min: 1, desc: "New markdown background" })),
|
|
7791
|
+
options: f.optional(
|
|
7792
|
+
f.array(agentOption, {
|
|
7793
|
+
min: 2,
|
|
7794
|
+
desc: "Replacement options. Accepted only while the decision has no votes \u2014 once somebody has voted, cancel it and raise a new one instead. Send default_option with it: the old default no longer exists."
|
|
7795
|
+
})
|
|
7796
|
+
),
|
|
7797
|
+
default_option: f.optional(
|
|
7798
|
+
f.number({ int: true, min: 1, max: 3, desc: "1-based position of the new default" })
|
|
7799
|
+
),
|
|
7800
|
+
tag_names: f.optional(
|
|
7801
|
+
f.array(f.string(), { desc: "Replacement tag names (replaces the current set)" })
|
|
7802
|
+
),
|
|
7743
7803
|
deadline_hours: f.optional(f.number({ desc: "New deadline, in hours from now. 1 to 720." })),
|
|
7804
|
+
deadline_at: f.optional(
|
|
7805
|
+
f.string({
|
|
7806
|
+
desc: "New absolute deadline (ISO 8601). Wins over deadline_hours; must be in the future and within 720 hours."
|
|
7807
|
+
})
|
|
7808
|
+
),
|
|
7744
7809
|
add_impacted_cards: f.optional(
|
|
7745
7810
|
f.array(f.string(), { desc: "Slugs or ids of cards to block on this decision" })
|
|
7746
7811
|
),
|
|
@@ -7755,13 +7820,29 @@ var updateDecisionContract = defineToolContract({
|
|
|
7755
7820
|
}
|
|
7756
7821
|
},
|
|
7757
7822
|
mcp: {
|
|
7758
|
-
description: "Change an open Conveyor decision:
|
|
7823
|
+
description: "Change an open Conveyor decision: fix the question, summary, context or tags, rewrite the options while nobody has voted, move the deadline, link or unlink impacted cards, cancel it, or reopen a settled one.",
|
|
7759
7824
|
fields: {
|
|
7760
7825
|
projectId: mcpProjectId,
|
|
7761
7826
|
decision: f.string({ desc: "Decision slug or id" }),
|
|
7827
|
+
question: f.optional(f.string({ desc: "New question" })),
|
|
7828
|
+
summary: f.optional(f.string({ desc: cardDescriptionDesc("New summary") })),
|
|
7829
|
+
context: f.optional(f.string({ desc: "New markdown background" })),
|
|
7830
|
+
options: f.optional(
|
|
7831
|
+
f.array(agentOption, {
|
|
7832
|
+
min: 2,
|
|
7833
|
+
desc: "Replacement options. Accepted only while the decision has no votes. Send defaultOption with it: the old default no longer exists."
|
|
7834
|
+
})
|
|
7835
|
+
),
|
|
7836
|
+
defaultOption: f.optional(
|
|
7837
|
+
f.number({ int: true, min: 1, max: 3, desc: "1-based position of the new default" })
|
|
7838
|
+
),
|
|
7839
|
+
tagNames: f.optional(f.array(f.string(), { desc: "Replacement tag names" })),
|
|
7762
7840
|
deadlineHours: f.optional(
|
|
7763
7841
|
f.number({ min: 1, max: 720, desc: "New deadline, in hours from now" })
|
|
7764
7842
|
),
|
|
7843
|
+
deadlineAt: f.optional(
|
|
7844
|
+
f.string({ desc: "New absolute deadline (ISO 8601). Wins over deadlineHours." })
|
|
7845
|
+
),
|
|
7765
7846
|
addImpactedCards: f.optional(
|
|
7766
7847
|
f.array(f.string(), { desc: "Slugs or ids of cards to block on this decision" })
|
|
7767
7848
|
),
|
|
@@ -8506,10 +8587,11 @@ function buildReadTaskChatTool(connection) {
|
|
|
8506
8587
|
taskId: task_id
|
|
8507
8588
|
});
|
|
8508
8589
|
return textResult(JSON.stringify(messages, null, 2));
|
|
8509
|
-
} catch {
|
|
8590
|
+
} catch (error) {
|
|
8510
8591
|
return textResult(
|
|
8511
8592
|
JSON.stringify({
|
|
8512
|
-
|
|
8593
|
+
error: `Failed to read chat: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
8594
|
+
note: "Usually transient \u2014 retry. Chat history up to this session's start was provided in the initial context."
|
|
8513
8595
|
})
|
|
8514
8596
|
);
|
|
8515
8597
|
}
|
|
@@ -9293,7 +9375,8 @@ Paste this into the PR description to show it inline:
|
|
|
9293
9375
|
${snippet}`;
|
|
9294
9376
|
}
|
|
9295
9377
|
function buildUploadAttachmentTool(connection, config) {
|
|
9296
|
-
return defineContractTool(uploadAttachmentContract, async ({ path, title, tags }) => {
|
|
9378
|
+
return defineContractTool(uploadAttachmentContract, async ({ path, title, comment, tags }) => {
|
|
9379
|
+
const caption = title ?? comment;
|
|
9297
9380
|
try {
|
|
9298
9381
|
const filePath = isAbsolute(path) ? path : join16(config.workspaceDir, path);
|
|
9299
9382
|
const mimeType = inferMimeType(filePath);
|
|
@@ -9329,11 +9412,11 @@ function buildUploadAttachmentTool(connection, config) {
|
|
|
9329
9412
|
const result = await connection.call("confirmFileUpload", {
|
|
9330
9413
|
sessionId: connection.sessionId,
|
|
9331
9414
|
fileId,
|
|
9332
|
-
title,
|
|
9415
|
+
title: caption,
|
|
9333
9416
|
tags
|
|
9334
9417
|
});
|
|
9335
9418
|
return textResult(
|
|
9336
|
-
`Uploaded ${fileName} (${info.size} bytes) and posted it to the task chat${
|
|
9419
|
+
`Uploaded ${fileName} (${info.size} bytes) and posted it to the task chat${caption ? ` with caption "${caption}"` : ""}. File ID: ${result.fileId}${describeTags(result.appliedTags, result.unknownTags)}${describeEmbed(fileName, result.downloadUrl)}`
|
|
9337
9420
|
);
|
|
9338
9421
|
} catch (error) {
|
|
9339
9422
|
return textResult(
|
|
@@ -10765,14 +10848,19 @@ function raiseAndVoteTools(connection, projectId) {
|
|
|
10765
10848
|
includeCurrentCard: input.include_current_card,
|
|
10766
10849
|
sessionId: connection.sessionId,
|
|
10767
10850
|
tagNames: input.tag_names,
|
|
10768
|
-
raisedByPr: input.raised_by_pr
|
|
10851
|
+
raisedByPr: input.raised_by_pr,
|
|
10852
|
+
raisedByCard: input.raised_by_card,
|
|
10853
|
+
deadlineAt: input.deadline_at
|
|
10769
10854
|
});
|
|
10770
|
-
const
|
|
10855
|
+
const { impactedLinked, refused, ...detail } = res;
|
|
10856
|
+
const refusedLine = refused.length > 0 ? `
|
|
10771
10857
|
|
|
10772
|
-
Not linked: ${
|
|
10858
|
+
Not linked: ${refused.map((r) => `${r.ref} (${r.reason})`).join("; ")}` : "";
|
|
10773
10859
|
return textResult(
|
|
10774
10860
|
`Raised decision ${res.slug} \u2014 ${res.url}
|
|
10775
|
-
Blocking ${
|
|
10861
|
+
Blocking ${impactedLinked.length} card(s).${refusedLine}
|
|
10862
|
+
|
|
10863
|
+
${JSON.stringify(detail, null, 2)}`
|
|
10776
10864
|
);
|
|
10777
10865
|
} catch (error) {
|
|
10778
10866
|
return errText5("Failed to raise the decision", error);
|
|
@@ -10817,7 +10905,14 @@ function settleTools(connection, projectId) {
|
|
|
10817
10905
|
const res = await connection.call("updateProjectDecision", {
|
|
10818
10906
|
projectId,
|
|
10819
10907
|
decision: input.decision,
|
|
10908
|
+
question: input.question,
|
|
10909
|
+
summary: input.summary,
|
|
10910
|
+
context: input.context,
|
|
10911
|
+
options: input.options,
|
|
10912
|
+
defaultOption: input.default_option,
|
|
10913
|
+
tagNames: input.tag_names,
|
|
10820
10914
|
deadlineHours: input.deadline_hours,
|
|
10915
|
+
deadlineAt: input.deadline_at,
|
|
10821
10916
|
addImpactedCards: input.add_impacted_cards,
|
|
10822
10917
|
removeImpactedCards: input.remove_impacted_cards,
|
|
10823
10918
|
status: input.status
|
|
@@ -11216,6 +11311,26 @@ function safeVoid(promise, context) {
|
|
|
11216
11311
|
});
|
|
11217
11312
|
}
|
|
11218
11313
|
}
|
|
11314
|
+
function addTurnUsage(total, usage) {
|
|
11315
|
+
const next = {
|
|
11316
|
+
inputTokens: total.inputTokens + (usage.input_tokens ?? 0),
|
|
11317
|
+
outputTokens: total.outputTokens + (usage.output_tokens ?? 0),
|
|
11318
|
+
cacheReadInputTokens: total.cacheReadInputTokens + (usage.cache_read_input_tokens ?? 0),
|
|
11319
|
+
cacheCreationInputTokens: total.cacheCreationInputTokens + (usage.cache_creation_input_tokens ?? 0),
|
|
11320
|
+
totalTokens: 0
|
|
11321
|
+
};
|
|
11322
|
+
next.totalTokens = next.inputTokens + next.outputTokens + next.cacheReadInputTokens + next.cacheCreationInputTokens;
|
|
11323
|
+
return next;
|
|
11324
|
+
}
|
|
11325
|
+
function emptyTurnUsage() {
|
|
11326
|
+
return {
|
|
11327
|
+
inputTokens: 0,
|
|
11328
|
+
outputTokens: 0,
|
|
11329
|
+
cacheReadInputTokens: 0,
|
|
11330
|
+
cacheCreationInputTokens: 0,
|
|
11331
|
+
totalTokens: 0
|
|
11332
|
+
};
|
|
11333
|
+
}
|
|
11219
11334
|
function epochSecondsToISO(value) {
|
|
11220
11335
|
if (typeof value === "string") return value;
|
|
11221
11336
|
if (typeof value !== "number" || value <= 0) return void 0;
|
|
@@ -11297,11 +11412,16 @@ function emitContextUpdate(modelUsage, host, context, lastAssistantUsage) {
|
|
|
11297
11412
|
});
|
|
11298
11413
|
}
|
|
11299
11414
|
}
|
|
11300
|
-
function handleSuccessResult(event, host, context, startTime, lastAssistantUsage) {
|
|
11415
|
+
function handleSuccessResult(event, host, context, startTime, lastAssistantUsage, turnUsage) {
|
|
11301
11416
|
const durationMs = Date.now() - startTime;
|
|
11302
11417
|
const summary = event.result || "Task completed.";
|
|
11303
11418
|
const retriable = isRetriableMessage(summary);
|
|
11304
|
-
host.connection.sendEvent({
|
|
11419
|
+
host.connection.sendEvent({
|
|
11420
|
+
type: "completed",
|
|
11421
|
+
summary,
|
|
11422
|
+
durationMs,
|
|
11423
|
+
...turnUsage && turnUsage.totalTokens > 0 ? { usage: turnUsage } : {}
|
|
11424
|
+
});
|
|
11305
11425
|
const { modelUsage } = event;
|
|
11306
11426
|
if (modelUsage && typeof modelUsage === "object") {
|
|
11307
11427
|
emitContextUpdate(modelUsage, host, context, lastAssistantUsage);
|
|
@@ -11322,7 +11442,7 @@ function handleErrorResult(event, host) {
|
|
|
11322
11442
|
host.connection.sendEvent({ type: "error", message: errorMsg });
|
|
11323
11443
|
return { retriable };
|
|
11324
11444
|
}
|
|
11325
|
-
function handleResultEvent(event, host, context, startTime, lastAssistantUsage) {
|
|
11445
|
+
function handleResultEvent(event, host, context, startTime, lastAssistantUsage, turnUsage) {
|
|
11326
11446
|
const resultSummary = event.subtype === "success" ? event.result : event.errors.join(", ");
|
|
11327
11447
|
if (event.subtype === "success") {
|
|
11328
11448
|
const result2 = handleSuccessResult(
|
|
@@ -11330,15 +11450,16 @@ function handleResultEvent(event, host, context, startTime, lastAssistantUsage)
|
|
|
11330
11450
|
host,
|
|
11331
11451
|
context,
|
|
11332
11452
|
startTime,
|
|
11333
|
-
lastAssistantUsage
|
|
11453
|
+
lastAssistantUsage,
|
|
11454
|
+
turnUsage
|
|
11334
11455
|
);
|
|
11335
11456
|
return { ...result2, resultSummary };
|
|
11336
11457
|
}
|
|
11337
11458
|
const result = handleErrorResult(event, host);
|
|
11338
11459
|
return { ...result, resultSummary };
|
|
11339
11460
|
}
|
|
11340
|
-
async function emitResultEvent(event, host, context, startTime, lastAssistantUsage) {
|
|
11341
|
-
const result = handleResultEvent(event, host, context, startTime, lastAssistantUsage);
|
|
11461
|
+
async function emitResultEvent(event, host, context, startTime, lastAssistantUsage, turnUsage) {
|
|
11462
|
+
const result = handleResultEvent(event, host, context, startTime, lastAssistantUsage, turnUsage);
|
|
11342
11463
|
const durationMs = Date.now() - startTime;
|
|
11343
11464
|
if (event.subtype === "success") {
|
|
11344
11465
|
const successEvent = event;
|
|
@@ -11346,7 +11467,8 @@ async function emitResultEvent(event, host, context, startTime, lastAssistantUsa
|
|
|
11346
11467
|
await host.callbacks.onEvent({
|
|
11347
11468
|
type: "completed",
|
|
11348
11469
|
summary,
|
|
11349
|
-
durationMs
|
|
11470
|
+
durationMs,
|
|
11471
|
+
...turnUsage && turnUsage.totalTokens > 0 ? { usage: turnUsage } : {}
|
|
11350
11472
|
});
|
|
11351
11473
|
} else if (!result.staleSession) {
|
|
11352
11474
|
const errorEvent = event;
|
|
@@ -11452,13 +11574,20 @@ async function handleAssistantCase(event, host, turnToolCalls) {
|
|
|
11452
11574
|
const msgUsage = event.message.usage;
|
|
11453
11575
|
return msgUsage ?? void 0;
|
|
11454
11576
|
}
|
|
11455
|
-
async function handleResultCase(event, host, context, startTime, isTyping, lastAssistantUsage) {
|
|
11577
|
+
async function handleResultCase(event, host, context, startTime, isTyping, lastAssistantUsage, turnUsage) {
|
|
11456
11578
|
let stoppedTyping = false;
|
|
11457
11579
|
if (isTyping) {
|
|
11458
11580
|
host.connection.sendTypingStop();
|
|
11459
11581
|
stoppedTyping = true;
|
|
11460
11582
|
}
|
|
11461
|
-
const resultInfo = await emitResultEvent(
|
|
11583
|
+
const resultInfo = await emitResultEvent(
|
|
11584
|
+
event,
|
|
11585
|
+
host,
|
|
11586
|
+
context,
|
|
11587
|
+
startTime,
|
|
11588
|
+
lastAssistantUsage,
|
|
11589
|
+
turnUsage
|
|
11590
|
+
);
|
|
11462
11591
|
return {
|
|
11463
11592
|
retriable: resultInfo.retriable,
|
|
11464
11593
|
resultSummary: resultInfo.resultSummary,
|
|
@@ -11562,7 +11691,10 @@ async function processAssistantCase(event, host, state) {
|
|
|
11562
11691
|
state.isTyping = true;
|
|
11563
11692
|
}
|
|
11564
11693
|
const usage = await handleAssistantCase(event, host, state.turnToolCalls);
|
|
11565
|
-
if (usage)
|
|
11694
|
+
if (usage) {
|
|
11695
|
+
state.lastAssistantUsage = usage;
|
|
11696
|
+
state.turnUsage = addTurnUsage(state.turnUsage, usage);
|
|
11697
|
+
}
|
|
11566
11698
|
if (!state.sawApiError || event.isApiError && !state.terminalApiError) {
|
|
11567
11699
|
const fullText = event.message.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
11568
11700
|
if (API_ERROR_PATTERN.test(fullText)) {
|
|
@@ -11581,7 +11713,8 @@ async function processResultCase(event, host, context, startTime, state) {
|
|
|
11581
11713
|
context,
|
|
11582
11714
|
startTime,
|
|
11583
11715
|
state.isTyping,
|
|
11584
|
-
state.lastAssistantUsage
|
|
11716
|
+
state.lastAssistantUsage,
|
|
11717
|
+
state.turnUsage
|
|
11585
11718
|
);
|
|
11586
11719
|
if (info.stoppedTyping) state.isTyping = false;
|
|
11587
11720
|
state.retriable = info.retriable;
|
|
@@ -11614,6 +11747,7 @@ async function processEvents(events, context, host) {
|
|
|
11614
11747
|
staleSession: void 0,
|
|
11615
11748
|
authError: void 0,
|
|
11616
11749
|
lastAssistantUsage: void 0,
|
|
11750
|
+
turnUsage: emptyTurnUsage(),
|
|
11617
11751
|
turnToolCalls: [],
|
|
11618
11752
|
questionPending: false
|
|
11619
11753
|
};
|
|
@@ -11693,6 +11827,9 @@ function fallbackResetIso(rateLimitType, now = Date.now()) {
|
|
|
11693
11827
|
return new Date(now + (weekly ? TWENTY_FOUR_HOURS_MS : FIVE_HOURS_MS)).toISOString();
|
|
11694
11828
|
}
|
|
11695
11829
|
|
|
11830
|
+
// src/execution/tool-access.ts
|
|
11831
|
+
import { isAbsolute as isAbsolute3, posix } from "path";
|
|
11832
|
+
|
|
11696
11833
|
// src/execution/task-property-utils.ts
|
|
11697
11834
|
function collectMissingProps(taskProps) {
|
|
11698
11835
|
const missing = [];
|
|
@@ -11835,6 +11972,12 @@ function isPlanFile(input) {
|
|
|
11835
11972
|
const filePath = String(input.file_path ?? input.path ?? "");
|
|
11836
11973
|
return filePath.includes(".claude/plans/");
|
|
11837
11974
|
}
|
|
11975
|
+
function isScratchPath(input) {
|
|
11976
|
+
const filePath = String(input.file_path ?? input.path ?? "");
|
|
11977
|
+
if (!isAbsolute3(filePath)) return false;
|
|
11978
|
+
return posix.normalize(filePath).startsWith("/tmp/");
|
|
11979
|
+
}
|
|
11980
|
+
var ARTIFACT_DENIED = "Publish deliverables on the card with upload_attachment; the Artifact tool is not available in Conveyor sessions.";
|
|
11838
11981
|
var READ_ONLY_BLOCKED_BASH = [
|
|
11839
11982
|
{ name: "git commit", re: /\bgit\s+(?:-\S+\s+)*commit\b/ },
|
|
11840
11983
|
{ name: "git push", re: /\bgit\s+(?:-\S+\s+)*push\b/ },
|
|
@@ -11860,12 +12003,12 @@ function handleReadOnlyToolAccess(toolName, input) {
|
|
|
11860
12003
|
return { behavior: "deny", message: CHANNEL_POST_DENIED };
|
|
11861
12004
|
}
|
|
11862
12005
|
if (PM_PLAN_FILE_TOOLS.has(toolName)) {
|
|
11863
|
-
if (isPlanFile(input)) {
|
|
12006
|
+
if (isPlanFile(input) || isScratchPath(input)) {
|
|
11864
12007
|
return { behavior: "allow", updatedInput: input };
|
|
11865
12008
|
}
|
|
11866
12009
|
return {
|
|
11867
12010
|
behavior: "deny",
|
|
11868
|
-
message: "Discovery mode is read-only. File writes are restricted to plan files."
|
|
12011
|
+
message: "Discovery mode is read-only. File writes are restricted to plan files and /tmp/ scratch files \u2014 write a deliverable under /tmp/ and attach it with upload_attachment."
|
|
11869
12012
|
};
|
|
11870
12013
|
}
|
|
11871
12014
|
if (toolName === "Bash") {
|
|
@@ -12024,7 +12167,7 @@ var DENIAL_FORCE_STOP_THRESHOLD = 8;
|
|
|
12024
12167
|
function handleDenialEscalation(host, consecutiveDenials) {
|
|
12025
12168
|
if (consecutiveDenials === DENIAL_WARNING_THRESHOLD) {
|
|
12026
12169
|
host.connection.postChatMessage(
|
|
12027
|
-
`\u26A0\uFE0F Multiple tool denials detected. You are in ${host.agentMode} mode \u2014 file writes outside .claude/plans/ are not permitted. Focus on creating a plan instead of implementing code changes.`
|
|
12170
|
+
`\u26A0\uFE0F Multiple tool denials detected. You are in ${host.agentMode} mode \u2014 file writes outside .claude/plans/ and /tmp/ are not permitted. Focus on creating a plan instead of implementing code changes.`
|
|
12028
12171
|
);
|
|
12029
12172
|
}
|
|
12030
12173
|
if (consecutiveDenials >= DENIAL_FORCE_STOP_THRESHOLD) {
|
|
@@ -12070,6 +12213,9 @@ function checkToolLoop(host, toolName, input) {
|
|
|
12070
12213
|
return { behavior: "deny", message: buildRepeatLoopMessage(repeats, isHeavyGateActive()) };
|
|
12071
12214
|
}
|
|
12072
12215
|
function resolveToolAccess(host, toolName, input) {
|
|
12216
|
+
if (toolName === "Artifact") {
|
|
12217
|
+
return { behavior: "deny", message: ARTIFACT_DENIED };
|
|
12218
|
+
}
|
|
12073
12219
|
switch (host.agentMode) {
|
|
12074
12220
|
case "discovery":
|
|
12075
12221
|
case "help":
|
|
@@ -13784,6 +13930,100 @@ var BackgroundWorkTracker = class {
|
|
|
13784
13930
|
}
|
|
13785
13931
|
};
|
|
13786
13932
|
|
|
13933
|
+
// src/runner/scheduled-wakeup.ts
|
|
13934
|
+
var SCHEDULE_WAKEUP_TOOL = "schedulewakeup";
|
|
13935
|
+
var SCHEDULED_WAKEUP_MIN_DELAY_SECONDS = 60;
|
|
13936
|
+
var SCHEDULED_WAKEUP_MAX_DELAY_SECONDS = 3600;
|
|
13937
|
+
var SCHEDULED_WAKEUP_GRACE_MS = 2 * 60 * 1e3;
|
|
13938
|
+
function asRecord3(input) {
|
|
13939
|
+
if (typeof input === "string") {
|
|
13940
|
+
try {
|
|
13941
|
+
const parsed = JSON.parse(input);
|
|
13942
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
13943
|
+
} catch {
|
|
13944
|
+
return null;
|
|
13945
|
+
}
|
|
13946
|
+
}
|
|
13947
|
+
return typeof input === "object" && input !== null ? input : null;
|
|
13948
|
+
}
|
|
13949
|
+
function parseScheduledWakeup(tool2, input, now = Date.now()) {
|
|
13950
|
+
if (tool2.toLowerCase() !== SCHEDULE_WAKEUP_TOOL) return null;
|
|
13951
|
+
const record = asRecord3(input);
|
|
13952
|
+
if (!record) return null;
|
|
13953
|
+
if (record.stop === true) return null;
|
|
13954
|
+
const raw = record.delaySeconds;
|
|
13955
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return null;
|
|
13956
|
+
const delaySeconds = Math.min(
|
|
13957
|
+
SCHEDULED_WAKEUP_MAX_DELAY_SECONDS,
|
|
13958
|
+
Math.max(SCHEDULED_WAKEUP_MIN_DELAY_SECONDS, raw)
|
|
13959
|
+
);
|
|
13960
|
+
return { delaySeconds, deadline: now + delaySeconds * 1e3 + SCHEDULED_WAKEUP_GRACE_MS };
|
|
13961
|
+
}
|
|
13962
|
+
function isScheduledWakeupStop(tool2, input) {
|
|
13963
|
+
if (tool2.toLowerCase() !== SCHEDULE_WAKEUP_TOOL) return false;
|
|
13964
|
+
return asRecord3(input)?.stop === true;
|
|
13965
|
+
}
|
|
13966
|
+
var ScheduledWakeupTracker = class {
|
|
13967
|
+
/** Absolute expiry (epoch ms) of the armed wakeup; null when none. */
|
|
13968
|
+
deadline = null;
|
|
13969
|
+
onChange;
|
|
13970
|
+
log;
|
|
13971
|
+
constructor(options = {}) {
|
|
13972
|
+
this.onChange = options.onChange;
|
|
13973
|
+
this.log = options.log ?? ((message) => process.stderr.write(message));
|
|
13974
|
+
}
|
|
13975
|
+
/**
|
|
13976
|
+
* Feed one `tool_use` from the turn stream. Returns true when it changed the
|
|
13977
|
+
* armed state (test/diagnostic signal; callers can ignore it).
|
|
13978
|
+
*/
|
|
13979
|
+
noteToolUse(tool2, input, now = Date.now()) {
|
|
13980
|
+
if (isScheduledWakeupStop(tool2, input)) {
|
|
13981
|
+
if (this.deadline === null) return false;
|
|
13982
|
+
this.deadline = null;
|
|
13983
|
+
this.log("[conveyor-agent] scheduled wakeup stopped \u2014 heartbeats may report idle again\n");
|
|
13984
|
+
this.onChange?.();
|
|
13985
|
+
return true;
|
|
13986
|
+
}
|
|
13987
|
+
const arm = parseScheduledWakeup(tool2, input, now);
|
|
13988
|
+
if (!arm) return false;
|
|
13989
|
+
this.deadline = arm.deadline;
|
|
13990
|
+
this.log(
|
|
13991
|
+
`[conveyor-agent] scheduled wakeup armed (${arm.delaySeconds}s) \u2014 heartbeats stay non-idle until it fires
|
|
13992
|
+
`
|
|
13993
|
+
);
|
|
13994
|
+
this.onChange?.();
|
|
13995
|
+
return true;
|
|
13996
|
+
}
|
|
13997
|
+
/** Is a non-expired wakeup armed? */
|
|
13998
|
+
hasPending(now = Date.now()) {
|
|
13999
|
+
this.prune(now);
|
|
14000
|
+
return this.deadline !== null;
|
|
14001
|
+
}
|
|
14002
|
+
/** Absolute expiry of the armed wakeup, or null when none is armed. Callers
|
|
14003
|
+
* bound their own deferrals by this rather than by a fixed cap — a one-hour
|
|
14004
|
+
* arm outlives `BACKGROUND_WORK_MAX_MS`. */
|
|
14005
|
+
pendingDeadline(now = Date.now()) {
|
|
14006
|
+
this.prune(now);
|
|
14007
|
+
return this.deadline;
|
|
14008
|
+
}
|
|
14009
|
+
/** Drop the arm (a turn started, or the session is shutting down). */
|
|
14010
|
+
clear() {
|
|
14011
|
+
if (this.deadline === null) return;
|
|
14012
|
+
this.deadline = null;
|
|
14013
|
+
this.onChange?.();
|
|
14014
|
+
}
|
|
14015
|
+
/** Expire a deadline that has passed, loudly — the wakeup should have fired
|
|
14016
|
+
* a turn by now, so a tripped expiry is worth seeing in the log. */
|
|
14017
|
+
prune(now) {
|
|
14018
|
+
if (this.deadline === null || this.deadline > now) return;
|
|
14019
|
+
this.deadline = null;
|
|
14020
|
+
this.log(
|
|
14021
|
+
"[conveyor-agent] scheduled wakeup deadline passed with no turn \u2014 reporting idle again\n"
|
|
14022
|
+
);
|
|
14023
|
+
this.onChange?.();
|
|
14024
|
+
}
|
|
14025
|
+
};
|
|
14026
|
+
|
|
13787
14027
|
// src/runner/live-children.ts
|
|
13788
14028
|
function findLiveChild(sources) {
|
|
13789
14029
|
for (const source of sources) {
|
|
@@ -13851,6 +14091,13 @@ var SessionRunner = class _SessionRunner {
|
|
|
13851
14091
|
backgroundWork = new BackgroundWorkTracker({
|
|
13852
14092
|
onChange: () => this.refreshLoopStatus()
|
|
13853
14093
|
});
|
|
14094
|
+
/** An armed `ScheduleWakeup` (the dynamic `/loop` mode): the turn ended, but
|
|
14095
|
+
* the agent asked to be re-invoked at a deadline. Same liveness contract as
|
|
14096
|
+
* `backgroundWork` — while it reports pending, heartbeats go out non-idle so
|
|
14097
|
+
* the reconciler cannot sleep the pod before the wakeup fires. */
|
|
14098
|
+
scheduledWakeup = new ScheduledWakeupTracker({
|
|
14099
|
+
onChange: () => this.refreshLoopStatus()
|
|
14100
|
+
});
|
|
13854
14101
|
/** Boot supervisor handle, set post-construction once it's known (it is
|
|
13855
14102
|
* started alongside connect(), before the runner itself exists in some
|
|
13856
14103
|
* call sites) — notified when the core loop goes live so it can release
|
|
@@ -13895,6 +14142,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
13895
14142
|
},
|
|
13896
14143
|
onIdleTimeout: () => {
|
|
13897
14144
|
if (this.deferShutdownForLiveChild("idle")) return;
|
|
14145
|
+
if (this.deferShutdownForScheduledWakeup("idle")) return;
|
|
13898
14146
|
if (this.deferShutdownForBackgroundWork()) return;
|
|
13899
14147
|
process.stderr.write("[conveyor-agent] Idle timeout reached, stopping agent\n");
|
|
13900
14148
|
this.stopped = true;
|
|
@@ -13907,6 +14155,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
13907
14155
|
},
|
|
13908
14156
|
onDormantTimeout: () => {
|
|
13909
14157
|
if (this.deferShutdownForLiveChild("dormant")) return;
|
|
14158
|
+
if (this.deferShutdownForScheduledWakeup("dormant")) return;
|
|
13910
14159
|
process.stderr.write("[conveyor-agent] Dormant idle timeout reached, shutting down\n");
|
|
13911
14160
|
this.stopped = true;
|
|
13912
14161
|
this.queryBridge?.stop();
|
|
@@ -13966,6 +14215,50 @@ var SessionRunner = class _SessionRunner {
|
|
|
13966
14215
|
const label = timer === "idle" ? "Idle" : "Dormant idle";
|
|
13967
14216
|
process.stderr.write(
|
|
13968
14217
|
`[conveyor-agent] ${label} timeout deferred: ${live.kind} session ${live.sessionId} active
|
|
14218
|
+
`
|
|
14219
|
+
);
|
|
14220
|
+
if (timer === "idle") {
|
|
14221
|
+
this.lifecycle.startIdleTimer(recheckMs);
|
|
14222
|
+
} else {
|
|
14223
|
+
this.dormantDeadline = Date.now() + recheckMs;
|
|
14224
|
+
this.lifecycle.startDormantTimer(recheckMs);
|
|
14225
|
+
}
|
|
14226
|
+
return true;
|
|
14227
|
+
}
|
|
14228
|
+
/**
|
|
14229
|
+
* Suppress an idle or dormant shutdown while a `ScheduleWakeup` is armed.
|
|
14230
|
+
*
|
|
14231
|
+
* Both timers matter. The idle timer (30 min) kills a loop that paces itself
|
|
14232
|
+
* slower than that, and the dormant timer (60 min after a `completed` event)
|
|
14233
|
+
* kills a one-hour arm — the agent declares completion at the end of every
|
|
14234
|
+
* tick, so a dynamic loop sits dormant by construction.
|
|
14235
|
+
*
|
|
14236
|
+
* The bound is the WAKEUP'S OWN deadline, not `BACKGROUND_WORK_MAX_MS`: the
|
|
14237
|
+
* tool clamps `delaySeconds` to one hour, which already exceeds that 45-min
|
|
14238
|
+
* cap, so reusing it would shut the pod down before the longest legal arm
|
|
14239
|
+
* fired. Once the deadline passes the tracker expires it and this returns
|
|
14240
|
+
* false, so a lost timer cannot pin a pod.
|
|
14241
|
+
*/
|
|
14242
|
+
deferShutdownForScheduledWakeup(timer) {
|
|
14243
|
+
let deadline;
|
|
14244
|
+
try {
|
|
14245
|
+
deadline = this.scheduledWakeup.pendingDeadline();
|
|
14246
|
+
} catch (err) {
|
|
14247
|
+
process.stderr.write(
|
|
14248
|
+
`[conveyor-agent] Scheduled-wakeup probe failed, not deferring: ${err}
|
|
14249
|
+
`
|
|
14250
|
+
);
|
|
14251
|
+
return false;
|
|
14252
|
+
}
|
|
14253
|
+
if (deadline === null) return false;
|
|
14254
|
+
const remainingMs = Math.max(0, deadline - Date.now());
|
|
14255
|
+
const recheckMs = Math.min(
|
|
14256
|
+
Math.max(remainingMs, 1),
|
|
14257
|
+
Math.max(this.lifecycle.config.idleTimeoutMs, 1)
|
|
14258
|
+
);
|
|
14259
|
+
const label = timer === "idle" ? "Idle" : "Dormant idle";
|
|
14260
|
+
process.stderr.write(
|
|
14261
|
+
`[conveyor-agent] ${label} timeout deferred: scheduled wakeup armed (${Math.round(remainingMs / 1e3)}s remaining)
|
|
13969
14262
|
`
|
|
13970
14263
|
);
|
|
13971
14264
|
if (timer === "idle") {
|
|
@@ -14895,6 +15188,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
14895
15188
|
});
|
|
14896
15189
|
this.portDiscovery.stop();
|
|
14897
15190
|
this.backgroundWork.clear();
|
|
15191
|
+
this.scheduledWakeup.clear();
|
|
14898
15192
|
this.loopLag.stop();
|
|
14899
15193
|
this.lifecycle.destroy();
|
|
14900
15194
|
this.connection.disconnect();
|
|
@@ -15135,6 +15429,7 @@ ${outcome.failures.join("\n")}
|
|
|
15135
15429
|
if (this.questionPendingSince !== null && Date.now() - this.questionPendingSince < resolveQuestionGraceMs() + QUESTION_HOLD_SLACK_MS) {
|
|
15136
15430
|
return "waiting";
|
|
15137
15431
|
}
|
|
15432
|
+
if (this.scheduledWakeup.hasPending()) return "waiting";
|
|
15138
15433
|
return this.backgroundWork.hasPending() || isHeavyGateActive() ? "waiting" : "idle";
|
|
15139
15434
|
}
|
|
15140
15435
|
// ── Question grace hold ────────────────────────────────────────────
|
|
@@ -15205,11 +15500,12 @@ ${outcome.failures.join("\n")}
|
|
|
15205
15500
|
this.loopLag.setStatus(loopStatus);
|
|
15206
15501
|
return loopStatus;
|
|
15207
15502
|
}
|
|
15208
|
-
/** One `tool_use` from the turn stream — registers
|
|
15209
|
-
*
|
|
15503
|
+
/** One `tool_use` from the turn stream — registers work that outlives the
|
|
15504
|
+
* turn: a background launch, or an armed `ScheduleWakeup`. */
|
|
15210
15505
|
noteToolUseForBackgroundWork(event) {
|
|
15211
15506
|
if (event.type !== "tool_use" || typeof event.tool !== "string") return;
|
|
15212
15507
|
this.backgroundWork.noteToolUse(event.tool, event.input);
|
|
15508
|
+
this.scheduledWakeup.noteToolUse(event.tool, event.input);
|
|
15213
15509
|
}
|
|
15214
15510
|
async setState(status) {
|
|
15215
15511
|
if (status === "running") {
|
|
@@ -15250,6 +15546,7 @@ ${outcome.failures.join("\n")}
|
|
|
15250
15546
|
/** Mark a fresh turn without emitting a duplicate status. QueryExecutor's
|
|
15251
15547
|
* prefilled PTY callback already emitted `running` before it reaches here. */
|
|
15252
15548
|
beginRunningEpisode() {
|
|
15549
|
+
this.scheduledWakeup.clear();
|
|
15253
15550
|
this.activeEpisodeStartedAt = Date.now();
|
|
15254
15551
|
this.backgroundDeferStartedAt = null;
|
|
15255
15552
|
this.backgroundWakeFiredThisIdle = false;
|
|
@@ -15261,6 +15558,7 @@ ${outcome.failures.join("\n")}
|
|
|
15261
15558
|
this.clearQuestionHold();
|
|
15262
15559
|
this.portDiscovery.stop();
|
|
15263
15560
|
this.backgroundWork.clear();
|
|
15561
|
+
this.scheduledWakeup.clear();
|
|
15264
15562
|
this.loopLag.stop();
|
|
15265
15563
|
this.lifecycle.destroy();
|
|
15266
15564
|
try {
|
|
@@ -26,6 +26,9 @@ function buildSpawnArgs(input) {
|
|
|
26
26
|
args.push("--permission-mode", "plan");
|
|
27
27
|
}
|
|
28
28
|
args.push("--settings", input.settingsPath);
|
|
29
|
+
if (input.disallowedTools && input.disallowedTools.length > 0) {
|
|
30
|
+
args.push("--disallowed-tools", input.disallowedTools.join(","));
|
|
31
|
+
}
|
|
29
32
|
if (input.appendSystemPrompt) {
|
|
30
33
|
args.push("--append-system-prompt", input.appendSystemPrompt);
|
|
31
34
|
}
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
WorkspaceCommandSupervisor,
|
|
8
8
|
startWorkspaceCommandsAfterConnect,
|
|
9
9
|
stopWorkspaceCommands
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-7WO6NTOS.js";
|
|
11
11
|
import {
|
|
12
12
|
PtyHarness,
|
|
13
13
|
SessionRunner,
|
|
@@ -25,13 +25,13 @@ import {
|
|
|
25
25
|
runUsageProbe,
|
|
26
26
|
sampleKeyUsage,
|
|
27
27
|
usesNativeUsageReporting
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-RLROQPPT.js";
|
|
29
29
|
import "./chunk-37J5MMQT.js";
|
|
30
30
|
import {
|
|
31
31
|
inheritedEnv,
|
|
32
32
|
resolvePtySpawn,
|
|
33
33
|
sessionTempBase
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-WCJ6HRVU.js";
|
|
35
35
|
import {
|
|
36
36
|
AgentConnection,
|
|
37
37
|
CodespacePortVisibility,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
createServiceLogger,
|
|
45
45
|
fetchBootstrap,
|
|
46
46
|
loadConveyorConfig
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-BZZ4B37Q.js";
|
|
48
48
|
import "./chunk-Q4FQOJ7D.js";
|
|
49
49
|
import "./chunk-W4LZ7R6Z.js";
|
|
50
50
|
import "./chunk-372R6E4C.js";
|
|
@@ -1205,7 +1205,7 @@ function wireSpawnChildren(mode, connection, supervisors, logger7) {
|
|
|
1205
1205
|
|
|
1206
1206
|
// src/cli.ts
|
|
1207
1207
|
if (process.argv[2] === "boot") {
|
|
1208
|
-
const { runBoot } = await import("./boot-
|
|
1208
|
+
const { runBoot } = await import("./boot-65PTSADA.js");
|
|
1209
1209
|
process.exit(await runBoot(process.argv.slice(3)));
|
|
1210
1210
|
}
|
|
1211
1211
|
if (isLegacyEntrypointLaunch(process.env)) {
|
|
@@ -1307,7 +1307,7 @@ process.on("unhandledRejection", (reason) => {
|
|
|
1307
1307
|
process.exit(1);
|
|
1308
1308
|
});
|
|
1309
1309
|
if (process.env.CONVEYOR_MODE === "workbench") {
|
|
1310
|
-
const { startWorkbenchServer } = await import("./server-
|
|
1310
|
+
const { startWorkbenchServer } = await import("./server-E6IYHSMN.js");
|
|
1311
1311
|
const { oomWatchdogOptionsFromEnv } = await import("./oom-watchdog-PAC5OJJG.js");
|
|
1312
1312
|
const { DEFAULT_WORKBENCH_PORT } = await import("./protocol-QBCYO4GI.js");
|
|
1313
1313
|
const port = Number(process.env.CONVEYOR_WORKBENCH_PORT) || DEFAULT_WORKBENCH_PORT;
|
|
@@ -1458,7 +1458,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
|
|
|
1458
1458
|
process.exit(1);
|
|
1459
1459
|
}
|
|
1460
1460
|
if (CONVEYOR_MODE === "serving") {
|
|
1461
|
-
const { runServingSession } = await import("./serve-boot-
|
|
1461
|
+
const { runServingSession } = await import("./serve-boot-T7MNWASE.js");
|
|
1462
1462
|
exitContext.runnerMode = "serving";
|
|
1463
1463
|
exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
|
|
1464
1464
|
const outcome = await runServingSession({
|
package/dist/index.d.ts
CHANGED
|
@@ -732,6 +732,11 @@ declare class SessionRunner {
|
|
|
732
732
|
* it reports pending, heartbeats go out non-idle so the workspace activity
|
|
733
733
|
* clock keeps being bumped and the reconciler can't sleep the pod mid-gate. */
|
|
734
734
|
private readonly backgroundWork;
|
|
735
|
+
/** An armed `ScheduleWakeup` (the dynamic `/loop` mode): the turn ended, but
|
|
736
|
+
* the agent asked to be re-invoked at a deadline. Same liveness contract as
|
|
737
|
+
* `backgroundWork` — while it reports pending, heartbeats go out non-idle so
|
|
738
|
+
* the reconciler cannot sleep the pod before the wakeup fires. */
|
|
739
|
+
private readonly scheduledWakeup;
|
|
735
740
|
/** Boot supervisor handle, set post-construction once it's known (it is
|
|
736
741
|
* started alongside connect(), before the runner itself exists in some
|
|
737
742
|
* call sites) — notified when the core loop goes live so it can release
|
|
@@ -782,6 +787,21 @@ declare class SessionRunner {
|
|
|
782
787
|
* live build costs work.
|
|
783
788
|
*/
|
|
784
789
|
private deferShutdownForLiveChild;
|
|
790
|
+
/**
|
|
791
|
+
* Suppress an idle or dormant shutdown while a `ScheduleWakeup` is armed.
|
|
792
|
+
*
|
|
793
|
+
* Both timers matter. The idle timer (30 min) kills a loop that paces itself
|
|
794
|
+
* slower than that, and the dormant timer (60 min after a `completed` event)
|
|
795
|
+
* kills a one-hour arm — the agent declares completion at the end of every
|
|
796
|
+
* tick, so a dynamic loop sits dormant by construction.
|
|
797
|
+
*
|
|
798
|
+
* The bound is the WAKEUP'S OWN deadline, not `BACKGROUND_WORK_MAX_MS`: the
|
|
799
|
+
* tool clamps `delaySeconds` to one hour, which already exceeds that 45-min
|
|
800
|
+
* cap, so reusing it would shut the pod down before the longest legal arm
|
|
801
|
+
* fired. Once the deadline passes the tracker expires it and this returns
|
|
802
|
+
* false, so a lost timer cannot pin a pod.
|
|
803
|
+
*/
|
|
804
|
+
private deferShutdownForScheduledWakeup;
|
|
785
805
|
/** Epoch ms of the first idle-shutdown deferral for background work in the
|
|
786
806
|
* current idle episode; null when nothing has deferred. Reset when a turn
|
|
787
807
|
* runs, so each idle episode gets its own bounded hold. */
|
|
@@ -1101,8 +1121,8 @@ declare class SessionRunner {
|
|
|
1101
1121
|
/** Mirror the resolved status into the loop-lag buffer so the starvation-proof
|
|
1102
1122
|
* heartbeat worker reports it too. Returns what it wrote. */
|
|
1103
1123
|
private refreshLoopStatus;
|
|
1104
|
-
/** One `tool_use` from the turn stream — registers
|
|
1105
|
-
*
|
|
1124
|
+
/** One `tool_use` from the turn stream — registers work that outlives the
|
|
1125
|
+
* turn: a background launch, or an armed `ScheduleWakeup`. */
|
|
1106
1126
|
private noteToolUseForBackgroundWork;
|
|
1107
1127
|
private setState;
|
|
1108
1128
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RLROQPPT.js";
|
|
5
5
|
import "./chunk-37J5MMQT.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-WCJ6HRVU.js";
|
|
7
7
|
import {
|
|
8
8
|
AgentConnection,
|
|
9
9
|
GIT_TIMEOUT_MS,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
stageAndCommit,
|
|
18
18
|
updateRemoteToken,
|
|
19
19
|
workspacePathExists
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-BZZ4B37Q.js";
|
|
21
21
|
import "./chunk-Q4FQOJ7D.js";
|
|
22
22
|
import {
|
|
23
23
|
runAuthTokenCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7WO6NTOS.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentConnection,
|
|
6
6
|
CodespacePortVisibility,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createServiceLogger,
|
|
12
12
|
ensureOnTaskBranch,
|
|
13
13
|
loadConveyorConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BZZ4B37Q.js";
|
|
15
15
|
import "./chunk-Q4FQOJ7D.js";
|
|
16
16
|
import "./chunk-W4LZ7R6Z.js";
|
|
17
17
|
import "./chunk-IA45XHOA.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.25",
|
|
4
4
|
"description": "Conveyor Agent Runner v10 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"typecheck": "tsgo --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
41
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.280",
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
43
43
|
"node-pty": "^1.0.0",
|
|
44
44
|
"socket.io-client": "^4.8.3",
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"@project/shared": "workspace:*",
|
|
50
50
|
"tsup": "^8.0.0",
|
|
51
51
|
"typescript": "^5.3.0",
|
|
52
|
-
"vitest": "^
|
|
52
|
+
"vitest": "^5.0.1"
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -157,6 +157,18 @@ and the order to verify in. Write it last, when you know which parts were hard.
|
|
|
157
157
|
It is not optional and it is not a summary. It is the only channel your context
|
|
158
158
|
has across the handoff, because handoffs carry the card and never the session.
|
|
159
159
|
|
|
160
|
+
**Deliverables beyond the plan go on the card.** A diagram, an HTML report, a
|
|
161
|
+
screenshot, or a data export the team should keep is attached with
|
|
162
|
+
`mcp__conveyor__upload_attachment` — never published as a Claude artifact, which
|
|
163
|
+
is an off-platform link the card cannot render. Write the file under `/tmp/`
|
|
164
|
+
first (a Planner session may write there; the repo checkout stays read-only) and
|
|
165
|
+
pass the caption so the file shows in the card thread rather than only in the
|
|
166
|
+
Files grid. The caption field is spelled differently per surface, and both
|
|
167
|
+
surfaces accept either name: in a pod pass `path` + `title`; on the local MCP
|
|
168
|
+
pass `taskId` + `path` + `comment` (or `title`). An HTML deliverable must be
|
|
169
|
+
self-contained — inline CSS, no `<script>`, no external assets — because the
|
|
170
|
+
card serves it under `sandbox; default-src 'none'`.
|
|
171
|
+
|
|
160
172
|
Size it: 1 SP default, 2 multi-file, 3 complex patterns/design, 5 hard. Larger
|
|
161
173
|
→ propose a pack (parent + `mcp__conveyor__create_subtask` children) instead of
|
|
162
174
|
one mega-card. **Child→child ordering rides `create_subtask`/`update_subtask`'s
|