@rallycry/conveyor-agent 11.0.8 → 11.0.10

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) {
@@ -8040,6 +8118,7 @@ ${config.instructions}`);
8040
8118
  `
8041
8119
  Your turn output appears ONLY in the live agent terminal \u2014 it is NOT posted to the task chat. The team does NOT see your replies unless you post them.`,
8042
8120
  `Use post_to_chat (omit task_id \u2192 your own task's chat) to share meaningful status as you work, a summary when you finish, and any blocker or question you need a human to answer.`,
8121
+ `A question you need answered goes through AskUserQuestion \u2014 that is what pages a person. post_to_chat with milestone: "blocked" records that you are parked and does not page anyone.`,
8043
8122
  `Pass task_id only to message a DIFFERENT task's chat (e.g. a parent task via get_task).`,
8044
8123
  `Use read_task_chat only if you need to re-read earlier messages beyond the chat context above.`,
8045
8124
  `
@@ -8346,7 +8425,7 @@ async function buildInitialPrompt(mode, context, isAuto, agentMode, packExecutio
8346
8425
  }
8347
8426
 
8348
8427
  // src/tools/task-context-tools.ts
8349
- import { z as z13 } from "zod";
8428
+ import { z as z14 } from "zod";
8350
8429
 
8351
8430
  // ../shared/dist/tool-contracts/index.js
8352
8431
  var f = {
@@ -8375,14 +8454,14 @@ var f = {
8375
8454
  return { kind: "nullable", inner };
8376
8455
  }
8377
8456
  };
8378
- function compileString(z20, spec) {
8379
- let schema = z20.string();
8457
+ function compileString(z21, spec) {
8458
+ let schema = z21.string();
8380
8459
  if (spec.min !== void 0) schema = schema.min(spec.min);
8381
8460
  if (spec.max !== void 0) schema = schema.max(spec.max);
8382
8461
  return schema;
8383
8462
  }
8384
- function compileNumber(z20, spec) {
8385
- let schema = z20.number();
8463
+ function compileNumber(z21, spec) {
8464
+ let schema = z21.number();
8386
8465
  if (spec.int) schema = schema.int();
8387
8466
  if (spec.positive) schema = schema.positive();
8388
8467
  if (spec.nonnegative) schema = schema.nonnegative();
@@ -8390,49 +8469,49 @@ function compileNumber(z20, spec) {
8390
8469
  if (spec.max !== void 0) schema = schema.max(spec.max);
8391
8470
  return schema;
8392
8471
  }
8393
- function compileArray(z20, spec) {
8394
- let schema = z20.array(compileField(z20, spec.item));
8472
+ function compileArray(z21, spec) {
8473
+ let schema = z21.array(compileField(z21, spec.item));
8395
8474
  if (spec.min !== void 0) schema = schema.min(spec.min);
8396
8475
  return schema;
8397
8476
  }
8398
- function compileBase(z20, spec) {
8477
+ function compileBase(z21, spec) {
8399
8478
  switch (spec.kind) {
8400
8479
  case "string":
8401
- return compileString(z20, spec);
8480
+ return compileString(z21, spec);
8402
8481
  case "number":
8403
- return compileNumber(z20, spec);
8482
+ return compileNumber(z21, spec);
8404
8483
  case "boolean":
8405
- return z20.boolean();
8484
+ return z21.boolean();
8406
8485
  case "enum":
8407
- return z20.enum([...spec.values]);
8486
+ return z21.enum([...spec.values]);
8408
8487
  case "array":
8409
- return compileArray(z20, spec);
8488
+ return compileArray(z21, spec);
8410
8489
  case "object":
8411
- return z20.object(compileShape(z20, spec.fields));
8490
+ return z21.object(compileShape(z21, spec.fields));
8412
8491
  }
8413
8492
  }
8414
8493
  function descriptionOf(spec) {
8415
8494
  if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
8416
8495
  return spec.desc;
8417
8496
  }
8418
- function compileUndescribed(z20, spec) {
8497
+ function compileUndescribed(z21, spec) {
8419
8498
  if (spec.kind === "optional") {
8420
- return compileUndescribed(z20, spec.inner).optional();
8499
+ return compileUndescribed(z21, spec.inner).optional();
8421
8500
  }
8422
8501
  if (spec.kind === "nullable") {
8423
- return compileUndescribed(z20, spec.inner).nullable();
8502
+ return compileUndescribed(z21, spec.inner).nullable();
8424
8503
  }
8425
- return compileBase(z20, spec);
8504
+ return compileBase(z21, spec);
8426
8505
  }
8427
- function compileField(z20, spec) {
8428
- const schema = compileUndescribed(z20, spec);
8506
+ function compileField(z21, spec) {
8507
+ const schema = compileUndescribed(z21, spec);
8429
8508
  const desc = descriptionOf(spec);
8430
8509
  return desc === void 0 ? schema : schema.describe(desc);
8431
8510
  }
8432
- function compileShape(z20, fields) {
8511
+ function compileShape(z21, fields) {
8433
8512
  const shape = {};
8434
8513
  for (const [key, spec] of Object.entries(fields)) {
8435
- shape[key] = compileField(z20, spec);
8514
+ shape[key] = compileField(z21, spec);
8436
8515
  }
8437
8516
  return shape;
8438
8517
  }
@@ -8481,7 +8560,7 @@ var postToChatContract = defineToolContract({
8481
8560
  ),
8482
8561
  milestone: f.optional(
8483
8562
  f.enum(["plan_ready", "implementation_complete", "blocked"], {
8484
- desc: "Declare a narrative milestone instead of a routine update. Use SPARINGLY \u2014 only when the plan is ready, implementation is complete, or you are blocked. Milestones appear on the card's activity timeline and in Slack."
8563
+ desc: "Declare a narrative milestone instead of a routine update. Use SPARINGLY \u2014 only when the plan is ready (plan_ready), implementation is complete (implementation_complete), or you are parked and cannot continue until a human acts (blocked). `blocked` posts a note and enables reply delivery but pages nobody; to page a person, ask them with AskUserQuestion. Never use `blocked` for a finished plan, finished work, or a tool error you can retry. Milestones appear on the card's activity timeline and in Slack."
8485
8564
  })
8486
8565
  )
8487
8566
  }
@@ -9013,7 +9092,7 @@ var createSubtaskContract = defineToolContract({
9013
9092
  ),
9014
9093
  tags: f.optional(
9015
9094
  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.'
9095
+ 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
9096
  })
9018
9097
  )
9019
9098
  }
@@ -9606,10 +9685,149 @@ var readMeetingTranscriptContract = defineToolContract({
9606
9685
  }
9607
9686
  }
9608
9687
  });
9688
+ 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.";
9689
+ 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.";
9690
+ var TITLE_DESC = "Meeting title. Defaults to `Meeting <date>` when you do not name one.";
9691
+ var createMeetingContract = defineToolContract({
9692
+ name: "create_meeting",
9693
+ agent: {
9694
+ 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.`,
9695
+ fields: {
9696
+ rawText: f.string({ desc: RAW_TEXT_DESC, min: 1 }),
9697
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9698
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC }))
9699
+ }
9700
+ },
9701
+ mcp: {
9702
+ 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}`,
9703
+ fields: {
9704
+ projectId: mcpProjectId,
9705
+ rawText: f.string({ desc: RAW_TEXT_DESC, min: 1 }),
9706
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9707
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC }))
9708
+ }
9709
+ }
9710
+ });
9711
+ 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.";
9712
+ var updateMeetingContract = defineToolContract({
9713
+ name: "update_meeting",
9714
+ agent: {
9715
+ 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.`,
9716
+ fields: {
9717
+ meetingId: f.string({ desc: MEETING_ID }),
9718
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9719
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC })),
9720
+ summary: f.optional(f.string({ desc: SUMMARY_DESC, min: 1 }))
9721
+ }
9722
+ },
9723
+ mcp: {
9724
+ 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}`,
9725
+ fields: {
9726
+ projectId: mcpProjectId,
9727
+ meetingId: f.string({ desc: MEETING_ID }),
9728
+ title: f.optional(f.string({ desc: TITLE_DESC, max: 200 })),
9729
+ occurredAt: f.optional(f.string({ desc: OCCURRED_AT_DESC })),
9730
+ summary: f.optional(f.string({ desc: SUMMARY_DESC, min: 1 }))
9731
+ }
9732
+ }
9733
+ });
9734
+ var CHECKLIST_ITEM_TITLE = "The item's exact title, as get_meeting lists it. Matched case-insensitively.";
9735
+ 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.";
9736
+ var addMeetingChecklistItemsContract = defineToolContract({
9737
+ name: "add_meeting_checklist_items",
9738
+ agent: {
9739
+ 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.`,
9740
+ fields: {
9741
+ meetingId: f.string({ desc: MEETING_ID }),
9742
+ items: f.array(
9743
+ f.object({ title: f.string({ desc: "One follow-up, as a single line.", min: 1 }) }),
9744
+ { desc: "The items to add, in the order they should appear." }
9745
+ )
9746
+ }
9747
+ },
9748
+ mcp: {
9749
+ description: `Add follow-up items to a meeting's checklist. ${CHECKLIST_INTRO} Existing titles are skipped, so re-running is safe. ${MCP_TAIL}`,
9750
+ fields: {
9751
+ projectId: mcpProjectId,
9752
+ meetingId: f.string({ desc: MEETING_ID }),
9753
+ items: f.array(
9754
+ f.object({ title: f.string({ desc: "One follow-up, as a single line.", min: 1 }) }),
9755
+ { desc: "The items to add, in the order they should appear." }
9756
+ )
9757
+ }
9758
+ }
9759
+ });
9760
+ 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.";
9761
+ var checkMeetingChecklistItemContract = defineToolContract({
9762
+ name: "check_meeting_checklist_item",
9763
+ agent: {
9764
+ 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.`,
9765
+ fields: {
9766
+ meetingId: f.string({ desc: MEETING_ID }),
9767
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9768
+ checked: f.boolean({ desc: "true to tick the item, false to untick it." }),
9769
+ linkedTask: f.optional(f.string({ desc: LINKED_TASK_DESC }))
9770
+ }
9771
+ },
9772
+ mcp: {
9773
+ 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}`,
9774
+ fields: {
9775
+ projectId: mcpProjectId,
9776
+ meetingId: f.string({ desc: MEETING_ID }),
9777
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9778
+ checked: f.boolean({ desc: "true to tick the item, false to untick it." }),
9779
+ linkedTask: f.optional(f.string({ desc: LINKED_TASK_DESC }))
9780
+ }
9781
+ }
9782
+ });
9783
+ var editMeetingChecklistItemContract = defineToolContract({
9784
+ name: "edit_meeting_checklist_item",
9785
+ agent: {
9786
+ 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.`,
9787
+ fields: {
9788
+ meetingId: f.string({ desc: MEETING_ID }),
9789
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9790
+ newTitle: f.string({ desc: "The replacement text, as a single line.", min: 1 })
9791
+ }
9792
+ },
9793
+ mcp: {
9794
+ description: `Reword a meeting checklist item. To mark it done use check_meeting_checklist_item. ${MCP_TAIL}`,
9795
+ fields: {
9796
+ projectId: mcpProjectId,
9797
+ meetingId: f.string({ desc: MEETING_ID }),
9798
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE }),
9799
+ newTitle: f.string({ desc: "The replacement text, as a single line.", min: 1 })
9800
+ }
9801
+ }
9802
+ });
9803
+ var removeMeetingChecklistItemContract = defineToolContract({
9804
+ name: "remove_meeting_checklist_item",
9805
+ agent: {
9806
+ 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.`,
9807
+ fields: {
9808
+ meetingId: f.string({ desc: MEETING_ID }),
9809
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE })
9810
+ }
9811
+ },
9812
+ mcp: {
9813
+ description: `Delete a meeting checklist item. Prefer unticking over deleting an item someone already completed. ${MCP_TAIL}`,
9814
+ fields: {
9815
+ projectId: mcpProjectId,
9816
+ meetingId: f.string({ desc: MEETING_ID }),
9817
+ title: f.string({ desc: CHECKLIST_ITEM_TITLE })
9818
+ }
9819
+ }
9820
+ });
9609
9821
  var meetingsContracts = [
9610
9822
  listMeetingsContract,
9611
9823
  getMeetingContract,
9612
- readMeetingTranscriptContract
9824
+ readMeetingTranscriptContract,
9825
+ createMeetingContract,
9826
+ updateMeetingContract,
9827
+ addMeetingChecklistItemsContract,
9828
+ checkMeetingChecklistItemContract,
9829
+ editMeetingChecklistItemContract,
9830
+ removeMeetingChecklistItemContract
9613
9831
  ];
