@rallycry/conveyor-agent 11.0.8 → 11.0.9

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.
@@ -59,7 +59,7 @@ import {
59
59
  statWorkspacePath,
60
60
  updateRemoteToken,
61
61
  verifyGitCredential
62
- } from "./chunk-APP6Y2D7.js";
62
+ } from "./chunk-YMZ6Q55T.js";
63
63
  import {
64
64
  registerBootMilestoneSocketFallback,
65
65
  reportBootMilestone
@@ -230,6 +230,7 @@ import { z as z6 } from "zod";
230
230
  import { z as z7 } from "zod";
231
231
  import { z as z8 } from "zod";
232
232
  import { z as z9 } from "zod";
233
+ import { z as z10 } from "zod";
233
234
  var EXTERNAL_AGENT_MESSAGE_SOURCE = "external_agent";
234
235
  var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
235
236
  var DEFAULT_OPUS_MODEL = "claude-opus-5";
@@ -993,10 +994,6 @@ var ReportReviewSpawnFailureRequestSchema = z4.object({
993
994
  var ReportBuilderSpawnFailureRequestSchema = ReportReviewSpawnFailureRequestSchema.omit({
994
995
  reviewSessionId: true
995
996
  }).extend({ buildSessionId: z4.string() });
996
- var RequestWorkspaceRecycleRequestSchema = z4.object({
997
- sessionId: z4.string(),
998
- reason: z4.string().max(2e3)
999
- });
1000
997
  var SpawnTaskSessionRequestSchema = z4.object({
1001
998
  taskId: z4.string(),
1002
999
  kind: z4.enum(["tui", "shell"])
@@ -1679,12 +1676,21 @@ var GetProjectAnalyticsSummaryRequestSchema = z6.object({
1679
1676
  rangeDays: z6.number().int().min(1).max(GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS).optional(),
1680
1677
  campaign: z6.string().max(200).optional()
1681
1678
  });
1679
+ var RequestWorkspaceRecycleRequestSchema = z7.object({
1680
+ sessionId: z7.string(),
1681
+ reason: z7.string().max(2e3)
1682
+ });
1683
+ var ReportApiOutageRequestSchema = z7.object({
1684
+ sessionId: z7.string(),
1685
+ detail: z7.string().max(2e3),
1686
+ attempts: z7.number().int().min(0).max(100)
1687
+ });
1682
1688
  var SHA_PATTERN = /^[0-9a-f]{40}$/i;
1683
- var ReviewGuideFileReferenceSchema = z7.object({
1684
- path: z7.string().min(1).max(500),
1685
- startLine: z7.number().int().positive().max(1e6).optional(),
1686
- endLine: z7.number().int().positive().max(1e6).optional(),
1687
- hunkHeader: z7.string().min(1).max(300).optional()
1689
+ var ReviewGuideFileReferenceSchema = z8.object({
1690
+ path: z8.string().min(1).max(500),
1691
+ startLine: z8.number().int().positive().max(1e6).optional(),
1692
+ endLine: z8.number().int().positive().max(1e6).optional(),
1693
+ hunkHeader: z8.string().min(1).max(300).optional()
1688
1694
  }).strict().superRefine((value, ctx) => {
1689
1695
  if (value.endLine !== void 0 && value.startLine === void 0) {
1690
1696
  ctx.addIssue({
@@ -1701,19 +1707,19 @@ var ReviewGuideFileReferenceSchema = z7.object({
1701
1707
  });
1702
1708
  }
1703
1709
  });
1704
- var ReviewGuideSectionSchema = z7.object({
1705
- title: z7.string().min(1).max(160),
1706
- explanation: z7.string().min(1).max(2e3),
1707
- classification: z7.enum(["core", "supporting"]).optional(),
1708
- files: z7.array(ReviewGuideFileReferenceSchema).min(1).max(20)
1710
+ var ReviewGuideSectionSchema = z8.object({
1711
+ title: z8.string().min(1).max(160),
1712
+ explanation: z8.string().min(1).max(2e3),
1713
+ classification: z8.enum(["core", "supporting"]).optional(),
1714
+ files: z8.array(ReviewGuideFileReferenceSchema).min(1).max(20)
1709
1715
  }).strict();
1710
- var ReviewGuideContentSchema = z7.object({
1711
- overview: z7.string().min(1).max(3e3),
1712
- sections: z7.array(ReviewGuideSectionSchema).min(1).max(12)
1716
+ var ReviewGuideContentSchema = z8.object({
1717
+ overview: z8.string().min(1).max(3e3),
1718
+ sections: z8.array(ReviewGuideSectionSchema).min(1).max(12)
1713
1719
  }).strict();
1714
1720
  var PublishReviewGuideRequestSchema = ReviewGuideContentSchema.extend({
1715
- sessionId: z7.string().min(1),
1716
- reviewedSha: z7.string().regex(SHA_PATTERN, "reviewedSha must be a 40-character commit SHA")
1721
+ sessionId: z8.string().min(1),
1722
+ reviewedSha: z8.string().regex(SHA_PATTERN, "reviewedSha must be a 40-character commit SHA")
1717
1723
  }).strict();
1718
1724
  var CONTEXT_LINK_LOCATOR_MAX = 300;
1719
1725
  var TEST_TITLE = /\b(?:it|test|describe)(?:\.\w+)?\s*\(\s*(['"`])((?:(?!\1)[\s\S])*)\1/g;
@@ -1730,130 +1736,130 @@ function locatorMatchesContent(content, locatorType, locator) {
1730
1736
  var TAG_DESCRIPTION_MAX = CARD_DESCRIPTION_MAX;
1731
1737
  var TAG_OVERVIEW_MAX = 32e3;
1732
1738
  var TAG_REASON_MAX = 500;
1733
- var ProjectTagContextPathSchema = z8.object({
1734
- type: z8.enum(["rule", "doc", "file", "folder"]),
1735
- path: z8.string().min(1).max(500),
1736
- label: z8.string().max(100).optional(),
1739
+ var ProjectTagContextPathSchema = z9.object({
1740
+ type: z9.enum(["rule", "doc", "file", "folder"]),
1741
+ path: z9.string().min(1).max(500),
1742
+ label: z9.string().max(100).optional(),
1737
1743
  /** Verified-link tether — text that must keep existing in the file. */
1738
- locator: z8.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional(),
1744
+ locator: z9.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional(),
1739
1745
  /** test = must appear in a real test/describe title; code = any substring. */
1740
- locatorType: z8.enum(["test", "code"]).optional()
1746
+ locatorType: z9.enum(["test", "code"]).optional()
1741
1747
  }).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
1742
1748
  message: "locator and locatorType must be provided together"
1743
1749
  }).refine((link) => link.locator === void 0 || link.type !== "folder", {
1744
1750
  message: "folder links cannot carry a locator"
1745
1751
  });
1746
- var hexColor = z8.string().regex(/^#[0-9a-fA-F]{6}$/, "Expected #RRGGBB hex color");
1747
- var overviewPathSchema = z8.string().min(1).max(500).regex(/^[^\r\n]*$/, "Overview path cannot contain line breaks");
1748
- var CreateProjectTagRequestSchema = z8.object({
1749
- projectId: z8.string(),
1750
- name: z8.string().min(1).max(50),
1752
+ var hexColor = z9.string().regex(/^#[0-9a-fA-F]{6}$/, "Expected #RRGGBB hex color");
1753
+ var overviewPathSchema = z9.string().min(1).max(500).regex(/^[^\r\n]*$/, "Overview path cannot contain line breaks");
1754
+ var CreateProjectTagRequestSchema = z9.object({
1755
+ projectId: z9.string(),
1756
+ name: z9.string().min(1).max(50),
1751
1757
  color: hexColor.optional(),
1752
- description: z8.string().max(TAG_DESCRIPTION_MAX).optional(),
1753
- overview: z8.string().max(TAG_OVERVIEW_MAX).optional(),
1758
+ description: z9.string().max(TAG_DESCRIPTION_MAX).optional(),
1759
+ overview: z9.string().max(TAG_OVERVIEW_MAX).optional(),
1754
1760
  /** Source the overview from this repo file (stored overview stays as the pending fallback). */
1755
1761
  overviewPath: overviewPathSchema.optional(),
1756
- contextPaths: z8.array(ProjectTagContextPathSchema).max(20).optional(),
1762
+ contextPaths: z9.array(ProjectTagContextPathSchema).max(20).optional(),
1757
1763
  /** Parents to link at create time (multi-parent DAG). */
1758
- parentTagIds: z8.array(z8.string()).max(25).optional(),
1759
- requestingUserId: z8.string().optional()
1764
+ parentTagIds: z9.array(z9.string()).max(25).optional(),
1765
+ requestingUserId: z9.string().optional()
1760
1766
  });
1761
- var UpdateProjectTagRequestSchema = z8.object({
1762
- projectId: z8.string(),
1763
- tagId: z8.string(),
1764
- name: z8.string().min(1).max(50).optional(),
1767
+ var UpdateProjectTagRequestSchema = z9.object({
1768
+ projectId: z9.string(),
1769
+ tagId: z9.string(),
1770
+ name: z9.string().min(1).max(50).optional(),
1765
1771
  color: hexColor.optional(),
1766
- description: z8.string().max(TAG_DESCRIPTION_MAX).optional(),
1772
+ description: z9.string().max(TAG_DESCRIPTION_MAX).optional(),
1767
1773
  /** Full markdown glossary body; null clears it. Rejected while overviewPath is set. */
1768
- overview: z8.string().max(TAG_OVERVIEW_MAX).nullable().optional(),
1774
+ overview: z9.string().max(TAG_OVERVIEW_MAX).nullable().optional(),
1769
1775
  /** Repo file to source the overview from; null clears back to the stored overview. */
1770
1776
  overviewPath: overviewPathSchema.nullable().optional(),
1771
1777
  /** Full replacement of the tag's context links when provided. */
1772
- contextPaths: z8.array(ProjectTagContextPathSchema).max(20).optional(),
1778
+ contextPaths: z9.array(ProjectTagContextPathSchema).max(20).optional(),
1773
1779
  /** Full-set replacement of the tag's parent tags (multi-parent DAG). */
1774
- parentTagIds: z8.array(z8.string()).max(25).optional(),
1780
+ parentTagIds: z9.array(z9.string()).max(25).optional(),
1775
1781
  /** One-line revision provenance, recorded in the tag's history. */
1776
- reason: z8.string().max(TAG_REASON_MAX).optional(),
1782
+ reason: z9.string().max(TAG_REASON_MAX).optional(),
1777
1783
  /** Card the caller was working in — stamped into the revision history. */
1778
- taskId: z8.string().optional(),
1779
- requestingUserId: z8.string().optional()
1784
+ taskId: z9.string().optional(),
1785
+ requestingUserId: z9.string().optional()
1780
1786
  });
1781
- var PostToProjectChatRequestSchema = z8.object({
1782
- projectId: z8.string(),
1783
- content: z8.string().min(1).max(2e4),
1784
- requestingUserId: z8.string().optional(),
1787
+ var PostToProjectChatRequestSchema = z9.object({
1788
+ projectId: z9.string(),
1789
+ content: z9.string().min(1).max(2e4),
1790
+ requestingUserId: z9.string().optional(),
1785
1791
  /** Marks the post so the server can persist it beyond chat (tag-audit summaries land in tag history). */
1786
- kind: z8.enum(["tag_audit_summary"]).optional()
1787
- });
1788
- var StartTagAuditRequestSchema = z8.object({
1789
- projectId: z8.string(),
1790
- requestingUserId: z8.string().optional()
1791
- });
1792
- var StartTaskAuditRequestSchema = z8.object({
1793
- projectId: z8.string(),
1794
- taskIds: z8.array(z8.string()).min(1).max(20),
1795
- requestingUserId: z8.string().optional()
1796
- });
1797
- var GetActiveAuditSessionsRequestSchema = z8.object({
1798
- projectId: z8.string()
1799
- });
1800
- var ReportTaskAuditResultRequestSchema = z8.object({
1801
- projectId: z8.string(),
1802
- taskId: z8.string(),
1803
- summary: z8.string(),
1804
- turnGrades: z8.array(
1805
- z8.object({
1806
- turnIndex: z8.number(),
1807
- phase: z8.enum(["planning", "building", "human"]),
1808
- grade: z8.enum(["correct", "neutral", "blunder"]),
1809
- reasoning: z8.string(),
1810
- eventType: z8.string(),
1811
- eventSummary: z8.string()
1792
+ kind: z9.enum(["tag_audit_summary"]).optional()
1793
+ });
1794
+ var StartTagAuditRequestSchema = z9.object({
1795
+ projectId: z9.string(),
1796
+ requestingUserId: z9.string().optional()
1797
+ });
1798
+ var StartTaskAuditRequestSchema = z9.object({
1799
+ projectId: z9.string(),
1800
+ taskIds: z9.array(z9.string()).min(1).max(20),
1801
+ requestingUserId: z9.string().optional()
1802
+ });
1803
+ var GetActiveAuditSessionsRequestSchema = z9.object({
1804
+ projectId: z9.string()
1805
+ });
1806
+ var ReportTaskAuditResultRequestSchema = z9.object({
1807
+ projectId: z9.string(),
1808
+ taskId: z9.string(),
1809
+ summary: z9.string(),
1810
+ turnGrades: z9.array(
1811
+ z9.object({
1812
+ turnIndex: z9.number(),
1813
+ phase: z9.enum(["planning", "building", "human"]),
1814
+ grade: z9.enum(["correct", "neutral", "blunder"]),
1815
+ reasoning: z9.string(),
1816
+ eventType: z9.string(),
1817
+ eventSummary: z9.string()
1812
1818
  })
1813
1819
  ),
1814
- planningAccuracy: z8.number().nullable(),
1815
- buildingAccuracy: z8.number().nullable(),
1816
- humanAccuracy: z8.number().nullable(),
1817
- planningCorrect: z8.number(),
1818
- planningNeutral: z8.number(),
1819
- planningBlunder: z8.number(),
1820
- buildingCorrect: z8.number(),
1821
- buildingNeutral: z8.number(),
1822
- buildingBlunder: z8.number(),
1823
- humanCorrect: z8.number(),
1824
- humanNeutral: z8.number(),
1825
- humanBlunder: z8.number(),
1826
- humanEvaluations: z8.array(
1827
- z8.object({
1828
- messageIndex: z8.number(),
1829
- rating: z8.union([z8.literal(-1), z8.literal(0), z8.literal(1)]),
1830
- reasoning: z8.string()
1820
+ planningAccuracy: z9.number().nullable(),
1821
+ buildingAccuracy: z9.number().nullable(),
1822
+ humanAccuracy: z9.number().nullable(),
1823
+ planningCorrect: z9.number(),
1824
+ planningNeutral: z9.number(),
1825
+ planningBlunder: z9.number(),
1826
+ buildingCorrect: z9.number(),
1827
+ buildingNeutral: z9.number(),
1828
+ buildingBlunder: z9.number(),
1829
+ humanCorrect: z9.number(),
1830
+ humanNeutral: z9.number(),
1831
+ humanBlunder: z9.number(),
1832
+ humanEvaluations: z9.array(
1833
+ z9.object({
1834
+ messageIndex: z9.number(),
1835
+ rating: z9.union([z9.literal(-1), z9.literal(0), z9.literal(1)]),
1836
+ reasoning: z9.string()
1831
1837
  })
1832
1838
  ).optional(),
1833
- suggestionIds: z8.array(z8.string()),
1834
- auditCostUsd: z8.number().nullable(),
1835
- model: z8.string().nullable(),
1839
+ suggestionIds: z9.array(z9.string()),
1840
+ auditCostUsd: z9.number().nullable(),
1841
+ model: z9.string().nullable(),
1836
1842
  /** When set, the audit is marked failed with this message instead. */
1837
- error: z8.string().optional()
1843
+ error: z9.string().optional()
1838
1844
  });
1839
- var GetTaskAuditsRequestSchema = z8.object({
1840
- projectId: z8.string(),
1841
- limit: z8.number().int().positive().max(200).optional().default(50)
1845
+ var GetTaskAuditsRequestSchema = z9.object({
1846
+ projectId: z9.string(),
1847
+ limit: z9.number().int().positive().max(200).optional().default(50)
1842
1848
  });
1843
- var GetTaskAuditRequestSchema = z8.object({
1844
- projectId: z8.string(),
1845
- auditId: z8.string()
1849
+ var GetTaskAuditRequestSchema = z9.object({
1850
+ projectId: z9.string(),
1851
+ auditId: z9.string()
1846
1852
  });
1847
- var GetTaskAuditAggregatesRequestSchema = z8.object({
1848
- projectId: z8.string()
1853
+ var GetTaskAuditAggregatesRequestSchema = z9.object({
1854
+ projectId: z9.string()
1849
1855
  });
1850
- var DeleteTaskAuditRequestSchema = z8.object({
1851
- projectId: z8.string(),
1852
- auditId: z8.string(),
1853
- requestingUserId: z8.string().optional()
1856
+ var DeleteTaskAuditRequestSchema = z9.object({
1857
+ projectId: z9.string(),
1858
+ auditId: z9.string(),
1859
+ requestingUserId: z9.string().optional()
1854
1860
  });
1855
- var MarkInitialPromptSubmittedRequestSchema = z8.object({
1856
- sessionId: z8.string()
1861
+ var MarkInitialPromptSubmittedRequestSchema = z9.object({
1862
+ sessionId: z9.string()
1857
1863
  });
1858
1864
  var CRITICAL_AUTOMATED_SOURCES = /* @__PURE__ */ new Set([
1859
1865
  "ci_failure",
@@ -1871,47 +1877,139 @@ var DEFAULT_PACK_EXECUTION = "single-pod";
1871
1877
  function parsePackExecution(value) {
1872
1878
  return PACK_EXECUTIONS.includes(value ?? "") ? value : DEFAULT_PACK_EXECUTION;
1873
1879
  }
1880
+ var MEETING_CHECKLIST_TITLE_MAX = 300;
1874
1881
  var MEETING_TRANSCRIPT_MAX_CHARS = 2e6;
1875
1882
  var MEETING_TITLE_MAX = 200;
1876
- var CreateMeetingFromTranscriptRequestSchema = z9.object({
1877
- projectId: z9.string().cuid(),
1878
- rawText: z9.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
1879
- title: z9.string().min(1).max(MEETING_TITLE_MAX).optional(),
1883
+ var MEETING_OCCURRED_AT_MIN_YEAR = 2e3;
1884
+ var MEETING_OCCURRED_AT_MAX_FUTURE_MS = 48 * 60 * 60 * 1e3;
1885
+ var OCCURRED_AT_RANGE_MESSAGE = `occurredAt must be a real date: no earlier than ${MEETING_OCCURRED_AT_MIN_YEAR}, and no more than 48 hours in the future.`;
1886
+ var MeetingOccurredAtSchema = z10.string().datetime().refine((value) => {
1887
+ const ms = Date.parse(value);
1888
+ if (Number.isNaN(ms)) return false;
1889
+ if (ms > Date.now() + MEETING_OCCURRED_AT_MAX_FUTURE_MS) return false;
1890
+ return new Date(ms).getUTCFullYear() >= MEETING_OCCURRED_AT_MIN_YEAR;
1891
+ }, OCCURRED_AT_RANGE_MESSAGE);
1892
+ var CreateMeetingFromTranscriptRequestSchema = z10.object({
1893
+ projectId: z10.string().cuid(),
1894
+ rawText: z10.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
1895
+ title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
1880
1896
  /** ISO 8601. Defaults to now when the source carries no date. */
1881
- occurredAt: z9.string().datetime().optional(),
1897
+ occurredAt: MeetingOccurredAtSchema.optional(),
1882
1898
  /** Override auto-detection. Rarely needed; detection handles the three formats. */
1883
- format: z9.enum(["text", "vtt", "srt"]).optional(),
1884
- source: z9.enum(["manual", "slack"]).optional()
1885
- });
1886
- var GetMeetingRequestSchema = z9.object({
1887
- projectId: z9.string().cuid(),
1888
- meetingId: z9.string().cuid()
1889
- });
1890
- var UpdateMeetingRequestSchema = z9.object({
1891
- projectId: z9.string().cuid(),
1892
- meetingId: z9.string().cuid(),
1893
- title: z9.string().min(1).max(MEETING_TITLE_MAX).optional(),
1894
- occurredAt: z9.string().datetime().optional()
1895
- });
1896
- var RegenerateMeetingSummaryRequestSchema = z9.object({
1897
- projectId: z9.string().cuid(),
1898
- meetingId: z9.string().cuid()
1899
- });
1900
- var DeleteMeetingRequestSchema = z9.object({
1901
- projectId: z9.string().cuid(),
1902
- meetingId: z9.string().cuid()
1903
- });
1904
- var ListMeetingsRequestSchema = z9.object({
1905
- projectId: z9.string().cuid(),
1906
- limit: z9.number().int().min(1).max(50).optional(),
1907
- search: z9.string().max(200).optional()
1908
- });
1909
- var ReadMeetingTranscriptRequestSchema = z9.object({
1910
- projectId: z9.string().cuid(),
1911
- meetingId: z9.string().cuid(),
1912
- offset: z9.number().int().min(0).optional(),
1913
- limit: z9.number().int().min(1).max(500).optional()
1899
+ format: z10.enum(["text", "vtt", "srt"]).optional(),
1900
+ source: z10.enum(["manual", "slack"]).optional()
1901
+ });
1902
+ var GetMeetingRequestSchema = z10.object({
1903
+ projectId: z10.string().cuid(),
1904
+ meetingId: z10.string().cuid()
1905
+ });
1906
+ var UpdateMeetingRequestSchema = z10.object({
1907
+ projectId: z10.string().cuid(),
1908
+ meetingId: z10.string().cuid(),
1909
+ title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
1910
+ occurredAt: MeetingOccurredAtSchema.optional()
1911
+ });
1912
+ var RegenerateMeetingSummaryRequestSchema = z10.object({
1913
+ projectId: z10.string().cuid(),
1914
+ meetingId: z10.string().cuid()
1915
+ });
1916
+ var DeleteMeetingRequestSchema = z10.object({
1917
+ projectId: z10.string().cuid(),
1918
+ meetingId: z10.string().cuid()
1919
+ });
1920
+ var checklistTitle = z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX);
1921
+ var ListMeetingChecklistRequestSchema = z10.object({
1922
+ projectId: z10.string().cuid(),
1923
+ meetingId: z10.string().cuid()
1924
+ });
1925
+ var AddMeetingChecklistItemsRequestSchema = z10.object({
1926
+ projectId: z10.string().cuid(),
1927
+ meetingId: z10.string().cuid(),
1928
+ items: z10.array(z10.object({ title: checklistTitle })).min(1).max(50)
1929
+ });
1930
+ var UpdateMeetingChecklistItemRequestSchema = z10.object({
1931
+ projectId: z10.string().cuid(),
1932
+ meetingId: z10.string().cuid(),
1933
+ itemId: z10.string().cuid(),
1934
+ title: checklistTitle.optional(),
1935
+ ordinal: z10.number().int().min(0).optional(),
1936
+ /** Explicit null clears the link; undefined leaves it alone. */
1937
+ linkedTaskId: z10.string().cuid().nullable().optional()
1938
+ }).refine(
1939
+ (v) => v.title !== void 0 || v.ordinal !== void 0 || v.linkedTaskId !== void 0,
1940
+ "Pass at least one of title, ordinal, or linkedTaskId."
1941
+ );
1942
+ var DeleteMeetingChecklistItemRequestSchema = z10.object({
1943
+ projectId: z10.string().cuid(),
1944
+ meetingId: z10.string().cuid(),
1945
+ itemId: z10.string().cuid()
1946
+ });
1947
+ var SetMeetingChecklistItemCheckedRequestSchema = z10.object({
1948
+ projectId: z10.string().cuid(),
1949
+ meetingId: z10.string().cuid(),
1950
+ itemId: z10.string().cuid(),
1951
+ checked: z10.boolean(),
1952
+ /** Attach the card in the same call that ticks the item. */
1953
+ linkedTaskId: z10.string().cuid().nullable().optional()
1954
+ });
1955
+ var ListMeetingsRequestSchema = z10.object({
1956
+ projectId: z10.string().cuid(),
1957
+ limit: z10.number().int().min(1).max(50).optional(),
1958
+ search: z10.string().max(200).optional()
1959
+ });
1960
+ var ReadMeetingTranscriptRequestSchema = z10.object({
1961
+ projectId: z10.string().cuid(),
1962
+ meetingId: z10.string().cuid(),
1963
+ offset: z10.number().int().min(0).optional(),
1964
+ limit: z10.number().int().min(1).max(500).optional()
1965
+ });
1966
+ var MEETING_SUMMARY_MAX_CHARS = 5e4;
1967
+ var AddProjectMeetingChecklistItemsRequestSchema = z10.object({
1968
+ projectId: z10.string().cuid(),
1969
+ meetingId: z10.string().cuid(),
1970
+ items: z10.array(z10.object({ title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX) })).min(1).max(50),
1971
+ requestingUserId: z10.string().optional()
1972
+ });
1973
+ var CheckProjectMeetingChecklistItemRequestSchema = z10.object({
1974
+ projectId: z10.string().cuid(),
1975
+ meetingId: z10.string().cuid(),
1976
+ title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1977
+ checked: z10.boolean(),
1978
+ /** Card id or slug. Resolved server-side and required to be in the project. */
1979
+ linkedTask: z10.string().min(1).optional(),
1980
+ requestingUserId: z10.string().optional()
1981
+ });
1982
+ var EditProjectMeetingChecklistItemRequestSchema = z10.object({
1983
+ projectId: z10.string().cuid(),
1984
+ meetingId: z10.string().cuid(),
1985
+ title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1986
+ newTitle: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1987
+ requestingUserId: z10.string().optional()
1988
+ });
1989
+ var RemoveProjectMeetingChecklistItemRequestSchema = z10.object({
1990
+ projectId: z10.string().cuid(),
1991
+ meetingId: z10.string().cuid(),
1992
+ title: z10.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
1993
+ requestingUserId: z10.string().optional()
1994
+ });
1995
+ var CreateProjectMeetingRequestSchema = z10.object({
1996
+ projectId: z10.string().cuid(),
1997
+ rawText: z10.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
1998
+ title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
1999
+ occurredAt: MeetingOccurredAtSchema.optional(),
2000
+ requestingUserId: z10.string().optional()
1914
2001
  });
2002
+ var UpdateProjectMeetingRequestSchema = z10.object({
2003
+ projectId: z10.string().cuid(),
2004
+ meetingId: z10.string().cuid(),
2005
+ title: z10.string().min(1).max(MEETING_TITLE_MAX).optional(),
2006
+ occurredAt: MeetingOccurredAtSchema.optional(),
2007
+ summary: z10.string().min(1).max(MEETING_SUMMARY_MAX_CHARS).optional(),
2008
+ requestingUserId: z10.string().optional()
2009
+ }).refine(
2010
+ (v) => v.title !== void 0 || v.occurredAt !== void 0 || v.summary !== void 0,
2011
+ "Pass at least one of title, occurredAt, or summary."
2012
+ );
1915
2013
  var AGENT_STATUS_REASON_USER_QUESTION = "user_question";
1916
2014
  var TASK_CHAT_HISTORY_LIMIT = 20;
1917
2015
  var PM_CHAT_HISTORY_LIMIT = 40;
@@ -1954,6 +2052,9 @@ When you write prose a person will read \u2014 chat messages, plan updates, PR t
1954
2052
  - Define a technical term on first use when a non-engineer will read the message.
1955
2053
  - Never drop a condition, number, or scope qualifier to shorten a sentence. Precision beats brevity.
1956
2054
  These rules do NOT apply to code, code comments, commit messages, or quoted output.`;
2055
+ var CLAUDESPACE_WORKLOAD_LABEL = "rc-workload";
2056
+ var CLAUDESPACE_WORKLOAD_VALUE = "claudespace";
2057
+ var CONVEYOR_POD_SELECTOR = `${CLAUDESPACE_WORKLOAD_LABEL}=${CLAUDESPACE_WORKLOAD_VALUE}`;
1957
2058
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
1958
2059
  var MENTION_TOKEN_REGEX = /@\[(\w+):([^\]]+)\]/g;
1959
2060
  function parseMentions(content) {
@@ -2495,7 +2596,7 @@ function defineTool(name, description, schema, handler, options) {
2495
2596
 
2496
2597
  // src/harness/claude-code/index.ts
2497
2598
  import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
2498
- import { z as z10 } from "zod";
2599
+ import { z as z11 } from "zod";
2499
2600
  var ClaudeCodeHarness = class {
2500
2601
  /** The SDK stream is itself the structured-event source. */
2501
2602
  emitsStructuredEvents = true;
@@ -2528,7 +2629,7 @@ var ClaudeCodeHarness = class {
2528
2629
  }
2529
2630
  );
2530
2631
  if (t.strict) {
2531
- sdkTool.inputSchema = z10.strictObject(t.schema);
2632
+ sdkTool.inputSchema = z11.strictObject(t.schema);
2532
2633
  }
2533
2634
  return sdkTool;
2534
2635
  });
@@ -3096,6 +3197,7 @@ function mapAssistant(record) {
3096
3197
  };
3097
3198
  const usage = mapUsage(message);
3098
3199
  if (usage !== void 0) result.message.usage = usage;
3200
+ if (record.isApiErrorMessage === true) result.isApiError = true;
3099
3201
  return result;
3100
3202
  }
3101
3203
  function mapResultSuccess(record) {
@@ -3786,7 +3888,7 @@ var PtyOutputCoalescer = class {
3786
3888
 
3787
3889
  // src/harness/pty/tool-server.ts
3788
3890
  import { createServer as createServer2 } from "http";
3789
- import { z as z11 } from "zod";
3891
+ import { z as z12 } from "zod";
3790
3892
  import { writeFile as writeFile3 } from "fs/promises";
3791
3893
  import { join as join3 } from "path";
3792
3894
  import { randomBytes } from "crypto";
@@ -3843,7 +3945,7 @@ var PtyToolServer = class {
3843
3945
  const mcp = new McpServer({ name: this.name, version: "1.0.0" });
3844
3946
  const register = mcp.registerTool.bind(mcp);
3845
3947
  for (const tool2 of this.tools) {
3846
- const inputSchema = tool2.strict ? z11.strictObject(tool2.schema) : tool2.schema;
3948
+ const inputSchema = tool2.strict ? z12.strictObject(tool2.schema) : tool2.schema;
3847
3949
  register(
3848
3950
  tool2.name,
3849
3951
  {
@@ -6799,9 +6901,7 @@ function buildChatInstructions(context, scenario, newMessages) {
6799
6901
  const parts = [];
6800
6902
  if (scenario === "fresh") {
6801
6903
  parts.push(
6802
- `Read the chat history above and respond to what the team asked.`,
6803
- `Reply with post_to_chat \u2014 your turn output is NOT shown in chat, so post_to_chat is the only way the team sees your answer.`,
6804
- `Ask clarifying questions when they help. This is a conversation, not an autonomous build.`
6904
+ `The user's message is above. Do what it asks and reply with post_to_chat \u2014 your turn output is NOT shown in chat, so post_to_chat is the only way the user sees your answer.`
6805
6905
  );
6806
6906
  } else if (userMessages.length > 0) {
6807
6907
  parts.push(
@@ -6818,17 +6918,6 @@ Respond to them with post_to_chat (your turn output is NOT shown in chat).`
6818
6918
  `Post a brief status update with post_to_chat if you still owe the team a response, then wait for them.`
6819
6919
  );
6820
6920
  }
6821
- if (!context.plan?.trim()) {
6822
- parts.push(
6823
- `
6824
- If this conversation turns into a development task: save a plan with update_task first \u2014 that identifies the card and moves it to In Progress \u2014 then implement it and open a PR with mcp__conveyor__create_pull_request, like a normal build.`
6825
- );
6826
- return parts;
6827
- }
6828
- parts.push(
6829
- `
6830
- This card has a saved plan, so it is a development task now. Implement it on the git branch "${context.githubBranch}", then run the \`/conveyor-build\` skill and follow it \u2014 the same workflow any build on this card would use, including how to verify before opening the PR.`
6831
- );
6832
6921
  if (context.githubPRUrl) {
6833
6922
  parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
6834
6923
  }
@@ -7461,31 +7550,20 @@ function buildModePrompt(agentMode, context, runnerMode) {
7461
7550
  }
7462
7551
  }
7463
7552
  function buildChatPrompt(context) {
7464
- const base = context?.baseBranch?.trim() || "dev";
7553
+ const branch = context?.githubBranch?.trim();
7554
+ const gitScope = branch ? ` on branch \`${branch}\`` : "";
7465
7555
  return [
7466
7556
  `
7467
- ## Mode: Chat`,
7468
- `You are in Chat mode \u2014 a conversational assistant working directly with the user on this card.`,
7469
- `- Respond conversationally to the user in chat. Ask clarifying questions when useful; this is a back-and-forth, not an autonomous build.`,
7470
- `- You have full read/write access to the workspace and can run non-destructive shell commands, so you CAN create files (notes, scripts, docs, data, diagrams, etc.) when they help the user.`,
7471
- `- This card starts Unidentified. That is correct \u2014 it stays a conversation until the work becomes a development task (see below).`,
7472
- ``,
7473
- `### Deliverables \u2014 attach files to the card`,
7474
- `- When you create a file the user should keep, attach it to the card with the \`upload_attachment\` tool so it shows up on the card. Mention in chat what you attached.`,
7475
- `- \`upload_attachment\` accepts any file type (markdown, PDF, HTML, text, data, images) up to 25MB \u2014 this is how docs are delivered. Do NOT publish deliverables as a Claude artifact or any other off-platform link; the card is the system of record.`,
7476
- `- For conversational work, attachments are the deliverable. Do NOT open a PR to hand over a document or an answer.`,
7557
+ ## Mode: Interactive`,
7558
+ `This is a direct Claude Code session on this card. The user drives it message by message; you have the same access you would have running Claude Code locally in this repo (full read/write, shell, git${gitScope}).`,
7559
+ `- Your turn output is NOT shown in chat \u2014 reply with \`post_to_chat\`.`,
7560
+ `- There is no required Conveyor workflow here. Do what the message asks: answer, investigate, write files, build, or open a PR. Skills such as \`/conveyor-plan\` and \`/conveyor-build\` are available if the user asks for them or they clearly help; nothing runs them for you.`,
7477
7561
  ``,
7478
- `### Turning into a development task`,
7479
- `- If the conversation becomes a request to change the code, escalate the card first: save a plan with \`update_task\`. That first plan identifies the card and moves it to In Progress.`,
7480
- `- The plan is also the gate on your PR path: \`create_pull_request\`, \`git push\` and \`gh pr\` are DENIED until a plan is saved, and allowed afterwards. Do not try to work around a denial \u2014 save the plan.`,
7481
- `- Once the plan is saved, it IS a normal build: run the \`/conveyor-build\` skill and follow it, including how to sync \`${base}\` and verify before opening the PR. Do not re-derive that workflow here.`,
7482
- `- Keep talking to the user while you build \u2014 this is still their card.`,
7483
- ``,
7484
- `### Finishing the conversation`,
7485
- `- When the user indicates they are done (or explicitly asks to wrap up / close the card), call \`force_update_task_status\` with status \`"Complete"\`. It works from either status, so a card still sitting Unidentified can be completed the same way as one that reached In Progress.`,
7486
- `- If you opened a PR, do NOT complete the card \u2014 the PR review flow takes it from there.`,
7487
- `- If the user is still engaged, keep helping \u2014 only complete the card once the interaction has concluded.`,
7488
- `- Do not complete the card while you still owe the user a response or an attachment.`
7562
+ `### Card facts`,
7563
+ `- This card starts Unidentified and stays so until a plan is saved with \`update_task\`; that first plan identifies it and moves it to In Progress.`,
7564
+ `- Files the user should keep go on the card via \`upload_attachment\` (any file type, up to 25MB), not an off-platform link.`,
7565
+ `- \`create_pull_request\` opens the PR for this card and moves it to Review PR. Save a short plan first so the card is identified \u2014 a record, not an approval gate.`,
7566
+ `- When the user says they are done, call \`force_update_task_status\` with status \`"Complete"\`. Not if a PR is open \u2014 the review flow takes it from there.`
7489
7567
  ].join("\n");
7490
7568
  }
7491
7569
  function buildReviewTagSection(context) {
@@ -8346,7 +8424,7 @@ async function buildInitialPrompt(mode, context, isAuto, agentMode, packExecutio
8346
8424
  }
8347
8425
 
8348
8426
  // src/tools/task-context-tools.ts
8349
- import { z as z13 } from "zod";
8427
+ import { z as z14 } from "zod";
8350
8428
 
8351
8429
  // ../shared/dist/tool-contracts/index.js
8352
8430
  var f = {
@@ -8375,14 +8453,14 @@ var f = {
8375
8453
  return { kind: "nullable", inner };
8376
8454
  }
8377
8455
  };
8378
- function compileString(z20, spec) {
8379
- let schema = z20.string();
8456
+ function compileString(z21, spec) {
8457
+ let schema = z21.string();
8380
8458
  if (spec.min !== void 0) schema = schema.min(spec.min);
8381
8459
  if (spec.max !== void 0) schema = schema.max(spec.max);
8382
8460
  return schema;
8383
8461
  }
8384
- function compileNumber(z20, spec) {
8385
- let schema = z20.number();
8462
+ function compileNumber(z21, spec) {
8463
+ let schema = z21.number();
8386
8464
  if (spec.int) schema = schema.int();
8387
8465
  if (spec.positive) schema = schema.positive();
8388
8466
  if (spec.nonnegative) schema = schema.nonnegative();
@@ -8390,49 +8468,49 @@ function compileNumber(z20, spec) {
8390
8468
  if (spec.max !== void 0) schema = schema.max(spec.max);
8391
8469
  return schema;
8392
8470
  }
8393
- function compileArray(z20, spec) {
8394
- let schema = z20.array(compileField(z20, spec.item));
8471
+ function compileArray(z21, spec) {
8472
+ let schema = z21.array(compileField(z21, spec.item));
8395
8473
  if (spec.min !== void 0) schema = schema.min(spec.min);
8396
8474
  return schema;
8397
8475
  }
8398
- function compileBase(z20, spec) {
8476
+ function compileBase(z21, spec) {
8399
8477
  switch (spec.kind) {
8400
8478
  case "string":
8401
- return compileString(z20, spec);
8479
+ return compileString(z21, spec);
8402
8480
  case "number":
8403
- return compileNumber(z20, spec);
8481
+ return compileNumber(z21, spec);
8404
8482
  case "boolean":
8405
- return z20.boolean();
8483
+ return z21.boolean();
8406
8484
  case "enum":
8407
- return z20.enum([...spec.values]);
8485
+ return z21.enum([...spec.values]);
8408
8486
  case "array":
8409
- return compileArray(z20, spec);
8487
+ return compileArray(z21, spec);
8410
8488
  case "object":
8411
- return z20.object(compileShape(z20, spec.fields));
8489
+ return z21.object(compileShape(z21, spec.fields));
8412
8490
  }
8413
8491
  }
8414
8492
  function descriptionOf(spec) {
8415
8493
  if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
8416
8494
  return spec.desc;
8417
8495
  }
8418
- function compileUndescribed(z20, spec) {
8496
+ function compileUndescribed(z21, spec) {
8419
8497
  if (spec.kind === "optional") {
8420
- return compileUndescribed(z20, spec.inner).optional();
8498
+ return compileUndescribed(z21, spec.inner).optional();
8421
8499
  }
8422
8500
  if (spec.kind === "nullable") {
8423
- return compileUndescribed(z20, spec.inner).nullable();
8501
+ return compileUndescribed(z21, spec.inner).nullable();
8424
8502
  }
8425
- return compileBase(z20, spec);
8503
+ return compileBase(z21, spec);
8426
8504
  }
8427
- function compileField(z20, spec) {
8428
- const schema = compileUndescribed(z20, spec);
8505
+ function compileField(z21, spec) {
8506
+ const schema = compileUndescribed(z21, spec);
8429
8507
  const desc = descriptionOf(spec);
8430
8508
  return desc === void 0 ? schema : schema.describe(desc);
8431
8509
  }
8432
- function compileShape(z20, fields) {
8510
+ function compileShape(z21, fields) {
8433
8511
  const shape = {};
8434
8512
  for (const [key, spec] of Object.entries(fields)) {
8435
- shape[key] = compileField(z20, spec);
8513
+ shape[key] = compileField(z21, spec);
8436
8514
  }
8437
8515
  return shape;
8438
8516
  }
@@ -9013,7 +9091,7 @@ var createSubtaskContract = defineToolContract({
9013
9091
  ),
9014
9092
  tags: f.optional(
9015
9093
  f.array(f.string(), {
9016
- desc: 'Tag names to assign to the subtask (e.g. ["refactor"]). Unknown names are rejected \u2014 create the tag first with manage_tags. Use list_tags to see available tags.'
9094
+ desc: 'Tag names to assign to the subtask (e.g. ["refactor"]). Unknown names are rejected before the subtask is created, so nothing is written and a corrected retry creates exactly one subtask. Create the tag first with manage_tags. Use list_tags to see available tags.'
9017
9095
  })
9018
9096
  )
9019
9097
  }
@@ -9606,10 +9684,149 @@ var readMeetingTranscriptContract = defineToolContract({
9606
9684
  }
9607
9685
  }
9608
9686
  });
9687
+ var RAW_TEXT_DESC = "The transcript itself. Plain text with `Speaker: line` labels, or a WebVTT/SRT file's contents \u2014 the format is detected, and the speaker labels become the participant list. Paste the whole thing; it is stored as the meeting's record.";
9688
+ var OCCURRED_AT_DESC = "When the meeting happened, ISO 8601 (e.g. 2026-09-02T15:00:00Z). Defaults to now. Must be a real date \u2014 no earlier than 2000, and no more than 48 hours ahead.";
9689
+ var TITLE_DESC = "Meeting title. Defaults to `Meeting <date>` when you do not name one.";
9690
+ var createMeetingContract = defineToolContract({
9691
+ name: "create_meeting",
9692
+ agent: {
9693
+ description: `File a transcript as a project meeting. Use this when you have the text of a call \u2014 a paste, an export, notes captured elsewhere \u2014 and it belongs in the project's record. The transcript is parsed into speaker segments and an AI summary is written straight after, so the meeting reads "processing" for a few seconds before its overview appears. This creates a MEETING, not cards: turning what was decided into work is still create_task's job.`,
9694
+ fields: {
9695
+ rawText: f.string({ desc: RAW_TEXT_DESC, min: 1 }),
9696
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9697
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC }))
9698
+ }
9699
+ },
9700
+ mcp: {
9701
+ description: `File a transcript as a project meeting. The text is parsed into speaker segments and summarized automatically, so the meeting reports status "processing" briefly before its overview lands. Creates a meeting only \u2014 use the card tools to turn decisions into work. ${MCP_TAIL}`,
9702
+ fields: {
9703
+ projectId: mcpProjectId,
9704
+ rawText: f.string({ desc: RAW_TEXT_DESC, min: 1 }),
9705
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9706
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC }))
9707
+ }
9708
+ }
9709
+ });
9710
+ var SUMMARY_DESC = "Replace the meeting's overview with this text. Markdown, and it is what every reader sees from then on \u2014 write the whole overview, not a note about it. Writing a summary marks the meeting ready, so a later regenerate is the only thing that overwrites it.";
9711
+ var updateMeetingContract = defineToolContract({
9712
+ name: "update_meeting",
9713
+ agent: {
9714
+ description: `Correct a meeting's title or date, or replace its AI summary with a better one. This is how a rewritten overview actually persists \u2014 read the transcript, write the summary you want, and pass it here. Pass at least one field; the ones you leave out are untouched.`,
9715
+ fields: {
9716
+ meetingId: f.string({ desc: MEETING_ID }),
9717
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9718
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC })),
9719
+ summary: f.optional(f.string({ desc: SUMMARY_DESC, min: 1 }))
9720
+ }
9721
+ },
9722
+ mcp: {
9723
+ description: `Correct a meeting's title or date, or replace its AI summary with a rewritten one. Pass at least one field; omitted fields are left alone. ${MCP_TAIL}`,
9724
+ fields: {
9725
+ projectId: mcpProjectId,
9726
+ meetingId: f.string({ desc: MEETING_ID }),
9727
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9728
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC })),
9729
+ summary: f.optional(f.string({ desc: SUMMARY_DESC, min: 1 }))
9730
+ }
9731
+ }
9732
+ });
9733
+ var CHECKLIST_ITEM_TITLE = "The item's exact title, as get_meeting lists it. Matched case-insensitively.";
9734
+ var CHECKLIST_INTRO = "A meeting's checklist is its follow-ups: one line each, ticked when done, optionally pointing at the Conveyor card that carries the work. The AI summary's proposed next steps are seeded here automatically.";
9735
+ var addMeetingChecklistItemsContract = defineToolContract({
9736
+ name: "add_meeting_checklist_items",
9737
+ agent: {
9738
+ description: `Add follow-up items to a meeting's checklist. ${CHECKLIST_INTRO} Use this for a step the summary missed, or one that came out of a later conversation. Items whose title already exists are skipped, so re-running this is safe.`,
9739
+ fields: {
9740
+ meetingId: f.string({ desc: MEETING_ID }),
9741
+ items: f.array(
9742
+ f.object({ title: f.string({ desc: "One follow-up, as a single line.", min: 1 }) }),
9743
+ { desc: "The items to add, in the order they should appear." }
9744
+ )
9745
+ }
9746
+ },
9747
+ mcp: {
9748
+ description: `Add follow-up items to a meeting's checklist. ${CHECKLIST_INTRO} Existing titles are skipped, so re-running is safe. ${MCP_TAIL}`,
9749
+ fields: {
9750
+ projectId: mcpProjectId,
9751
+ meetingId: f.string({ desc: MEETING_ID }),
9752
+ items: f.array(
9753
+ f.object({ title: f.string({ desc: "One follow-up, as a single line.", min: 1 }) }),
9754
+ { desc: "The items to add, in the order they should appear." }
9755
+ )
9756
+ }
9757
+ }
9758
+ });
9759
+ var LINKED_TASK_DESC = "Card id or slug to attach to this item \u2014 the card that carries the work. Must be in the same project. Pass it in the same call that ticks the item when you just filed the card.";
9760
+ var checkMeetingChecklistItemContract = defineToolContract({
9761
+ name: "check_meeting_checklist_item",
9762
+ agent: {
9763
+ description: `Tick or untick one of a meeting's checklist items, and optionally attach the card that carries it. This is how a next step becomes tracked work: file the card with create_task, then call this with the item's title and the new card's slug. Ticking records YOU as the person who did it. Unticking clears that, but keeps the linked card.`,
9764
+ fields: {
9765
+ meetingId: f.string({ desc: MEETING_ID }),
9766
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9767
+ checked: f.boolean({ desc: "true to tick the item, false to untick it." }),
9768
+ linkedTask: f.optional(f.string({ desc: LINKED_TASK_DESC }))
9769
+ }
9770
+ },
9771
+ mcp: {
9772
+ description: `Tick or untick a meeting checklist item, optionally attaching the card that carries it. Ticking records the acting user. Unticking clears that but keeps the link. ${MCP_TAIL}`,
9773
+ fields: {
9774
+ projectId: mcpProjectId,
9775
+ meetingId: f.string({ desc: MEETING_ID }),
9776
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9777
+ checked: f.boolean({ desc: "true to tick the item, false to untick it." }),
9778
+ linkedTask: f.optional(f.string({ desc: LINKED_TASK_DESC }))
9779
+ }
9780
+ }
9781
+ });
9782
+ var editMeetingChecklistItemContract = defineToolContract({
9783
+ name: "edit_meeting_checklist_item",
9784
+ agent: {
9785
+ description: `Reword one of a meeting's checklist items. Use it to sharpen a vague next step into something someone can act on; to mark it done use check_meeting_checklist_item instead.`,
9786
+ fields: {
9787
+ meetingId: f.string({ desc: MEETING_ID }),
9788
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9789
+ newTitle: f.string({ desc: "The replacement text, as a single line.", min: 1 })
9790
+ }
9791
+ },
9792
+ mcp: {
9793
+ description: `Reword a meeting checklist item. To mark it done use check_meeting_checklist_item. ${MCP_TAIL}`,
9794
+ fields: {
9795
+ projectId: mcpProjectId,
9796
+ meetingId: f.string({ desc: MEETING_ID }),
9797
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9798
+ newTitle: f.string({ desc: "The replacement text, as a single line.", min: 1 })
9799
+ }
9800
+ }
9801
+ });
9802
+ var removeMeetingChecklistItemContract = defineToolContract({
9803
+ name: "remove_meeting_checklist_item",
9804
+ agent: {
9805
+ description: `Delete one of a meeting's checklist items. For a step that turned out not to be needed. An item someone already ticked is a record that work happened \u2014 untick it or leave it rather than deleting it.`,
9806
+ fields: {
9807
+ meetingId: f.string({ desc: MEETING_ID }),
9808
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE })
9809
+ }
9810
+ },
9811
+ mcp: {
9812
+ description: `Delete a meeting checklist item. Prefer unticking over deleting an item someone already completed. ${MCP_TAIL}`,
9813
+ fields: {
9814
+ projectId: mcpProjectId,
9815
+ meetingId: f.string({ desc: MEETING_ID }),
9816
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE })
9817
+ }
9818
+ }
9819
+ });
9609
9820
  var meetingsContracts = [
9610
9821
  listMeetingsContract,
9611
9822
  getMeetingContract,
9612
- readMeetingTranscriptContract
9823
+ readMeetingTranscriptContract,
9824
+ createMeetingContract,
9825
+ updateMeetingContract,
9826
+ addMeetingChecklistItemsContract,
9827
+ checkMeetingChecklistItemContract,
9828
+ editMeetingChecklistItemContract,
9829
+ removeMeetingChecklistItemContract
9613
9830
  ];
9614
9831
  var SINCE_MINUTES = f.optional(
9615
9832
  f.number({
@@ -9744,9 +9961,9 @@ var TOOL_CONTRACTS = Object.fromEntries(
9744
9961
  );
9745
9962
 
9746
9963
  // src/tools/contract-tool.ts
9747
- import { z as z12 } from "zod";
9964
+ import { z as z13 } from "zod";
9748
9965
  function agentShape(surface) {
9749
- return compileShape(z12, surface.fields);
9966
+ return compileShape(z13, surface.fields);
9750
9967
  }
9751
9968
  function defineContractTool(contract, handler, options) {
9752
9969
  return {
@@ -9903,11 +10120,11 @@ function buildGetExecutionLogsTool(connection) {
9903
10120
  "get_execution_logs",
9904
10121
  "Read CLI execution logs \u2014 agent reasoning, tool calls, and setup/dev-server output. Filter via source='agent' or 'application'. For human chat use read_task_chat.",
9905
10122
  {
9906
- task_id: z13.string().optional().describe(
10123
+ task_id: z14.string().optional().describe(
9907
10124
  "Task ID or slug. Omit to read logs from the current task. Only the current task or one of its child tasks can be read."
9908
10125
  ),
9909
- source: z13.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
9910
- limit: z13.number().optional().describe("Max number of log entries to return (default 50, max 500).")
10126
+ source: z14.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
10127
+ limit: z14.number().optional().describe("Max number of log entries to return (default 50, max 500).")
9911
10128
  },
9912
10129
  async ({ task_id, source, limit }) => {
9913
10130
  try {
@@ -10002,7 +10219,7 @@ function buildTaskContextTools(connection) {
10002
10219
  }
10003
10220
 
10004
10221
  // src/tools/dependency-suggestion-tools.ts
10005
- import { z as z14 } from "zod";
10222
+ import { z as z15 } from "zod";
10006
10223
  function buildGetDependenciesTool(connection) {
10007
10224
  return defineContractTool(
10008
10225
  getDependenciesContract,
@@ -10026,10 +10243,10 @@ function buildGetSuggestionsTool(connection) {
10026
10243
  "get_suggestions",
10027
10244
  "List project suggestions sorted by vote score. Filter by status or cap with limit (default 20). Suggestions are project-level ideas, not tasks \u2014 use get_task for tasks.",
10028
10245
  {
10029
- status: z14.string().optional().describe(
10246
+ status: z15.string().optional().describe(
10030
10247
  "Filter by status: Planning, Open, InProgress, ReviewPR, ReviewDev, ReviewLive, Complete, Cancelled"
10031
10248
  ),
10032
- limit: z14.number().int().min(1).max(100).optional().describe("Max results (default 20)")
10249
+ limit: z15.number().int().min(1).max(100).optional().describe("Max results (default 20)")
10033
10250
  },
10034
10251
  async ({ status, limit }) => {
10035
10252
  try {
@@ -10053,7 +10270,7 @@ function buildGetSuggestionsTool(connection) {
10053
10270
  }
10054
10271
 
10055
10272
  // src/tools/mutation-tools.ts
10056
- import { z as z15 } from "zod";
10273
+ import { z as z16 } from "zod";
10057
10274
 
10058
10275
  // src/runner/refresh-verify-heal.ts
10059
10276
  async function refreshAndVerifyGithubCredential(cwd, mint) {
@@ -10185,7 +10402,7 @@ function buildForceUpdateTaskStatusTool(connection) {
10185
10402
  "force_update_task_status",
10186
10403
  "EMERGENCY ONLY: force-override a task's Kanban status. Use when an automatic transition failed and the task is wedged. Normal flow transitions status automatically.",
10187
10404
  {
10188
- status: z15.enum([
10405
+ status: z16.enum([
10189
10406
  "Planning",
10190
10407
  "Open",
10191
10408
  "InProgress",
@@ -10195,7 +10412,7 @@ function buildForceUpdateTaskStatusTool(connection) {
10195
10412
  "Complete",
10196
10413
  "Cancelled"
10197
10414
  ]).describe("The new status for the task"),
10198
- task_id: z15.string().optional().describe("Child task ID to update. Omit to update the current task.")
10415
+ task_id: z16.string().optional().describe("Child task ID to update. Omit to update the current task.")
10199
10416
  },
10200
10417
  async ({ status, task_id }) => {
10201
10418
  try {
@@ -10348,10 +10565,10 @@ function buildCreateFollowUpTaskTool(connection) {
10348
10565
  "create_follow_up_task",
10349
10566
  "Create a follow-up task that depends on the current task. The new card is a SIBLING of this one (same parent) and is blocked until this task merges \u2014 it is NOT a child of this card. To break this card into child cards that build as a pack, use create_subtask. For blockers use add_dependency.",
10350
10567
  {
10351
- title: z15.string().describe("Follow-up task title"),
10352
- description: z15.string().optional().describe(cardDescriptionDesc("Brief description of the follow-up work")),
10353
- plan: z15.string().optional().describe("Implementation plan if known"),
10354
- story_point_value: z15.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
10568
+ title: z16.string().describe("Follow-up task title"),
10569
+ description: z16.string().optional().describe(cardDescriptionDesc("Brief description of the follow-up work")),
10570
+ plan: z16.string().optional().describe("Implementation plan if known"),
10571
+ story_point_value: z16.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
10355
10572
  },
10356
10573
  async ({ title, description, plan, story_point_value }) => {
10357
10574
  try {
@@ -10378,11 +10595,11 @@ function buildCreateSuggestionTool(connection) {
10378
10595
  "create_suggestion",
10379
10596
  "Suggest a feature, improvement, rule, or idea for the project. Duplicates are deduped and your upvote is recorded. For actionable work on this task open a follow-up task.",
10380
10597
  {
10381
- title: z15.string().describe("Short title for the suggestion"),
10382
- description: z15.string().optional().describe(
10598
+ title: z16.string().describe("Short title for the suggestion"),
10599
+ description: z16.string().optional().describe(
10383
10600
  "1-2 sentence description of what should change and why. Keep concise and project-focused."
10384
10601
  ),
10385
- tag_names: z15.array(z15.string()).optional().describe("Tag names to categorize the suggestion")
10602
+ tag_names: z16.array(z16.string()).optional().describe("Tag names to categorize the suggestion")
10386
10603
  },
10387
10604
  async ({ title, description, tag_names }) => {
10388
10605
  try {
@@ -10411,8 +10628,8 @@ function buildVoteSuggestionTool(connection) {
10411
10628
  "vote_suggestion",
10412
10629
  "Vote +1 or -1 on a project suggestion. Use to express support or disagreement with a specific suggestion returned by get_suggestions.",
10413
10630
  {
10414
- suggestion_id: z15.string().describe("The suggestion ID to vote on"),
10415
- value: z15.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
10631
+ suggestion_id: z16.string().describe("The suggestion ID to vote on"),
10632
+ value: z16.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
10416
10633
  },
10417
10634
  async ({ suggestion_id, value }) => {
10418
10635
  try {
@@ -10793,7 +11010,7 @@ function buildCommonTools(connection, config) {
10793
11010
  }
10794
11011
 
10795
11012
  // src/tools/pm-tools.ts
10796
- import { z as z16 } from "zod";
11013
+ import { z as z17 } from "zod";
10797
11014
 
10798
11015
  // src/tools/task-update-tools.ts
10799
11016
  var CURRENT_TASK_ONLY = ["title", "description", "plan", "githubBranch"];
@@ -10866,8 +11083,8 @@ function buildUpdateTaskTool(connection) {
10866
11083
  "update_task_plan",
10867
11084
  "Save the plan and/or description to the current task. In auto/building mode, save the plan BEFORE writing code and keep it current as the approach evolves \u2014 post it, then build; never pause the build waiting for approval. For children use update_subtask; for title/tags/PR use update_task_properties.",
10868
11085
  {
10869
- plan: z16.string().optional().describe("The task plan in markdown"),
10870
- description: z16.string().optional().describe(cardDescriptionDesc("Updated task description"))
11086
+ plan: z17.string().optional().describe("The task plan in markdown"),
11087
+ description: z17.string().optional().describe(cardDescriptionDesc("Updated task description"))
10871
11088
  },
10872
11089
  async ({ plan, description }) => {
10873
11090
  try {
@@ -10890,10 +11107,10 @@ function buildHandoffTool(connection) {
10890
11107
  "handoff_to_agent",
10891
11108
  "Hand this task off to an implementer agent for the build phase \u2014 mid-conversation, same session, no restart. Call this once the plan is compiled and saved (update_task). The server swaps this task to the difficulty-sized implementer agent (which may run at a different model level), announces the handoff in the activity log + chat, and switches you into build mode to start implementing. Size the work with the storyPoints arg (or set it first via update_task_properties). Returns the implementer's name + model.",
10892
11109
  {
10893
- storyPoints: z16.number().int().positive().optional().describe(
11110
+ storyPoints: z17.number().int().positive().optional().describe(
10894
11111
  "Difficulty sizing (1=Common, 2=Magic, 3=Rare, 5=Unique, 8=Pack) \u2014 picks which implementer agent takes over. Omit to use the task's current story points."
10895
11112
  ),
10896
- message: z16.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
11113
+ message: z17.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
10897
11114
  },
10898
11115
  async ({ storyPoints, message }) => {
10899
11116
  try {
@@ -11044,7 +11261,7 @@ function buildPackTools(connection) {
11044
11261
  "start_child_cloud_build",
11045
11262
  "Start a cloud build (codespace) for a child task. Preconditions: child status is `Open`, story points set, and an agent assigned \u2014 satisfy all three with update_subtask (status/agentIdOrName/storyPointValue) first; none happen automatically. A PACK_CHILD_LIMIT error is backpressure, not failure: check list_subtasks packSlots for which children hold the in-flight slots, merge/stop one, then retry. On a feature-branch pack, dev is merged into the pack branch first so the child branches from a fresh base; a conflict is reported in the result and never blocks the launch.",
11046
11263
  {
11047
- childTaskId: z16.string().describe("The child task ID to start a cloud build for")
11264
+ childTaskId: z17.string().describe("The child task ID to start a cloud build for")
11048
11265
  },
11049
11266
  async ({ childTaskId }) => {
11050
11267
  try {
@@ -11073,7 +11290,7 @@ Base sync: dev \u2192 ${sync.branch} failed \u2014 ${sync.error}. The child was
11073
11290
  "stop_child_build",
11074
11291
  "Send a graceful stop signal to a running child build's agent. Not a force-kill \u2014 the agent may take a moment to wind down. Stopping a child eventually frees its PACK_CHILD_LIMIT build slot (see list_subtasks packSlots).",
11075
11292
  {
11076
- childTaskId: z16.string().describe("The child task ID whose build should be stopped")
11293
+ childTaskId: z17.string().describe("The child task ID whose build should be stopped")
11077
11294
  },
11078
11295
  async ({ childTaskId }) => {
11079
11296
  try {
@@ -11132,7 +11349,7 @@ function buildPmTools(connection, options) {
11132
11349
  }
11133
11350
 
11134
11351
  // src/tools/discovery-tools.ts
11135
- import { z as z17 } from "zod";
11352
+ import { z as z18 } from "zod";
11136
11353
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique). The key is 'storyPointValue' \u2014 not 'storyPoints'.";
11137
11354
  var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk";
11138
11355
  function describeUpdatedFields(p) {
@@ -11151,12 +11368,12 @@ function buildDiscoveryTools(connection) {
11151
11368
  "update_task_properties",
11152
11369
  "Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
11153
11370
  {
11154
- title: z17.string().optional().describe("The new task title"),
11155
- storyPointValue: z17.number().optional().describe(SP_DESCRIPTION2),
11156
- tagNames: z17.array(z17.string()).optional().describe("Array of tag names to assign"),
11157
- githubPRUrl: z17.string().url().optional().describe("GitHub pull request URL to link to this task"),
11158
- githubBranch: z17.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
11159
- risk: z17.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
11371
+ title: z18.string().optional().describe("The new task title"),
11372
+ storyPointValue: z18.number().optional().describe(SP_DESCRIPTION2),
11373
+ tagNames: z18.array(z18.string()).optional().describe("Array of tag names to assign"),
11374
+ githubPRUrl: z18.string().url().optional().describe("GitHub pull request URL to link to this task"),
11375
+ githubBranch: z18.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
11376
+ risk: z18.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
11160
11377
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
11161
11378
  )
11162
11379
  },
@@ -11196,7 +11413,7 @@ function buildDiscoveryTools(connection) {
11196
11413
  }
11197
11414
 
11198
11415
  // src/tools/project-tools.ts
11199
- import { z as z18 } from "zod";
11416
+ import { z as z19 } from "zod";
11200
11417
 
11201
11418
  // src/execution/context-path-verifier.ts
11202
11419
  import { readFile as readFile2 } from "fs/promises";
@@ -11274,16 +11491,16 @@ function formatContextPathProblems(problems) {
11274
11491
  }
11275
11492
 
11276
11493
  // src/tools/project-tools.ts
11277
- var CONTEXT_PATH_SHAPE = z18.object({
11278
- type: z18.enum(["rule", "doc", "file", "folder"]).describe(
11494
+ var CONTEXT_PATH_SHAPE = z19.object({
11495
+ type: z19.enum(["rule", "doc", "file", "folder"]).describe(
11279
11496
  "Link kind \u2014 all paths are repo-relative; doc marks a synced project doc, which resolves from the workspace like rule/file"
11280
11497
  ),
11281
- path: z18.string().min(1).max(500).describe("Repo-relative path"),
11282
- label: z18.string().max(100).optional(),
11283
- locator: z18.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional().describe(
11498
+ path: z19.string().min(1).max(500).describe("Repo-relative path"),
11499
+ label: z19.string().max(100).optional(),
11500
+ locator: z19.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional().describe(
11284
11501
  'Verified-link tether: text that must keep existing in the file. With locatorType "test" it must appear inside a real it/test/describe TITLE; with "code" anywhere in the file. Validated at write time against the checkout and re-checked by the periodic sweep \u2014 a rename/delete flags the link stale. Locators containing <> are placeholders and never checked.'
11285
11502
  ),
11286
- locatorType: z18.enum(["test", "code"]).optional().describe(
11503
+ locatorType: z19.enum(["test", "code"]).optional().describe(
11287
11504
  "How the locator must match \u2014 required iff locator is set; not valid on folder links"
11288
11505
  )
11289
11506
  }).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
@@ -11332,15 +11549,15 @@ function buildCreateTagTool(connection, projectId, workspaceDir) {
11332
11549
  "create_tag",
11333
11550
  "Create a project tag. Include a crisp description (\u2264255 chars \u2014 the summary) and contextPaths (rule/doc/file/folder links agents auto-load when working on matching tasks); put the full spec in overview. Set parentTagIds to place the tag in the hierarchy right away. Every contextPath is checked against the repo checkout \u2014 a path that does not exist, or whose type does not match what is on disk, rejects the whole call. Fails if the name already exists.",
11334
11551
  {
11335
- name: z18.string().min(1).max(50),
11336
- color: z18.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("#RRGGBB (default gray)"),
11337
- description: z18.string().max(TAG_DESCRIPTION_MAX).optional(),
11338
- overview: z18.string().max(TAG_OVERVIEW_MAX).optional().describe("Full markdown glossary body \u2014 the term's spec"),
11339
- overviewPath: z18.string().min(1).max(500).optional().describe(
11552
+ name: z19.string().min(1).max(50),
11553
+ color: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("#RRGGBB (default gray)"),
11554
+ description: z19.string().max(TAG_DESCRIPTION_MAX).optional(),
11555
+ overview: z19.string().max(TAG_OVERVIEW_MAX).optional().describe("Full markdown glossary body \u2014 the term's spec"),
11556
+ overviewPath: z19.string().min(1).max(500).optional().describe(
11340
11557
  "Repo file to source the overview from (base-branch content is served everywhere). A not-yet-merged path is fine \u2014 the tag serves the stored overview as fallback until the file lands."
11341
11558
  ),
11342
- parentTagIds: z18.array(z18.string()).max(25).optional().describe("Parent tag ids from list_tags (multi-parent DAG) to link at create time"),
11343
- contextPaths: z18.array(CONTEXT_PATH_SHAPE).max(20).optional()
11559
+ parentTagIds: z19.array(z19.string()).max(25).optional().describe("Parent tag ids from list_tags (multi-parent DAG) to link at create time"),
11560
+ contextPaths: z19.array(CONTEXT_PATH_SHAPE).max(20).optional()
11344
11561
  },
11345
11562
  async ({ name, color, description, overview, overviewPath, parentTagIds, contextPaths }) => {
11346
11563
  const rejection = await rejectBadContextPaths(contextPaths, workspaceDir);
@@ -11368,19 +11585,19 @@ function buildUpdateTagTool(connection, projectId, taskId, workspaceDir) {
11368
11585
  "update_tag",
11369
11586
  "Update a tag's name, color, description (\u2264255), markdown overview, parent tags, or contextPaths. contextPaths and parentTagIds are FULL replacements \u2014 include what you want to keep. ALWAYS pass a short reason; it lands in the tag's revision history (with your current card auto-stamped) so the team sees why the glossary changed. Every contextPath is checked against the repo checkout \u2014 a path that does not exist, or whose type does not match what is on disk, rejects the whole call and nothing is written.",
11370
11587
  {
11371
- tagId: z18.string().describe("Tag id from list_tags"),
11372
- name: z18.string().min(1).max(50).optional(),
11373
- color: z18.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
11374
- description: z18.string().max(TAG_DESCRIPTION_MAX).optional(),
11375
- overview: z18.string().max(TAG_OVERVIEW_MAX).nullable().optional().describe(
11588
+ tagId: z19.string().describe("Tag id from list_tags"),
11589
+ name: z19.string().min(1).max(50).optional(),
11590
+ color: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
11591
+ description: z19.string().max(TAG_DESCRIPTION_MAX).optional(),
11592
+ overview: z19.string().max(TAG_OVERVIEW_MAX).nullable().optional().describe(
11376
11593
  "Full markdown glossary body; null clears it. REJECTED while overviewPath is set \u2014 edit the sourced file in the repo instead"
11377
11594
  ),
11378
- overviewPath: z18.string().min(1).max(500).nullable().optional().describe(
11595
+ overviewPath: z19.string().min(1).max(500).nullable().optional().describe(
11379
11596
  "Repo file to source the overview from (null clears back to the stored overview). A not-yet-merged path is fine \u2014 the stored overview serves as fallback until the file lands on the base branch."
11380
11597
  ),
11381
- parentTagIds: z18.array(z18.string()).max(25).optional().describe("Full-set replacement of the tag's parent tags (multi-parent DAG)"),
11382
- reason: z18.string().max(TAG_REASON_MAX).optional().describe("One line on why \u2014 shown in the tag's revision history"),
11383
- contextPaths: z18.array(CONTEXT_PATH_SHAPE).max(20).optional()
11598
+ parentTagIds: z19.array(z19.string()).max(25).optional().describe("Full-set replacement of the tag's parent tags (multi-parent DAG)"),
11599
+ reason: z19.string().max(TAG_REASON_MAX).optional().describe("One line on why \u2014 shown in the tag's revision history"),
11600
+ contextPaths: z19.array(CONTEXT_PATH_SHAPE).max(20).optional()
11384
11601
  },
11385
11602
  async ({
11386
11603
  tagId,
@@ -11438,8 +11655,8 @@ function buildPostToProjectChatTool(connection, projectId) {
11438
11655
  "post_to_project_chat",
11439
11656
  "Post a markdown message to the PROJECT chat \u2014 use once at the end of an audit for the summary the team reads.",
11440
11657
  {
11441
- message: z18.string().min(1).max(2e4),
11442
- kind: z18.enum(["tag_audit_summary"]).optional().describe(
11658
+ message: z19.string().min(1).max(2e4),
11659
+ kind: z19.enum(["tag_audit_summary"]).optional().describe(
11443
11660
  "Set to 'tag_audit_summary' when posting a tag-audit summary so it is also saved to the persistent tag history"
11444
11661
  )
11445
11662
  },
@@ -11458,7 +11675,7 @@ function buildGetProjectTaskTool(connection, projectId) {
11458
11675
  "get_project_task",
11459
11676
  "Fetch any task in the project by id or slug: title, description, plan, status, and metadata. The audit evidence trail starts here.",
11460
11677
  {
11461
- taskId: z18.string().describe("Task id or slug")
11678
+ taskId: z19.string().describe("Task id or slug")
11462
11679
  },
11463
11680
  async ({ taskId }) => {
11464
11681
  try {
@@ -11476,8 +11693,8 @@ function buildReadProjectTaskChatTool(connection, projectId) {
11476
11693
  "read_project_task_chat",
11477
11694
  "Read any project task's chat messages (newest last). role 'user' rows are HUMAN turns; 'assistant'/'system' rows are agent posts and activity-log entries.",
11478
11695
  {
11479
- taskId: z18.string().describe("Task id or slug"),
11480
- limit: z18.number().int().min(1).max(200).optional().describe("Messages to fetch (default 50)")
11696
+ taskId: z19.string().describe("Task id or slug"),
11697
+ limit: z19.number().int().min(1).max(200).optional().describe("Messages to fetch (default 50)")
11481
11698
  },
11482
11699
  async ({ taskId, limit }) => {
11483
11700
  try {
@@ -11499,9 +11716,9 @@ function buildGetProjectTaskLogsTool(connection, projectId) {
11499
11716
  "get_project_task_logs",
11500
11717
  "Read any project task's persisted agent event stream (message / tool_use / turn_end / error / completed). Turn boundaries are turn_end events. Entries are truncated to ~2KB each; max 500 per call.",
11501
11718
  {
11502
- taskId: z18.string().describe("Task id or slug"),
11503
- limit: z18.number().int().min(1).max(500).optional().describe("Entries to fetch (default 50)"),
11504
- source: z18.enum(["agent", "application"]).optional().describe("Filter: 'agent' = model events (default useful for grading)")
11719
+ taskId: z19.string().describe("Task id or slug"),
11720
+ limit: z19.number().int().min(1).max(500).optional().describe("Entries to fetch (default 50)"),
11721
+ source: z19.enum(["agent", "application"]).optional().describe("Filter: 'agent' = model events (default useful for grading)")
11505
11722
  },
11506
11723
  async ({ taskId, limit, source }) => {
11507
11724
  try {
@@ -11519,44 +11736,44 @@ function buildGetProjectTaskLogsTool(connection, projectId) {
11519
11736
  { annotations: { readOnlyHint: true } }
11520
11737
  );
11521
11738
  }
11522
- var TURN_GRADE_SHAPE = z18.object({
11523
- turnIndex: z18.number().int().min(0),
11524
- phase: z18.enum(["planning", "building", "human"]),
11525
- grade: z18.enum(["correct", "neutral", "blunder"]),
11526
- reasoning: z18.string(),
11527
- eventType: z18.string().describe('e.g. "message", "tool_use", "human_message"'),
11528
- eventSummary: z18.string().max(200).describe("\u2264120 chars of what happened this turn")
11739
+ var TURN_GRADE_SHAPE = z19.object({
11740
+ turnIndex: z19.number().int().min(0),
11741
+ phase: z19.enum(["planning", "building", "human"]),
11742
+ grade: z19.enum(["correct", "neutral", "blunder"]),
11743
+ reasoning: z19.string(),
11744
+ eventType: z19.string().describe('e.g. "message", "tool_use", "human_message"'),
11745
+ eventSummary: z19.string().max(200).describe("\u2264120 chars of what happened this turn")
11529
11746
  });
11530
- var HUMAN_EVAL_SHAPE = z18.object({
11531
- messageIndex: z18.number().int().min(0).describe("Index into the task's human messages, oldest first"),
11532
- rating: z18.number().int().min(-1).max(1),
11533
- reasoning: z18.string()
11747
+ var HUMAN_EVAL_SHAPE = z19.object({
11748
+ messageIndex: z19.number().int().min(0).describe("Index into the task's human messages, oldest first"),
11749
+ rating: z19.number().int().min(-1).max(1),
11750
+ reasoning: z19.string()
11534
11751
  });
11535
11752
  function buildReportTaskAuditResultTool(connection, projectId) {
11536
11753
  return defineTool(
11537
11754
  "report_task_audit_result",
11538
11755
  "Persist one audited task's grades (call once per task after grading it). Pass error instead to mark the audit failed when the evidence is unusable.",
11539
11756
  {
11540
- taskId: z18.string().describe("The audited task's id (NOT slug)"),
11541
- summary: z18.string().describe("3-6 sentences: what went well, what was wasted"),
11542
- turnGrades: z18.array(TURN_GRADE_SHAPE),
11543
- planningAccuracy: z18.number().min(0).max(1).nullable(),
11544
- buildingAccuracy: z18.number().min(0).max(1).nullable(),
11545
- humanAccuracy: z18.number().min(0).max(1).nullable(),
11546
- planningCorrect: z18.number().int().min(0),
11547
- planningNeutral: z18.number().int().min(0),
11548
- planningBlunder: z18.number().int().min(0),
11549
- buildingCorrect: z18.number().int().min(0),
11550
- buildingNeutral: z18.number().int().min(0),
11551
- buildingBlunder: z18.number().int().min(0),
11552
- humanCorrect: z18.number().int().min(0),
11553
- humanNeutral: z18.number().int().min(0),
11554
- humanBlunder: z18.number().int().min(0),
11555
- humanEvaluations: z18.array(HUMAN_EVAL_SHAPE).optional(),
11556
- suggestionIds: z18.array(z18.string()).describe("Suggestion ids filed for this task, or []"),
11557
- auditCostUsd: z18.number().nullable(),
11558
- model: z18.string().nullable().describe("The model you are running as"),
11559
- error: z18.string().optional().describe("Set ONLY to mark this task's audit failed")
11757
+ taskId: z19.string().describe("The audited task's id (NOT slug)"),
11758
+ summary: z19.string().describe("3-6 sentences: what went well, what was wasted"),
11759
+ turnGrades: z19.array(TURN_GRADE_SHAPE),
11760
+ planningAccuracy: z19.number().min(0).max(1).nullable(),
11761
+ buildingAccuracy: z19.number().min(0).max(1).nullable(),
11762
+ humanAccuracy: z19.number().min(0).max(1).nullable(),
11763
+ planningCorrect: z19.number().int().min(0),
11764
+ planningNeutral: z19.number().int().min(0),
11765
+ planningBlunder: z19.number().int().min(0),
11766
+ buildingCorrect: z19.number().int().min(0),
11767
+ buildingNeutral: z19.number().int().min(0),
11768
+ buildingBlunder: z19.number().int().min(0),
11769
+ humanCorrect: z19.number().int().min(0),
11770
+ humanNeutral: z19.number().int().min(0),
11771
+ humanBlunder: z19.number().int().min(0),
11772
+ humanEvaluations: z19.array(HUMAN_EVAL_SHAPE).optional(),
11773
+ suggestionIds: z19.array(z19.string()).describe("Suggestion ids filed for this task, or []"),
11774
+ auditCostUsd: z19.number().nullable(),
11775
+ model: z19.string().nullable().describe("The model you are running as"),
11776
+ error: z19.string().optional().describe("Set ONLY to mark this task's audit failed")
11560
11777
  },
11561
11778
  async (input) => {
11562
11779
  try {
@@ -11849,6 +12066,13 @@ function errText4(prefix, error) {
11849
12066
  return textResult(`${prefix}: ${error instanceof Error ? error.message : "Unknown error"}`);
11850
12067
  }
11851
12068
  function buildMeetingTools(connection, projectId) {
12069
+ return [
12070
+ ...readTools(connection, projectId),
12071
+ ...writeTools(connection, projectId),
12072
+ ...checklistTools(connection, projectId)
12073
+ ];
12074
+ }
12075
+ function readTools(connection, projectId) {
11852
12076
  return [
11853
12077
  defineContractTool(
11854
12078
  listMeetingsContract,
@@ -11911,6 +12135,107 @@ ${res.lines.join("\n")}${footer}`);
11911
12135
  )
11912
12136
  ];
11913
12137
  }
12138
+ function writeTools(connection, projectId) {
12139
+ return [
12140
+ defineContractTool(createMeetingContract, async (input) => {
12141
+ try {
12142
+ const res = await connection.call("createProjectMeeting", {
12143
+ projectId,
12144
+ rawText: input.rawText,
12145
+ title: input.title,
12146
+ occurredAt: input.occurredAt
12147
+ });
12148
+ return textResult(
12149
+ `Created "${res.title}" (${res.segmentCount} segments, ${res.participants.length} participants).
12150
+ The AI summary is being written now; read it back with get_meeting in a few seconds.
12151
+ ${res.url}`
12152
+ );
12153
+ } catch (error) {
12154
+ return errText4("Failed to create the meeting", error);
12155
+ }
12156
+ }),
12157
+ defineContractTool(updateMeetingContract, async (input) => {
12158
+ try {
12159
+ const res = await connection.call("updateProjectMeeting", {
12160
+ projectId,
12161
+ meetingId: input.meetingId,
12162
+ title: input.title,
12163
+ occurredAt: input.occurredAt,
12164
+ summary: input.summary
12165
+ });
12166
+ return textResult(`Updated "${res.title}".
12167
+ ${res.url}`);
12168
+ } catch (error) {
12169
+ return errText4("Failed to update the meeting", error);
12170
+ }
12171
+ })
12172
+ ];
12173
+ }
12174
+ function describeItem(item) {
12175
+ const box = item.checked ? "[x]" : "[ ]";
12176
+ const who = item.checked && item.checkedBy ? ` \u2014 ${item.checkedBy}` : "";
12177
+ const card = item.linkedTaskSlug ? ` (${item.linkedTaskSlug})` : "";
12178
+ return `${box} ${item.title}${who}${card}`;
12179
+ }
12180
+ function checklistTools(connection, projectId) {
12181
+ return [
12182
+ defineContractTool(addMeetingChecklistItemsContract, async (input) => {
12183
+ try {
12184
+ const res = await connection.call("addProjectMeetingChecklistItems", {
12185
+ projectId,
12186
+ meetingId: input.meetingId,
12187
+ items: input.items
12188
+ });
12189
+ if (res.length === 0) {
12190
+ return textResult("No items added \u2014 every title is already on this checklist.");
12191
+ }
12192
+ return textResult(`Added ${res.length}:
12193
+ ${res.map(describeItem).join("\n")}`);
12194
+ } catch (error) {
12195
+ return errText4("Failed to add checklist items", error);
12196
+ }
12197
+ }),
12198
+ defineContractTool(checkMeetingChecklistItemContract, async (input) => {
12199
+ try {
12200
+ const res = await connection.call("checkProjectMeetingChecklistItem", {
12201
+ projectId,
12202
+ meetingId: input.meetingId,
12203
+ title: input.title,
12204
+ checked: input.checked,
12205
+ linkedTask: input.linkedTask
12206
+ });
12207
+ return textResult(describeItem(res));
12208
+ } catch (error) {
12209
+ return errText4("Failed to update the checklist item", error);
12210
+ }
12211
+ }),
12212
+ defineContractTool(editMeetingChecklistItemContract, async (input) => {
12213
+ try {
12214
+ const res = await connection.call("editProjectMeetingChecklistItem", {
12215
+ projectId,
12216
+ meetingId: input.meetingId,
12217
+ title: input.title,
12218
+ newTitle: input.newTitle
12219
+ });
12220
+ return textResult(describeItem(res));
12221
+ } catch (error) {
12222
+ return errText4("Failed to edit the checklist item", error);
12223
+ }
12224
+ }),
12225
+ defineContractTool(removeMeetingChecklistItemContract, async (input) => {
12226
+ try {
12227
+ const res = await connection.call("removeProjectMeetingChecklistItem", {
12228
+ projectId,
12229
+ meetingId: input.meetingId,
12230
+ title: input.title
12231
+ });
12232
+ return textResult(`Removed "${res.title}".`);
12233
+ } catch (error) {
12234
+ return errText4("Failed to remove the checklist item", error);
12235
+ }
12236
+ })
12237
+ ];
12238
+ }
11914
12239
 
11915
12240
  // src/tools/connected-tools.ts
11916
12241
  function connectedToolsFor(connection, context) {
@@ -11930,7 +12255,7 @@ function connectedToolsFor(connection, context) {
11930
12255
  // src/tools/code-review-tools.ts
11931
12256
  import { execFile } from "child_process";
11932
12257
  import { promisify } from "util";
11933
- import { z as z19 } from "zod";
12258
+ import { z as z20 } from "zod";
11934
12259
  async function endReviewSession(connection, reason) {
11935
12260
  await connection.call("endReviewSession", {
11936
12261
  sessionId: connection.sessionId,
@@ -11938,26 +12263,26 @@ async function endReviewSession(connection, reason) {
11938
12263
  });
11939
12264
  }
11940
12265
  var RISK_LEVELS2 = ["critical", "high", "medium", "low"];
11941
- var reviewedShaSchema = z19.string().regex(/^[0-9a-f]{40}$/i).describe("REQUIRED. The full 40-character commit SHA this verdict reviews.");
12266
+ var reviewedShaSchema = z20.string().regex(/^[0-9a-f]{40}$/i).describe("REQUIRED. The full 40-character commit SHA this verdict reviews.");
11942
12267
  var riskDescription = "REQUIRED. The risk level this change carries, judged by the surface area it touches: critical = touches critical/foundational surface, high = important surface, medium = moderate, low = small/isolated. Set this on every verdict. You have authority to override a risk level already set on the task if you disagree with it.";
11943
- var ReviewGuideToolSchema = z19.strictObject({
11944
- reviewedSha: z19.string().regex(/^[0-9a-f]{40}$/i).describe(
12268
+ var ReviewGuideToolSchema = z20.strictObject({
12269
+ reviewedSha: z20.string().regex(/^[0-9a-f]{40}$/i).describe(
11945
12270
  "REQUIRED. The PR's current head as a full 40-char SHA. Run `git rev-parse HEAD` immediately before this call \u2014 never extend an abbreviated hash into 40 characters."
11946
12271
  ),
11947
- overview: z19.string().min(1).max(6e4).describe("REQUIRED. Plain-text walkthrough intro, max 3000 characters. Keep it short."),
11948
- sections: z19.array(
11949
- z19.strictObject({
11950
- title: z19.string().min(1).max(160),
11951
- explanation: z19.string().min(1).max(2e3),
11952
- classification: z19.enum(["core", "supporting"]).optional(),
11953
- files: z19.array(
11954
- z19.strictObject({
11955
- path: z19.string().min(1).max(500).describe(
12272
+ overview: z20.string().min(1).max(6e4).describe("REQUIRED. Plain-text walkthrough intro, max 3000 characters. Keep it short."),
12273
+ sections: z20.array(
12274
+ z20.strictObject({
12275
+ title: z20.string().min(1).max(160),
12276
+ explanation: z20.string().min(1).max(2e3),
12277
+ classification: z20.enum(["core", "supporting"]).optional(),
12278
+ files: z20.array(
12279
+ z20.strictObject({
12280
+ path: z20.string().min(1).max(500).describe(
11956
12281
  "A file the PR's diff actually changed. Context files you merely read are rejected."
11957
12282
  ),
11958
- startLine: z19.number().int().positive().max(1e6).optional(),
11959
- endLine: z19.number().int().positive().max(1e6).optional(),
11960
- hunkHeader: z19.string().min(1).max(300).optional().describe(
12283
+ startLine: z20.number().int().positive().max(1e6).optional(),
12284
+ endLine: z20.number().int().positive().max(1e6).optional(),
12285
+ hunkHeader: z20.string().min(1).max(300).optional().describe(
11961
12286
  "Optional anchor, matched byte-exactly against the full hunk header line from `git diff` INCLUDING the context text after the second @@. Copy it verbatim from `git diff <base>..HEAD -- <file> | grep '^@@'`, or omit anchors entirely (path-only entries always validate)."
11962
12287
  )
11963
12288
  })
@@ -12041,8 +12366,8 @@ function buildApproveCodeReviewTool(connection) {
12041
12366
  "Approve the code review and exit. Use when the diff passes all review criteria. Requires a summary and a risk level \u2014 for changes, use request_code_changes with a structured issues[] list.",
12042
12367
  {
12043
12368
  reviewedSha: reviewedShaSchema,
12044
- summary: z19.string().describe("Brief summary of what was reviewed and why it looks good"),
12045
- risk: z19.enum(RISK_LEVELS2).describe(riskDescription)
12369
+ summary: z20.string().describe("Brief summary of what was reviewed and why it looks good"),
12370
+ risk: z20.enum(RISK_LEVELS2).describe(riskDescription)
12046
12371
  },
12047
12372
  async ({ reviewedSha, summary, risk }) => {
12048
12373
  const content = `**Code Review: Approved** :white_check_mark:
@@ -12073,16 +12398,16 @@ function buildRequestCodeChangesTool(connection) {
12073
12398
  "Request changes during code review and exit. Use when substantive issues must be fixed before merge. Each issue: { file, line?, severity: critical|major|minor, description }.",
12074
12399
  {
12075
12400
  reviewedSha: reviewedShaSchema,
12076
- issues: z19.array(
12077
- z19.object({
12078
- file: z19.string().describe("File path where the issue was found"),
12079
- line: z19.number().optional().describe("Line number (if applicable)"),
12080
- severity: z19.enum(["critical", "major", "minor"]).describe("Issue severity"),
12081
- description: z19.string().describe("What is wrong and how to fix it")
12401
+ issues: z20.array(
12402
+ z20.object({
12403
+ file: z20.string().describe("File path where the issue was found"),
12404
+ line: z20.number().optional().describe("Line number (if applicable)"),
12405
+ severity: z20.enum(["critical", "major", "minor"]).describe("Issue severity"),
12406
+ description: z20.string().describe("What is wrong and how to fix it")
12082
12407
  })
12083
12408
  ).describe("List of issues found during review"),
12084
- summary: z19.string().describe("Brief overall summary of the review findings"),
12085
- risk: z19.enum(RISK_LEVELS2).describe(riskDescription)
12409
+ summary: z20.string().describe("Brief overall summary of the review findings"),
12410
+ risk: z20.enum(RISK_LEVELS2).describe(riskDescription)
12086
12411
  },
12087
12412
  async ({ reviewedSha, issues, summary, risk }) => {
12088
12413
  const issueLines = issues.map((issue) => {
@@ -12152,7 +12477,7 @@ function getModeTools(agentMode, connection, config, context) {
12152
12477
  }
12153
12478
  }
12154
12479
  function buildPrGuideToolsFor(effectiveMode, connection, config, context) {
12155
- const isLeafBuild = effectiveMode === "building" || effectiveMode === "auto";
12480
+ const isLeafBuild = effectiveMode === "building" || effectiveMode === "auto" || effectiveMode === "chat";
12156
12481
  const isSinglePodPack = config.mode === "pack" && config.packExecution !== "fan-out";
12157
12482
  return (isLeafBuild || isSinglePodPack) && (isSinglePodPack || !context?.isParentTask) ? [
12158
12483
  buildPublishReviewGuideTool(connection, {
@@ -12639,10 +12964,14 @@ async function processAssistantCase(event, host, state) {
12639
12964
  }
12640
12965
  const usage = await handleAssistantCase(event, host, state.turnToolCalls);
12641
12966
  if (usage) state.lastAssistantUsage = usage;
12642
- if (!state.sawApiError) {
12967
+ if (!state.sawApiError || event.isApiError && !state.terminalApiError) {
12643
12968
  const fullText = event.message.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
12644
12969
  if (API_ERROR_PATTERN.test(fullText)) {
12645
12970
  state.sawApiError = true;
12971
+ if (event.isApiError) {
12972
+ state.terminalApiError = true;
12973
+ state.apiErrorText = fullText.slice(0, 300);
12974
+ }
12646
12975
  }
12647
12976
  }
12648
12977
  }
@@ -12657,7 +12986,7 @@ async function processResultCase(event, host, context, startTime, state) {
12657
12986
  );
12658
12987
  if (info.stoppedTyping) state.isTyping = false;
12659
12988
  state.retriable = info.retriable;
12660
- if (!info.retriable) state.sawApiError = false;
12989
+ if (!info.retriable && !state.terminalApiError) state.sawApiError = false;
12661
12990
  state.resultSummary = info.resultSummary;
12662
12991
  if (info.staleSession) state.staleSession = true;
12663
12992
  if (info.authError) state.authError = true;
@@ -12678,6 +13007,8 @@ async function processEvents(events, context, host) {
12678
13007
  isTyping: false,
12679
13008
  retriable: false,
12680
13009
  sawApiError: false,
13010
+ terminalApiError: false,
13011
+ apiErrorText: void 0,
12681
13012
  resultSummary: void 0,
12682
13013
  rateLimitResetsAt: void 0,
12683
13014
  rateLimitRejectedType: void 0,
@@ -12720,8 +13051,12 @@ async function processEvents(events, context, host) {
12720
13051
  }
12721
13052
  flushPendingToolCalls(host, state.turnToolCalls);
12722
13053
  stopTypingIfNeeded(host, state.isTyping);
13054
+ return buildProcessResult(state);
13055
+ }
13056
+ function buildProcessResult(state) {
12723
13057
  return {
12724
13058
  retriable: state.retriable || state.sawApiError,
13059
+ ...state.apiErrorText && { apiErrorText: state.apiErrorText },
12725
13060
  resultSummary: state.resultSummary,
12726
13061
  rateLimitResetsAt: state.rateLimitResetsAt,
12727
13062
  ...state.rateLimitRejectedType && { rateLimitRejectedType: state.rateLimitRejectedType },
@@ -12964,25 +13299,6 @@ function handleReviewToolAccess(toolName, input) {
12964
13299
  }
12965
13300
  return handleBuildingToolAccess(toolName, input);
12966
13301
  }
12967
- var CHAT_BLOCKED_BASH = /\bgit\s+push\b|\bgh\s+pr\b|\bhub\s+pull-request\b/;
12968
- var CREATE_PR_TOOL = /(^|__)create_pull_request$/;
12969
- var CHAT_PLAN_GATE_MESSAGE = [
12970
- "This chat card has no saved plan, so it has no PR path yet.",
12971
- "If the conversation has turned into a development task, save a plan with update_task first \u2014 that identifies the card and moves it to In Progress \u2014 then commit, push, and open the PR.",
12972
- "If it is still a conversation, deliver the work by attaching files to the card with upload_attachment instead."
12973
- ].join(" ");
12974
- function isChatPrTool(toolName, input) {
12975
- if (CREATE_PR_TOOL.test(toolName)) return true;
12976
- return toolName === "Bash" && CHAT_BLOCKED_BASH.test(String(input.command ?? ""));
12977
- }
12978
- async function chatCardHasPlan(host) {
12979
- try {
12980
- const props = await host.connection.getTaskProperties();
12981
- return !!props.plan?.trim();
12982
- } catch {
12983
- return false;
12984
- }
12985
- }
12986
13302
  function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask) {
12987
13303
  if (hasExitedPlanMode) {
12988
13304
  return isParentTask ? handleReviewToolAccess(toolName, input) : handleBuildingToolAccess(toolName, input);
@@ -13162,7 +13478,6 @@ function resolveToolAccess(host, toolName, input) {
13162
13478
  }
13163
13479
  function buildCanUseTool(host) {
13164
13480
  let consecutiveDenials = 0;
13165
- let chatHasPlan = false;
13166
13481
  return async (toolName, input) => {
13167
13482
  if (toolName === "ExitPlanMode" && (host.agentMode === "auto" || host.agentMode === "discovery") && !host.hasExitedPlanMode) {
13168
13483
  return await handleExitPlanMode(host, input);
@@ -13178,14 +13493,6 @@ function buildCanUseTool(host) {
13178
13493
  }
13179
13494
  const loopResult = checkToolLoop(host, toolName, input);
13180
13495
  if (loopResult) return loopResult;
13181
- if (host.agentMode === "chat" && isChatPrTool(toolName, input)) {
13182
- if (!chatHasPlan) chatHasPlan = await chatCardHasPlan(host);
13183
- if (!chatHasPlan) {
13184
- consecutiveDenials++;
13185
- handleDenialEscalation(host, consecutiveDenials);
13186
- return { behavior: "deny", message: CHAT_PLAN_GATE_MESSAGE };
13187
- }
13188
- }
13189
13496
  const result = resolveToolAccess(host, toolName, input);
13190
13497
  if (result.behavior === "deny") {
13191
13498
  consecutiveDenials++;
@@ -13955,11 +14262,13 @@ function handleProcessResult(result, context, host, options) {
13955
14262
  }
13956
14263
  async function runWithRetry(initialQuery, context, host, options) {
13957
14264
  let lastErrorWasImage = false;
14265
+ let lastDetail = null;
13958
14266
  for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
13959
14267
  if (host.isStopped()) return;
13960
14268
  const agentQuery = attempt === 0 ? initialQuery : await buildRetryQuery(host, context, options, lastErrorWasImage);
13961
14269
  try {
13962
14270
  const result = await processEvents(agentQuery, context, host);
14271
+ if (result.apiErrorText) lastDetail = result.apiErrorText;
13963
14272
  const outcome = handleProcessResult(result, context, host, options);
13964
14273
  if (outcome.action === "return") return;
13965
14274
  if (outcome.action === "return_promise") return outcome.promise;
@@ -13968,12 +14277,16 @@ async function runWithRetry(initialQuery, context, host, options) {
13968
14277
  const outcome = handleRetryError(error, context, host, options, lastErrorWasImage);
13969
14278
  if (outcome instanceof Promise) return outcome;
13970
14279
  if (outcome.action === "return") return;
14280
+ lastDetail = getErrorMessage(error).slice(0, 300);
13971
14281
  lastErrorWasImage = outcome.lastErrorWasImage;
13972
14282
  }
13973
14283
  if (attempt >= RETRY_DELAYS_MS.length) {
14284
+ const detail = lastDetail ?? "API error";
13974
14285
  host.connection.postChatMessage(
13975
- `Agent shutting down after ${RETRY_DELAYS_MS.length} failed retry attempts due to API errors. The task will resume automatically when the codespace restarts.`
14286
+ `Claude API calls kept failing (\`${detail}\`) through ${RETRY_DELAYS_MS.length} retries over ~${Math.round(RETRY_DELAYS_MS.reduce((a, b) => a + b, 0) / 6e4)} minutes. Handing the card back: it returns to Open and this environment goes to sleep.`
13976
14287
  );
14288
+ host.apiOutageExhausted = true;
14289
+ host.apiOutageDetail = detail;
13977
14290
  return;
13978
14291
  }
13979
14292
  await emitRetryStatus(host, attempt, RETRY_DELAYS_MS[attempt]);
@@ -14043,6 +14356,8 @@ var QueryBridge = class {
14043
14356
  _isParentTask = false;
14044
14357
  _wasRateLimited = false;
14045
14358
  _wedgeAborted = false;
14359
+ _apiOutageExhausted = false;
14360
+ _apiOutageDetail = null;
14046
14361
  _keyCycleCount = 0;
14047
14362
  _abortController = null;
14048
14363
  /** Called by SessionRunner when ExitPlanMode triggers a mode transition. */
@@ -14081,6 +14396,18 @@ var QueryBridge = class {
14081
14396
  get lastTurnWedgeAborted() {
14082
14397
  return this._wedgeAborted;
14083
14398
  }
14399
+ /**
14400
+ * Whether the turn that just ran ended with every Claude API retry
14401
+ * exhausted. Reset at the start of every execute(); SessionRunner reads it
14402
+ * to flush WIP and hand the card back to the server.
14403
+ */
14404
+ get lastTurnApiOutage() {
14405
+ return this._apiOutageExhausted;
14406
+ }
14407
+ /** The API error text behind `lastTurnApiOutage`, or null. */
14408
+ get lastTurnApiOutageDetail() {
14409
+ return this._apiOutageDetail;
14410
+ }
14084
14411
  /**
14085
14412
  * Ask the live terminal (PTY harness only) to redraw its full screen.
14086
14413
  * Safe no-op on the SDK harness or between queries.
@@ -14155,6 +14482,8 @@ var QueryBridge = class {
14155
14482
  this._stopped = false;
14156
14483
  this._wasRateLimited = false;
14157
14484
  this._wedgeAborted = false;
14485
+ this._apiOutageExhausted = false;
14486
+ this._apiOutageDetail = null;
14158
14487
  this._abortController = new AbortController();
14159
14488
  const host = this.buildHost();
14160
14489
  try {
@@ -14204,7 +14533,7 @@ var QueryBridge = class {
14204
14533
  // ── QueryHost construction ──────────────────────────────────────────
14205
14534
  buildHost() {
14206
14535
  const bridge = this;
14207
- return {
14536
+ const host = {
14208
14537
  config: this.runnerConfig,
14209
14538
  connection: this.connection,
14210
14539
  callbacks: this.callbacks,
@@ -14247,12 +14576,6 @@ var QueryBridge = class {
14247
14576
  set wasRateLimited(val) {
14248
14577
  bridge._wasRateLimited = val;
14249
14578
  },
14250
- get wedgeAborted() {
14251
- return bridge._wedgeAborted;
14252
- },
14253
- set wedgeAborted(val) {
14254
- bridge._wedgeAborted = val;
14255
- },
14256
14579
  get keyCycleCount() {
14257
14580
  return bridge._keyCycleCount;
14258
14581
  },
@@ -14276,6 +14599,38 @@ var QueryBridge = class {
14276
14599
  createInputStream: (prompt) => bridge.createInputStream(prompt),
14277
14600
  onModeTransition: bridge.onModeTransition
14278
14601
  };
14602
+ return Object.defineProperties(host, this.turnFlagDescriptors());
14603
+ }
14604
+ /**
14605
+ * The per-turn recovery flags the executor writes and SessionRunner reads
14606
+ * after the turn (`lastTurnWedgeAborted`, `lastTurnApiOutage`). Live
14607
+ * accessors onto the bridge's backing fields, attached as descriptors
14608
+ * because an object spread would snapshot a getter's value instead.
14609
+ */
14610
+ turnFlagDescriptors() {
14611
+ return {
14612
+ wedgeAborted: {
14613
+ enumerable: true,
14614
+ get: () => this._wedgeAborted,
14615
+ set: (val) => {
14616
+ this._wedgeAborted = val;
14617
+ }
14618
+ },
14619
+ apiOutageExhausted: {
14620
+ enumerable: true,
14621
+ get: () => this._apiOutageExhausted,
14622
+ set: (val) => {
14623
+ this._apiOutageExhausted = val;
14624
+ }
14625
+ },
14626
+ apiOutageDetail: {
14627
+ enumerable: true,
14628
+ get: () => this._apiOutageDetail,
14629
+ set: (val) => {
14630
+ this._apiOutageDetail = val;
14631
+ }
14632
+ }
14633
+ };
14279
14634
  }
14280
14635
  // ── Input stream for Claude SDK ─────────────────────────────────────
14281
14636
  async *createInputStream(prompt) {
@@ -15620,6 +15975,27 @@ var SessionRunner = class _SessionRunner {
15620
15975
  }
15621
15976
  throw err;
15622
15977
  }
15978
+ await this.handleApiOutageExhausted();
15979
+ }
15980
+ /**
15981
+ * The turn ended with every Claude API retry exhausted (the CLI's own
15982
+ * retries gave up, then `runWithRetry`'s fresh-session backoff did too).
15983
+ * Push WIP first — the server sleeps this pod in response, so the flush
15984
+ * must not race the teardown — then hand the card back. The core loop
15985
+ * then falls into its normal idle wait; the server-driven sleep is what
15986
+ * ends the pod, and nothing here exits the process or re-prompts.
15987
+ *
15988
+ * NOT a stuck nudge: this keys on the CLI's own error record, never on
15989
+ * silence or idleness (see `.claude/rules/agent-runner.md`).
15990
+ */
15991
+ async handleApiOutageExhausted() {
15992
+ if (!this.queryBridge?.lastTurnApiOutage) return;
15993
+ process.stderr.write("[conveyor-agent] API retries exhausted \u2014 handing the card back\n");
15994
+ await this.flushWipNow("WIP: API outage hand-off");
15995
+ this.connection.reportApiOutage(
15996
+ this.queryBridge.lastTurnApiOutageDetail ?? "API error",
15997
+ RETRY_DELAYS_MS.length
15998
+ );
15623
15999
  }
15624
16000
  /**
15625
16001
  * A human typed into the parked, idle Connected-TUI (keep-alive PTY). Wake the