9614
9832
  var SINCE_MINUTES = f.optional(
9615
9833
  f.number({
@@ -9744,9 +9962,9 @@ var TOOL_CONTRACTS = Object.fromEntries(
9744
9962
  );
9745
9963
 
9746
9964
  // src/tools/contract-tool.ts
9747
- import { z as z12 } from "zod";
9965
+ import { z as z13 } from "zod";
9748
9966
  function agentShape(surface) {
9749
- return compileShape(z12, surface.fields);
9967
+ return compileShape(z13, surface.fields);
9750
9968
  }
9751
9969
  function defineContractTool(contract, handler, options) {
9752
9970
  return {
@@ -9903,11 +10121,11 @@ function buildGetExecutionLogsTool(connection) {
9903
10121
  "get_execution_logs",
9904
10122
  "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
10123
  {
9906
- task_id: z13.string().optional().describe(
10124
+ task_id: z14.string().optional().describe(
9907
10125
  "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
10126
  ),
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).")
10127
+ source: z14.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
10128
+ limit: z14.number().optional().describe("Max number of log entries to return (default 50, max 500).")
9911
10129
  },
9912
10130
  async ({ task_id, source, limit }) => {
9913
10131
  try {
@@ -10002,7 +10220,7 @@ function buildTaskContextTools(connection) {
10002
10220
  }
10003
10221
 
10004
10222
  // src/tools/dependency-suggestion-tools.ts
10005
- import { z as z14 } from "zod";
10223
+ import { z as z15 } from "zod";
10006
10224
  function buildGetDependenciesTool(connection) {
10007
10225
  return defineContractTool(
10008
10226
  getDependenciesContract,
@@ -10026,10 +10244,10 @@ function buildGetSuggestionsTool(connection) {
10026
10244
  "get_suggestions",
10027
10245
  "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
10246
  {
10029
- status: z14.string().optional().describe(
10247
+ status: z15.string().optional().describe(
10030
10248
  "Filter by status: Planning, Open, InProgress, ReviewPR, ReviewDev, ReviewLive, Complete, Cancelled"
10031
10249
  ),
10032
- limit: z14.number().int().min(1).max(100).optional().describe("Max results (default 20)")
10250
+ limit: z15.number().int().min(1).max(100).optional().describe("Max results (default 20)")
10033
10251
  },
10034
10252
  async ({ status, limit }) => {
10035
10253
  try {
@@ -10053,7 +10271,7 @@ function buildGetSuggestionsTool(connection) {
10053
10271
  }
10054
10272
 
10055
10273
  // src/tools/mutation-tools.ts
10056
- import { z as z15 } from "zod";
10274
+ import { z as z16 } from "zod";
10057
10275
 
10058
10276
  // src/runner/refresh-verify-heal.ts
10059
10277
  async function refreshAndVerifyGithubCredential(cwd, mint) {
@@ -10185,7 +10403,7 @@ function buildForceUpdateTaskStatusTool(connection) {
10185
10403
  "force_update_task_status",
10186
10404
  "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
10405
  {
10188
- status: z15.enum([
10406
+ status: z16.enum([
10189
10407
  "Planning",
10190
10408
  "Open",
10191
10409
  "InProgress",
@@ -10195,7 +10413,7 @@ function buildForceUpdateTaskStatusTool(connection) {
10195
10413
  "Complete",
10196
10414
  "Cancelled"
10197
10415
  ]).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.")
10416
+ task_id: z16.string().optional().describe("Child task ID to update. Omit to update the current task.")
10199
10417
  },
10200
10418
  async ({ status, task_id }) => {
10201
10419
  try {
@@ -10348,10 +10566,10 @@ function buildCreateFollowUpTaskTool(connection) {
10348
10566
  "create_follow_up_task",
10349
10567
  "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
10568
  {
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)")
10569
+ title: z16.string().describe("Follow-up task title"),
10570
+ description: z16.string().optional().describe(cardDescriptionDesc("Brief description of the follow-up work")),
10571
+ plan: z16.string().optional().describe("Implementation plan if known"),
10572
+ story_point_value: z16.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
10355
10573
  },
10356
10574
  async ({ title, description, plan, story_point_value }) => {
10357
10575
  try {
@@ -10378,11 +10596,11 @@ function buildCreateSuggestionTool(connection) {
10378
10596
  "create_suggestion",
10379
10597
  "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
10598
  {
10381
- title: z15.string().describe("Short title for the suggestion"),
10382
- description: z15.string().optional().describe(
10599
+ title: z16.string().describe("Short title for the suggestion"),
10600
+ description: z16.string().optional().describe(
10383
10601
  "1-2 sentence description of what should change and why. Keep concise and project-focused."
10384
10602
  ),
10385
- tag_names: z15.array(z15.string()).optional().describe("Tag names to categorize the suggestion")
10603
+ tag_names: z16.array(z16.string()).optional().describe("Tag names to categorize the suggestion")
10386
10604
  },
10387
10605
  async ({ title, description, tag_names }) => {
10388
10606
  try {
@@ -10411,8 +10629,8 @@ function buildVoteSuggestionTool(connection) {
10411
10629
  "vote_suggestion",
10412
10630
  "Vote +1 or -1 on a project suggestion. Use to express support or disagreement with a specific suggestion returned by get_suggestions.",
10413
10631
  {
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")
10632
+ suggestion_id: z16.string().describe("The suggestion ID to vote on"),
10633
+ value: z16.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
10416
10634
  },
10417
10635
  async ({ suggestion_id, value }) => {
10418
10636
  try {
@@ -10793,7 +11011,7 @@ function buildCommonTools(connection, config) {
10793
11011
  }
10794
11012
 
10795
11013
  // src/tools/pm-tools.ts
10796
- import { z as z16 } from "zod";
11014
+ import { z as z17 } from "zod";
10797
11015
 
10798
11016
  // src/tools/task-update-tools.ts
10799
11017
  var CURRENT_TASK_ONLY = ["title", "description", "plan", "githubBranch"];
@@ -10866,8 +11084,8 @@ function buildUpdateTaskTool(connection) {
10866
11084
  "update_task_plan",
10867
11085
  "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
11086
  {
10869
- plan: z16.string().optional().describe("The task plan in markdown"),
10870
- description: z16.string().optional().describe(cardDescriptionDesc("Updated task description"))
11087
+ plan: z17.string().optional().describe("The task plan in markdown"),
11088
+ description: z17.string().optional().describe(cardDescriptionDesc("Updated task description"))
10871
11089
  },
10872
11090
  async ({ plan, description }) => {
10873
11091
  try {
@@ -10890,10 +11108,10 @@ function buildHandoffTool(connection) {
10890
11108
  "handoff_to_agent",
10891
11109
  "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
11110
  {
10893
- storyPoints: z16.number().int().positive().optional().describe(
11111
+ storyPoints: z17.number().int().positive().optional().describe(
10894
11112
  "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
11113
  ),
10896
- message: z16.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
11114
+ message: z17.string().optional().describe("Optional kickoff note posted to the chat alongside the handoff notice.")
10897
11115
  },
10898
11116
  async ({ storyPoints, message }) => {
10899
11117
  try {
@@ -11044,7 +11262,7 @@ function buildPackTools(connection) {
11044
11262
  "start_child_cloud_build",
11045
11263
  "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
11264
  {
11047
- childTaskId: z16.string().describe("The child task ID to start a cloud build for")
11265
+ childTaskId: z17.string().describe("The child task ID to start a cloud build for")
11048
11266
  },
11049
11267
  async ({ childTaskId }) => {
11050
11268
  try {
@@ -11073,7 +11291,7 @@ Base sync: dev \u2192 ${sync.branch} failed \u2014 ${sync.error}. The child was
11073
11291
  "stop_child_build",
11074
11292
  "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
11293
  {
11076
- childTaskId: z16.string().describe("The child task ID whose build should be stopped")
11294
+ childTaskId: z17.string().describe("The child task ID whose build should be stopped")
11077
11295
  },
11078
11296
  async ({ childTaskId }) => {
11079
11297
  try {
@@ -11132,7 +11350,7 @@ function buildPmTools(connection, options) {
11132
11350
  }
11133
11351
 
11134
11352
  // src/tools/discovery-tools.ts
11135
- import { z as z17 } from "zod";
11353
+ import { z as z18 } from "zod";
11136
11354
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique). The key is 'storyPointValue' \u2014 not 'storyPoints'.";
11137
11355
  var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk";
11138
11356
  function describeUpdatedFields(p) {
@@ -11151,12 +11369,12 @@ function buildDiscoveryTools(connection) {
11151
11369
  "update_task_properties",
11152
11370
  "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
11371
  {
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(
11372
+ title: z18.string().optional().describe("The new task title"),
11373
+ storyPointValue: z18.number().optional().describe(SP_DESCRIPTION2),
11374
+ tagNames: z18.array(z18.string()).optional().describe("Array of tag names to assign"),
11375
+ githubPRUrl: z18.string().url().optional().describe("GitHub pull request URL to link to this task"),
11376
+ githubBranch: z18.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
11377
+ risk: z18.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
11160
11378
  "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
11161
11379
  )
11162
11380
  },
@@ -11196,7 +11414,7 @@ function buildDiscoveryTools(connection) {
11196
11414
  }
11197
11415
 
11198
11416
  // src/tools/project-tools.ts
11199
- import { z as z18 } from "zod";
11417
+ import { z as z19 } from "zod";
11200
11418
 
11201
11419
  // src/execution/context-path-verifier.ts
11202
11420
  import { readFile as readFile2 } from "fs/promises";
@@ -11274,16 +11492,16 @@ function formatContextPathProblems(problems) {
11274
11492
  }
11275
11493
 
11276
11494
  // src/tools/project-tools.ts
11277
- var CONTEXT_PATH_SHAPE = z18.object({
11278
- type: z18.enum(["rule", "doc", "file", "folder"]).describe(
11495
+ var CONTEXT_PATH_SHAPE = z19.object({
11496
+ type: z19.enum(["rule", "doc", "file", "folder"]).describe(
11279
11497
  "Link kind \u2014 all paths are repo-relative; doc marks a synced project doc, which resolves from the workspace like rule/file"
11280
11498
  ),
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(
11499
+ path: z19.string().min(1).max(500).describe("Repo-relative path"),
11500
+ label: z19.string().max(100).optional(),
11501
+ locator: z19.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional().describe(
11284
11502
  '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
11503
  ),
11286
- locatorType: z18.enum(["test", "code"]).optional().describe(
11504
+ locatorType: z19.enum(["test", "code"]).optional().describe(
11287
11505
  "How the locator must match \u2014 required iff locator is set; not valid on folder links"
11288
11506
  )
11289
11507
  }).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
@@ -11332,15 +11550,15 @@ function buildCreateTagTool(connection, projectId, workspaceDir) {
11332
11550
  "create_tag",
11333
11551
  "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
11552
  {
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(
11553
+ name: z19.string().min(1).max(50),
11554
+ color: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional().describe("#RRGGBB (default gray)"),
11555
+ description: z19.string().max(TAG_DESCRIPTION_MAX).optional(),
11556
+ overview: z19.string().max(TAG_OVERVIEW_MAX).optional().describe("Full markdown glossary body \u2014 the term's spec"),
11557
+ overviewPath: z19.string().min(1).max(500).optional().describe(
11340
11558
  "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
11559
  ),
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()
11560
+ parentTagIds: z19.array(z19.string()).max(25).optional().describe("Parent tag ids from list_tags (multi-parent DAG) to link at create time"),
11561
+ contextPaths: z19.array(CONTEXT_PATH_SHAPE).max(20).optional()
11344
11562
  },
11345
11563
  async ({ name, color, description, overview, overviewPath, parentTagIds, contextPaths }) => {
11346
11564
  const rejection = await rejectBadContextPaths(contextPaths, workspaceDir);
@@ -11368,19 +11586,19 @@ function buildUpdateTagTool(connection, projectId, taskId, workspaceDir) {
11368
11586
  "update_tag",
11369
11587
  "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
11588
  {
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(
11589
+ tagId: z19.string().describe("Tag id from list_tags"),
11590
+ name: z19.string().min(1).max(50).optional(),
11591
+ color: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
11592
+ description: z19.string().max(TAG_DESCRIPTION_MAX).optional(),
11593
+ overview: z19.string().max(TAG_OVERVIEW_MAX).nullable().optional().describe(
11376
11594
  "Full markdown glossary body; null clears it. REJECTED while overviewPath is set \u2014 edit the sourced file in the repo instead"
11377
11595
  ),
11378
- overviewPath: z18.string().min(1).max(500).nullable().optional().describe(
11596
+ overviewPath: z19.string().min(1).max(500).nullable().optional().describe(
11379
11597
  "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
11598
  ),
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()
11599
+ parentTagIds: z19.array(z19.string()).max(25).optional().describe("Full-set replacement of the tag's parent tags (multi-parent DAG)"),
11600
+ reason: z19.string().max(TAG_REASON_MAX).optional().describe("One line on why \u2014 shown in the tag's revision history"),
11601
+ contextPaths: z19.array(CONTEXT_PATH_SHAPE).max(20).optional()
11384
11602
  },
11385
11603
  async ({
11386
11604
  tagId,
@@ -11438,8 +11656,8 @@ function buildPostToProjectChatTool(connection, projectId) {
11438
11656
  "post_to_project_chat",
11439
11657
  "Post a markdown message to the PROJECT chat \u2014 use once at the end of an audit for the summary the team reads.",
11440
11658
  {
11441
- message: z18.string().min(1).max(2e4),
11442
- kind: z18.enum(["tag_audit_summary"]).optional().describe(
11659
+ message: z19.string().min(1).max(2e4),
11660
+ kind: z19.enum(["tag_audit_summary"]).optional().describe(
11443
11661
  "Set to 'tag_audit_summary' when posting a tag-audit summary so it is also saved to the persistent tag history"
11444
11662
  )
11445
11663
  },
@@ -11458,7 +11676,7 @@ function buildGetProjectTaskTool(connection, projectId) {
11458
11676
  "get_project_task",
11459
11677
  "Fetch any task in the project by id or slug: title, description, plan, status, and metadata. The audit evidence trail starts here.",
11460
11678
  {
11461
- taskId: z18.string().describe("Task id or slug")
11679
+ taskId: z19.string().describe("Task id or slug")
11462
11680
  },
11463
11681
  async ({ taskId }) => {
11464
11682
  try {
@@ -11476,8 +11694,8 @@ function buildReadProjectTaskChatTool(connection, projectId) {
11476
11694
  "read_project_task_chat",
11477
11695
  "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
11696
  {
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)")
11697
+ taskId: z19.string().describe("Task id or slug"),
11698
+ limit: z19.number().int().min(1).max(200).optional().describe("Messages to fetch (default 50)")
11481
11699
  },
11482
11700
  async ({ taskId, limit }) => {
11483
11701
  try {
@@ -11499,9 +11717,9 @@ function buildGetProjectTaskLogsTool(connection, projectId) {
11499
11717
  "get_project_task_logs",
11500
11718
  "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
11719
  {
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)")
11720
+ taskId: z19.string().describe("Task id or slug"),
11721
+ limit: z19.number().int().min(1).max(500).optional().describe("Entries to fetch (default 50)"),
11722
+ source: z19.enum(["agent", "application"]).optional().describe("Filter: 'agent' = model events (default useful for grading)")
11505
11723
  },
11506
11724
  async ({ taskId, limit, source }) => {
11507
11725
  try {
@@ -11519,44 +11737,44 @@ function buildGetProjectTaskLogsTool(connection, projectId) {
11519
11737
  { annotations: { readOnlyHint: true } }
11520
11738
  );
11521
11739
  }
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")
11740
+ var TURN_GRADE_SHAPE = z19.object({
11741
+ turnIndex: z19.number().int().min(0),
11742
+ phase: z19.enum(["planning", "building", "human"]),
11743
+ grade: z19.enum(["correct", "neutral", "blunder"]),
11744
+ reasoning: z19.string(),
11745
+ eventType: z19.string().describe('e.g. "message", "tool_use", "human_message"'),
11746
+ eventSummary: z19.string().max(200).describe("\u2264120 chars of what happened this turn")
11529
11747
  });
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()
11748
+ var HUMAN_EVAL_SHAPE = z19.object({
11749
+ messageIndex: z19.number().int().min(0).describe("Index into the task's human messages, oldest first"),
11750
+ rating: z19.number().int().min(-1).max(1),
11751
+ reasoning: z19.string()
11534
11752
  });
11535
11753
  function buildReportTaskAuditResultTool(connection, projectId) {
11536
11754
  return defineTool(
11537
11755
  "report_task_audit_result",
11538
11756
  "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
11757
  {
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")
11758
+ taskId: z19.string().describe("The audited task's id (NOT slug)"),
11759
+ summary: z19.string().describe("3-6 sentences: what went well, what was wasted"),
11760
+ turnGrades: z19.array(TURN_GRADE_SHAPE),
11761
+ planningAccuracy: z19.number().min(0).max(1).nullable(),
11762
+ buildingAccuracy: z19.number().min(0).max(1).nullable(),
11763
+ humanAccuracy: z19.number().min(0).max(1).nullable(),
11764
+ planningCorrect: z19.number().int().min(0),
11765
+ planningNeutral: z19.number().int().min(0),
11766
+ planningBlunder: z19.number().int().min(0),
11767
+ buildingCorrect: z19.number().int().min(0),
11768
+ buildingNeutral: z19.number().int().min(0),
11769
+ buildingBlunder: z19.number().int().min(0),
11770
+ humanCorrect: z19.number().int().min(0),
11771
+ humanNeutral: z19.number().int().min(0),
11772
+ humanBlunder: z19.number().int().min(0),
11773
+ humanEvaluations: z19.array(HUMAN_EVAL_SHAPE).optional(),
11774
+ suggestionIds: z19.array(z19.string()).describe("Suggestion ids filed for this task, or []"),
11775
+ auditCostUsd: z19.number().nullable(),
11776
+ model: z19.string().nullable().describe("The model you are running as"),
11777
+ error: z19.string().optional().describe("Set ONLY to mark this task's audit failed")
11560
11778
  },
11561
11779
  async (input) => {
11562
11780
  try {
@@ -11849,6 +12067,13 @@ function errText4(prefix, error) {
11849
12067
  return textResult(`${prefix}: ${error instanceof Error ? error.message : "Unknown error"}`);
11850
12068
  }
11851
12069
  function buildMeetingTools(connection, projectId) {
12070
+ return [
12071
+ ...readTools(connection, projectId),
12072
+ ...writeTools(connection, projectId),
12073
+ ...checklistTools(connection, projectId)
12074
+ ];
12075
+ }
12076
+ function readTools(connection, projectId) {
11852
12077
  return [
11853
12078
  defineContractTool(
11854
12079
  listMeetingsContract,
@@ -11911,6 +12136,107 @@ ${res.lines.join("\n")}${footer}`);
11911
12136
  )
11912
12137
  ];
11913
12138
  }
12139
+ function writeTools(connection, projectId) {
12140
+ return [
12141
+ defineContractTool(createMeetingContract, async (input) => {
12142
+ try {
12143
+ const res = await connection.call("createProjectMeeting", {
12144
+ projectId,
12145
+ rawText: input.rawText,
12146
+ title: input.title,
12147
+ occurredAt: input.occurredAt
12148
+ });
12149
+ return textResult(
12150
+ `Created "${res.title}" (${res.segmentCount} segments, ${res.participants.length} participants).
12151
+ The AI summary is being written now; read it back with get_meeting in a few seconds.
12152
+ ${res.url}`
12153
+ );
12154
+ } catch (error) {
12155
+ return errText4("Failed to create the meeting", error);
12156
+ }
12157
+ }),
12158
+ defineContractTool(updateMeetingContract, async (input) => {
12159
+ try {
12160
+ const res = await connection.call("updateProjectMeeting", {
12161
+ projectId,
12162
+ meetingId: input.meetingId,
12163
+ title: input.title,
12164
+ occurredAt: input.occurredAt,
12165
+ summary: input.summary
12166
+ });
12167
+ return textResult(`Updated "${res.title}".
12168
+ ${res.url}`);
12169
+ } catch (error) {
12170
+ return errText4("Failed to update the meeting", error);
12171
+ }
12172
+ })
12173
+ ];
12174
+ }
12175
+ function describeItem(item) {
12176
+ const box = item.checked ? "[x]" : "[ ]";
12177
+ const who = item.checked && item.checkedBy ? ` \u2014 ${item.checkedBy}` : "";
12178
+ const card = item.linkedTaskSlug ? ` (${item.linkedTaskSlug})` : "";
12179
+ return `${box} ${item.title}${who}${card}`;
12180
+ }
12181
+ function checklistTools(connection, projectId) {
12182
+ return [
12183
+ defineContractTool(addMeetingChecklistItemsContract, async (input) => {
12184
+ try {
12185
+ const res = await connection.call("addProjectMeetingChecklistItems", {
12186
+ projectId,
12187
+ meetingId: input.meetingId,
12188
+ items: input.items
12189
+ });
12190
+ if (res.length === 0) {
12191
+ return textResult("No items added \u2014 every title is already on this checklist.");
12192
+ }
12193
+ return textResult(`Added ${res.length}:
12194
+ ${res.map(describeItem).join("\n")}`);
12195
+ } catch (error) {
12196
+ return errText4("Failed to add checklist items", error);
12197
+ }
12198
+ }),
12199
+ defineContractTool(checkMeetingChecklistItemContract, async (input) => {
12200
+ try {
12201
+ const res = await connection.call("checkProjectMeetingChecklistItem", {
12202
+ projectId,
12203
+ meetingId: input.meetingId,
12204
+ title: input.title,
12205
+ checked: input.checked,
12206
+ linkedTask: input.linkedTask
12207
+ });
12208
+ return textResult(describeItem(res));
12209
+ } catch (error) {
12210
+ return errText4("Failed to update the checklist item", error);
12211
+ }
12212
+ }),
12213
+ defineContractTool(editMeetingChecklistItemContract, async (input) => {
12214
+ try {
12215
+ const res = await connection.call("editProjectMeetingChecklistItem", {
12216
+ projectId,
12217
+ meetingId: input.meetingId,
12218
+ title: input.title,
12219
+ newTitle: input.newTitle
12220
+ });
12221
+ return textResult(describeItem(res));
12222
+ } catch (error) {
12223
+ return errText4("Failed to edit the checklist item", error);
12224
+ }
12225
+ }),
12226
+ defineContractTool(removeMeetingChecklistItemContract, async (input) => {
12227
+ try {
12228
+ const res = await connection.call("removeProjectMeetingChecklistItem", {
12229
+ projectId,
12230
+ meetingId: input.meetingId,
12231
+ title: input.title
12232
+ });
12233
+ return textResult(`Removed "${res.title}".`);
12234
+ } catch (error) {
12235
+ return errText4("Failed to remove the checklist item", error);
12236
+ }
12237
+ })
12238
+ ];
12239
+ }
11914
12240
 
11915
12241
  // src/tools/connected-tools.ts
11916
12242
  function connectedToolsFor(connection, context) {
@@ -11930,7 +12256,7 @@ function connectedToolsFor(connection, context) {
11930
12256
  // src/tools/code-review-tools.ts
11931
12257
  import { execFile } from "child_process";
11932
12258
  import { promisify } from "util";
11933
- import { z as z19 } from "zod";
12259
+ import { z as z20 } from "zod";
11934
12260
  async function endReviewSession(connection, reason) {
11935
12261
  await connection.call("endReviewSession", {
11936
12262
  sessionId: connection.sessionId,
@@ -11938,26 +12264,26 @@ async function endReviewSession(connection, reason) {
11938
12264
  });
11939
12265
  }
11940
12266
  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.");
12267
+ var reviewedShaSchema = z20.string().regex(/^[0-9a-f]{40}$/i).describe("REQUIRED. The full 40-character commit SHA this verdict reviews.");
11942
12268
  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(
12269
+ var ReviewGuideToolSchema = z20.strictObject({
12270
+ reviewedSha: z20.string().regex(/^[0-9a-f]{40}$/i).describe(
11945
12271
  "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
12272
  ),
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(
12273
+ overview: z20.string().min(1).max(6e4).describe("REQUIRED. Plain-text walkthrough intro, max 3000 characters. Keep it short."),
12274
+ sections: z20.array(
12275
+ z20.strictObject({
12276
+ title: z20.string().min(1).max(160),
12277
+ explanation: z20.string().min(1).max(2e3),
12278
+ classification: z20.enum(["core", "supporting"]).optional(),
12279
+ files: z20.array(
12280
+ z20.strictObject({
12281
+ path: z20.string().min(1).max(500).describe(
11956
12282
  "A file the PR's diff actually changed. Context files you merely read are rejected."
11957
12283
  ),
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(
12284
+ startLine: z20.number().int().positive().max(1e6).optional(),
12285
+ endLine: z20.number().int().positive().max(1e6).optional(),
12286
+ hunkHeader: z20.string().min(1).max(300).optional().describe(
11961
12287
  "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
12288
  )
11963
12289
  })
@@ -12041,8 +12367,8 @@ function buildApproveCodeReviewTool(connection) {
12041
12367
  "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
12368
  {
12043
12369
  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)
12370
+ summary: z20.string().describe("Brief summary of what was reviewed and why it looks good"),
12371
+ risk: z20.enum(RISK_LEVELS2).describe(riskDescription)
12046
12372
  },
12047
12373
  async ({ reviewedSha, summary, risk }) => {
12048
12374
  const content = `**Code Review: Approved** :white_check_mark:
@@ -12073,16 +12399,16 @@ function buildRequestCodeChangesTool(connection) {
12073
12399
  "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
12400
  {
12075
12401
  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")
12402
+ issues: z20.array(
12403
+ z20.object({
12404
+ file: z20.string().describe("File path where the issue was found"),
12405
+ line: z20.number().optional().describe("Line number (if applicable)"),
12406
+ severity: z20.enum(["critical", "major", "minor"]).describe("Issue severity"),
12407
+ description: z20.string().describe("What is wrong and how to fix it")
12082
12408
  })
12083
12409
  ).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)
12410
+ summary: z20.string().describe("Brief overall summary of the review findings"),
12411
+ risk: z20.enum(RISK_LEVELS2).describe(riskDescription)
12086
12412
  },
12087
12413
  async ({ reviewedSha, issues, summary, risk }) => {
12088
12414
  const issueLines = issues.map((issue) => {
@@ -12152,7 +12478,7 @@ function getModeTools(agentMode, connection, config, context) {
12152
12478
  }
12153
12479
  }
12154
12480
  function buildPrGuideToolsFor(effectiveMode, connection, config, context) {
12155
- const isLeafBuild = effectiveMode === "building" || effectiveMode === "auto";
12481
+ const isLeafBuild = effectiveMode === "building" || effectiveMode === "auto" || effectiveMode === "chat";
12156
12482
  const isSinglePodPack = config.mode === "pack" && config.packExecution !== "fan-out";
12157
12483
  return (isLeafBuild || isSinglePodPack) && (isSinglePodPack || !context?.isParentTask) ? [
12158
12484
  buildPublishReviewGuideTool(connection, {
@@ -12639,10 +12965,14 @@ async function processAssistantCase(event, host, state) {
12639
12965
  }
12640
12966
  const usage = await handleAssistantCase(event, host, state.turnToolCalls);
12641
12967
  if (usage) state.lastAssistantUsage = usage;
12642
- if (!state.sawApiError) {
12968
+ if (!state.sawApiError || event.isApiError && !state.terminalApiError) {
12643
12969
  const fullText = event.message.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
12644
12970
  if (API_ERROR_PATTERN.test(fullText)) {
12645
12971
  state.sawApiError = true;
12972
+ if (event.isApiError) {
12973
+ state.terminalApiError = true;
12974
+ state.apiErrorText = fullText.slice(0, 300);
12975
+ }
12646
12976
  }
12647
12977
  }
12648
12978
  }
@@ -12657,7 +12987,7 @@ async function processResultCase(event, host, context, startTime, state) {
12657
12987
  );
12658
12988
  if (info.stoppedTyping) state.isTyping = false;
12659
12989
  state.retriable = info.retriable;
12660
- if (!info.retriable) state.sawApiError = false;
12990
+ if (!info.retriable && !state.terminalApiError) state.sawApiError = false;
12661
12991
  state.resultSummary = info.resultSummary;
12662
12992
  if (info.staleSession) state.staleSession = true;
12663
12993
  if (info.authError) state.authError = true;
@@ -12678,6 +13008,8 @@ async function processEvents(events, context, host) {
12678
13008
  isTyping: false,
12679
13009
  retriable: false,
12680
13010
  sawApiError: false,
13011
+ terminalApiError: false,
13012
+ apiErrorText: void 0,
12681
13013
  resultSummary: void 0,
12682
13014
  rateLimitResetsAt: void 0,
12683
13015
  rateLimitRejectedType: void 0,
@@ -12720,8 +13052,12 @@ async function processEvents(events, context, host) {
12720
13052
  }
12721
13053
  flushPendingToolCalls(host, state.turnToolCalls);
12722
13054
  stopTypingIfNeeded(host, state.isTyping);
13055
+ return buildProcessResult(state);
13056
+ }
13057
+ function buildProcessResult(state) {
12723
13058
  return {
12724
13059
  retriable: state.retriable || state.sawApiError,
13060
+ ...state.apiErrorText && { apiErrorText: state.apiErrorText },
12725
13061
  resultSummary: state.resultSummary,
12726
13062
  rateLimitResetsAt: state.rateLimitResetsAt,
12727
13063
  ...state.rateLimitRejectedType && { rateLimitRejectedType: state.rateLimitRejectedType },
@@ -12964,25 +13300,6 @@ function handleReviewToolAccess(toolName, input) {
12964
13300
  }
12965
13301
  return handleBuildingToolAccess(toolName, input);
12966
13302
  }
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
13303
  function handleAutoToolAccess(toolName, input, hasExitedPlanMode, isParentTask) {
12987
13304
  if (hasExitedPlanMode) {
12988
13305
  return isParentTask ? handleReviewToolAccess(toolName, input) : handleBuildingToolAccess(toolName, input);
@@ -13162,7 +13479,6 @@ function resolveToolAccess(host, toolName, input) {
13162
13479
  }
13163
13480
  function buildCanUseTool(host) {
13164
13481
  let consecutiveDenials = 0;
13165
- let chatHasPlan = false;
13166
13482
  return async (toolName, input) => {
13167
13483
  if (toolName === "ExitPlanMode" && (host.agentMode === "auto" || host.agentMode === "discovery") && !host.hasExitedPlanMode) {
13168
13484
  return await handleExitPlanMode(host, input);
@@ -13178,14 +13494,6 @@ function buildCanUseTool(host) {
13178
13494
  }
13179
13495
  const loopResult = checkToolLoop(host, toolName, input);
13180
13496
  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
13497
  const result = resolveToolAccess(host, toolName, input);
13190
13498
  if (result.behavior === "deny") {
13191
13499
  consecutiveDenials++;
@@ -13955,11 +14263,13 @@ function handleProcessResult(result, context, host, options) {
13955
14263
  }
13956
14264
  async function runWithRetry(initialQuery, context, host, options) {
13957
14265
  let lastErrorWasImage = false;
14266
+ let lastDetail = null;
13958
14267
  for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
13959
14268
  if (host.isStopped()) return;
13960
14269
  const agentQuery = attempt === 0 ? initialQuery : await buildRetryQuery(host, context, options, lastErrorWasImage);
13961
14270
  try {
13962
14271
  const result = await processEvents(agentQuery, context, host);
14272
+ if (result.apiErrorText) lastDetail = result.apiErrorText;
13963
14273
  const outcome = handleProcessResult(result, context, host, options);
13964
14274
  if (outcome.action === "return") return;
13965
14275
  if (outcome.action === "return_promise") return outcome.promise;
@@ -13968,12 +14278,16 @@ async function runWithRetry(initialQuery, context, host, options) {
13968
14278
  const outcome = handleRetryError(error, context, host, options, lastErrorWasImage);
13969
14279
  if (outcome instanceof Promise) return outcome;
13970
14280
  if (outcome.action === "return") return;
14281
+ lastDetail = getErrorMessage(error).slice(0, 300);
13971
14282
  lastErrorWasImage = outcome.lastErrorWasImage;
13972
14283
  }
13973
14284
  if (attempt >= RETRY_DELAYS_MS.length) {
14285
+ const detail = lastDetail ?? "API error";
13974
14286
  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.`
14287
+ `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
14288
  );
14289
+ host.apiOutageExhausted = true;
14290
+ host.apiOutageDetail = detail;
13977
14291
  return;
13978
14292
  }
13979
14293
  await emitRetryStatus(host, attempt, RETRY_DELAYS_MS[attempt]);
@@ -14043,6 +14357,8 @@ var QueryBridge = class {
14043
14357
  _isParentTask = false;
14044
14358
  _wasRateLimited = false;
14045
14359
  _wedgeAborted = false;
14360
+ _apiOutageExhausted = false;
14361
+ _apiOutageDetail = null;
14046
14362
  _keyCycleCount = 0;
14047
14363
  _abortController = null;
14048
14364
  /** Called by SessionRunner when ExitPlanMode triggers a mode transition. */
@@ -14081,6 +14397,18 @@ var QueryBridge = class {
14081
14397
  get lastTurnWedgeAborted() {
14082
14398
  return this._wedgeAborted;
14083
14399
  }
14400
+ /**
14401
+ * Whether the turn that just ran ended with every Claude API retry
14402
+ * exhausted. Reset at the start of every execute(); SessionRunner reads it
14403
+ * to flush WIP and hand the card back to the server.
14404
+ */
14405
+ get lastTurnApiOutage() {
14406
+ return this._apiOutageExhausted;
14407
+ }
14408
+ /** The API error text behind `lastTurnApiOutage`, or null. */
14409
+ get lastTurnApiOutageDetail() {
14410
+ return this._apiOutageDetail;
14411
+ }
14084
14412
  /**
14085
14413
  * Ask the live terminal (PTY harness only) to redraw its full screen.
14086
14414
  * Safe no-op on the SDK harness or between queries.
@@ -14155,6 +14483,8 @@ var QueryBridge = class {
14155
14483
  this._stopped = false;
14156
14484
  this._wasRateLimited = false;
14157
14485
  this._wedgeAborted = false;
14486
+ this._apiOutageExhausted = false;
14487
+ this._apiOutageDetail = null;
14158
14488
  this._abortController = new AbortController();
14159
14489
  const host = this.buildHost();
14160
14490
  try {
@@ -14204,7 +14534,7 @@ var QueryBridge = class {
14204
14534
  // ── QueryHost construction ──────────────────────────────────────────
14205
14535
  buildHost() {
14206
14536
  const bridge = this;
14207
- return {
14537
+ const host = {
14208
14538
  config: this.runnerConfig,
14209
14539
  connection: this.connection,
14210
14540
  callbacks: this.callbacks,
@@ -14247,12 +14577,6 @@ var QueryBridge = class {
14247
14577
  set wasRateLimited(val) {
14248
14578
  bridge._wasRateLimited = val;
14249
14579
  },
14250
- get wedgeAborted() {
14251
- return bridge._wedgeAborted;
14252
- },
14253
- set wedgeAborted(val) {
14254
- bridge._wedgeAborted = val;
14255
- },
14256
14580
  get keyCycleCount() {
14257
14581
  return bridge._keyCycleCount;
14258
14582
  },
@@ -14276,6 +14600,38 @@ var QueryBridge = class {
14276
14600
  createInputStream: (prompt) => bridge.createInputStream(prompt),
14277
14601
  onModeTransition: bridge.onModeTransition
14278
14602
  };
14603
+ return Object.defineProperties(host, this.turnFlagDescriptors());
14604
+ }
14605
+ /**
14606
+ * The per-turn recovery flags the executor writes and SessionRunner reads
14607
+ * after the turn (`lastTurnWedgeAborted`, `lastTurnApiOutage`). Live
14608
+ * accessors onto the bridge's backing fields, attached as descriptors
14609
+ * because an object spread would snapshot a getter's value instead.
14610
+ */
14611
+ turnFlagDescriptors() {
14612
+ return {
14613
+ wedgeAborted: {
14614
+ enumerable: true,
14615
+ get: () => this._wedgeAborted,
14616
+ set: (val) => {
14617
+ this._wedgeAborted = val;
14618
+ }
14619
+ },
14620
+ apiOutageExhausted: {
14621
+ enumerable: true,
14622
+ get: () => this._apiOutageExhausted,
14623
+ set: (val) => {
14624
+ this._apiOutageExhausted = val;
14625
+ }
14626
+ },
14627
+ apiOutageDetail: {
14628
+ enumerable: true,
14629
+ get: () => this._apiOutageDetail,
14630
+ set: (val) => {
14631
+ this._apiOutageDetail = val;
14632
+ }
14633
+ }
14634
+ };
14279
14635
  }
14280
14636
  // ── Input stream for Claude SDK ─────────────────────────────────────
14281
14637
  async *createInputStream(prompt) {
@@ -14769,7 +15125,7 @@ function resolveQuestionGraceMs() {
14769
15125
  }
14770
15126
  var QUESTION_TYPING_RECENT_MS = 6e4;
14771
15127
  var QUESTION_TYPING_DEFER_MS = 2 * 6e4;
14772
- var QUESTION_TIMEOUT_PROMPT = 'No one answered your clarifying questions within 10 minutes. Do this now: (1) Save your findings-so-far as the card plan (`update_task`). (2) Post the unanswered questions to chat with `post_to_chat` using `milestone: "blocked"`, stating the assumption you would make for each. (3) If you can safely proceed on your best-judgment answers, state the assumptions and continue; otherwise end your turn \u2014 answers posted in chat will be delivered when you resume.';
15128
+ var QUESTION_TIMEOUT_PROMPT = 'No one answered your clarifying questions within 10 minutes. Do this now: (1) Save your findings-so-far as the card plan (`update_task`). (2) Post the unanswered questions to chat with `post_to_chat` using `milestone: "blocked"`, stating the assumption you would make for each (this is a parking note \u2014 the question already paged them). (3) If you can safely proceed on your best-judgment answers, state the assumptions and continue; otherwise end your turn \u2014 answers posted in chat will be delivered when you resume.';
14773
15129
  var SessionRunner = class _SessionRunner {
14774
15130
  connection;
14775
15131
  mode;
@@ -15620,6 +15976,27 @@ var SessionRunner = class _SessionRunner {
15620
15976
  }
15621
15977
  throw err;
15622
15978
  }
15979
+ await this.handleApiOutageExhausted();
15980
+ }
15981
+ /**
15982
+ * The turn ended with every Claude API retry exhausted (the CLI's own
15983
+ * retries gave up, then `runWithRetry`'s fresh-session backoff did too).
15984
+ * Push WIP first — the server sleeps this pod in response, so the flush
15985
+ * must not race the teardown — then hand the card back. The core loop
15986
+ * then falls into its normal idle wait; the server-driven sleep is what
15987
+ * ends the pod, and nothing here exits the process or re-prompts.
15988
+ *
15989
+ * NOT a stuck nudge: this keys on the CLI's own error record, never on
15990
+ * silence or idleness (see `.claude/rules/agent-runner.md`).
15991
+ */
15992
+ async handleApiOutageExhausted() {
15993
+ if (!this.queryBridge?.lastTurnApiOutage) return;
15994
+ process.stderr.write("[conveyor-agent] API retries exhausted \u2014 handing the card back\n");
15995
+ await this.flushWipNow("WIP: API outage hand-off");
15996
+ this.connection.reportApiOutage(
15997
+ this.queryBridge.lastTurnApiOutageDetail ?? "API error",
15998
+ RETRY_DELAYS_MS.length
15999
+ );
15623
16000
  }
15624
16001
  /**
15625
16002
  * A human typed into the parked, idle Connected-TUI (keep-alive PTY). Wake the