@task-boards/mcp-server 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +1826 -1092
- package/build/cli.js.map +4 -4
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -25,6 +25,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
mod
|
|
26
26
|
));
|
|
27
27
|
|
|
28
|
+
// ../api/build/agent-runs/agent-runs.api.js
|
|
29
|
+
var require_agent_runs_api = __commonJS({
|
|
30
|
+
"../api/build/agent-runs/agent-runs.api.js"(exports) {
|
|
31
|
+
"use strict";
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.AGENT_RUNS_ROUTES = exports.AGENT_RUNS_PREFIX = void 0;
|
|
34
|
+
exports.AGENT_RUNS_PREFIX = "/agent-runs";
|
|
35
|
+
exports.AGENT_RUNS_ROUTES = {
|
|
36
|
+
listAgentRuns: () => exports.AGENT_RUNS_PREFIX,
|
|
37
|
+
claimAgentRuns: () => `${exports.AGENT_RUNS_PREFIX}/claim`,
|
|
38
|
+
listAgentRunsByWorkItem: (workItemId) => `/work-items/${workItemId}/agent-runs`,
|
|
39
|
+
retryAgentRun: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/retry`,
|
|
40
|
+
acknowledgeAgentRun: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/acknowledge`,
|
|
41
|
+
setAgentRunAttention: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/attention`,
|
|
42
|
+
completeAgentRun: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/complete`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
28
47
|
// ../api/build/api-prefix.js
|
|
29
48
|
var require_api_prefix = __commonJS({
|
|
30
49
|
"../api/build/api-prefix.js"(exports) {
|
|
@@ -102,6 +121,7 @@ var require_agent_run_outcome_enum = __commonJS({
|
|
|
102
121
|
exports.AGENT_RUN_OUTCOME = {
|
|
103
122
|
DEFAULT: "DEFAULT",
|
|
104
123
|
SKIP_DESIGN: "SKIP_DESIGN",
|
|
124
|
+
SKIP_DEV: "SKIP_DEV",
|
|
105
125
|
HAS_BUGS: "HAS_BUGS",
|
|
106
126
|
NEEDS_CLARIFICATION: "NEEDS_CLARIFICATION",
|
|
107
127
|
FAILED: "FAILED"
|
|
@@ -794,7 +814,8 @@ var require_feedback_api = __commonJS({
|
|
|
794
814
|
listFeedback: () => exports.FEEDBACK_ADMIN_PREFIX,
|
|
795
815
|
getUnreadSummary: () => `${exports.FEEDBACK_ADMIN_PREFIX}/unread-summary`,
|
|
796
816
|
markFeedbackRead: () => `${exports.FEEDBACK_ADMIN_PREFIX}/mark-read`,
|
|
797
|
-
getFeedback: (id) => `${exports.FEEDBACK_ADMIN_PREFIX}/${id}
|
|
817
|
+
getFeedback: (id) => `${exports.FEEDBACK_ADMIN_PREFIX}/${id}`,
|
|
818
|
+
deleteFeedback: (id) => `${exports.FEEDBACK_ADMIN_PREFIX}/${id}`
|
|
798
819
|
};
|
|
799
820
|
}
|
|
800
821
|
});
|
|
@@ -860,7 +881,8 @@ var require_notifications_api = __commonJS({
|
|
|
860
881
|
listNotifications: () => exports.NOTIFICATIONS_PREFIX,
|
|
861
882
|
getUnreadCount: () => `${exports.NOTIFICATIONS_PREFIX}/unread-count`,
|
|
862
883
|
markNotificationRead: (id) => `${exports.NOTIFICATIONS_PREFIX}/${id}/mark-read`,
|
|
863
|
-
markAllNotificationsRead: () => `${exports.NOTIFICATIONS_PREFIX}/mark-all-read
|
|
884
|
+
markAllNotificationsRead: () => `${exports.NOTIFICATIONS_PREFIX}/mark-all-read`,
|
|
885
|
+
clearAllNotifications: () => `${exports.NOTIFICATIONS_PREFIX}/clear-all`
|
|
864
886
|
};
|
|
865
887
|
}
|
|
866
888
|
});
|
|
@@ -915,25 +937,6 @@ var require_notifications = __commonJS({
|
|
|
915
937
|
}
|
|
916
938
|
});
|
|
917
939
|
|
|
918
|
-
// ../api/build/agent-runs/agent-runs.api.js
|
|
919
|
-
var require_agent_runs_api = __commonJS({
|
|
920
|
-
"../api/build/agent-runs/agent-runs.api.js"(exports) {
|
|
921
|
-
"use strict";
|
|
922
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
923
|
-
exports.AGENT_RUNS_ROUTES = exports.AGENT_RUNS_PREFIX = void 0;
|
|
924
|
-
exports.AGENT_RUNS_PREFIX = "/agent-runs";
|
|
925
|
-
exports.AGENT_RUNS_ROUTES = {
|
|
926
|
-
listAgentRuns: () => exports.AGENT_RUNS_PREFIX,
|
|
927
|
-
claimAgentRuns: () => `${exports.AGENT_RUNS_PREFIX}/claim`,
|
|
928
|
-
listAgentRunsByWorkItem: (workItemId) => `/work-items/${workItemId}/agent-runs`,
|
|
929
|
-
retryAgentRun: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/retry`,
|
|
930
|
-
acknowledgeAgentRun: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/acknowledge`,
|
|
931
|
-
setAgentRunAttention: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/attention`,
|
|
932
|
-
completeAgentRun: (id) => `${exports.AGENT_RUNS_PREFIX}/${id}/complete`
|
|
933
|
-
};
|
|
934
|
-
}
|
|
935
|
-
});
|
|
936
|
-
|
|
937
940
|
// ../api/build/agent-runs/agent-runs.types.js
|
|
938
941
|
var require_agent_runs_types = __commonJS({
|
|
939
942
|
"../api/build/agent-runs/agent-runs.types.js"(exports) {
|
|
@@ -1661,10 +1664,16 @@ var require_exception_codes = __commonJS({
|
|
|
1661
1664
|
LABEL_NAME_CONFLICT: "LABEL_NAME_CONFLICT",
|
|
1662
1665
|
LABEL_INVALID_IDS: "LABEL_INVALID_IDS",
|
|
1663
1666
|
FEEDBACK_NOT_FOUND: "FEEDBACK_NOT_FOUND",
|
|
1667
|
+
FEEDBACK_NOT_READ: "FEEDBACK_NOT_READ",
|
|
1664
1668
|
STAFF_ROLE_ALREADY_ASSIGNED: "STAFF_ROLE_ALREADY_ASSIGNED",
|
|
1665
1669
|
STAFF_ROLE_NOT_ASSIGNED: "STAFF_ROLE_NOT_ASSIGNED",
|
|
1666
1670
|
STAFF_ROLE_INCOMPATIBLE_WITH_ADMIN: "STAFF_ROLE_INCOMPATIBLE_WITH_ADMIN",
|
|
1667
1671
|
CANNOT_ASSIGN_STAFF_ROLE_TO_SERVICE_USER: "CANNOT_ASSIGN_STAFF_ROLE_TO_SERVICE_USER",
|
|
1672
|
+
USER_BLOCKED: "USER_BLOCKED",
|
|
1673
|
+
CANNOT_BLOCK_SELF: "CANNOT_BLOCK_SELF",
|
|
1674
|
+
CANNOT_BLOCK_ADMIN: "CANNOT_BLOCK_ADMIN",
|
|
1675
|
+
USER_ALREADY_BLOCKED: "USER_ALREADY_BLOCKED",
|
|
1676
|
+
USER_NOT_BLOCKED: "USER_NOT_BLOCKED",
|
|
1668
1677
|
SUBSCRIPTION_PROJECT_LIMIT_REACHED: "SUBSCRIPTION_PROJECT_LIMIT_REACHED",
|
|
1669
1678
|
SUBSCRIPTION_MEMBER_LIMIT_REACHED: "SUBSCRIPTION_MEMBER_LIMIT_REACHED",
|
|
1670
1679
|
SUBSCRIPTION_PROJECT_READ_ONLY: "SUBSCRIPTION_PROJECT_READ_ONLY",
|
|
@@ -1682,7 +1691,11 @@ var require_exception_codes = __commonJS({
|
|
|
1682
1691
|
ADMIN_NOTIFICATION_SETTINGS_NOT_FOUND: "ADMIN_NOTIFICATION_SETTINGS_NOT_FOUND",
|
|
1683
1692
|
ADMIN_NOTIFICATION_CHANNEL_INVALID: "ADMIN_NOTIFICATION_CHANNEL_INVALID",
|
|
1684
1693
|
ADMIN_NOTIFICATION_EMAIL_RECIPIENT_REQUIRED: "ADMIN_NOTIFICATION_EMAIL_RECIPIENT_REQUIRED",
|
|
1685
|
-
ADMIN_NOTIFICATION_TELEGRAM_CONFIG_INCOMPLETE: "ADMIN_NOTIFICATION_TELEGRAM_CONFIG_INCOMPLETE"
|
|
1694
|
+
ADMIN_NOTIFICATION_TELEGRAM_CONFIG_INCOMPLETE: "ADMIN_NOTIFICATION_TELEGRAM_CONFIG_INCOMPLETE",
|
|
1695
|
+
PAYMENTS_DISABLED: "PAYMENTS_DISABLED",
|
|
1696
|
+
PAYMENTS_NOT_CONFIGURED: "PAYMENTS_NOT_CONFIGURED",
|
|
1697
|
+
PAYMENTS_PROBE_FAILED: "PAYMENTS_PROBE_FAILED",
|
|
1698
|
+
SYSTEM_READ_ONLY: "SYSTEM_READ_ONLY"
|
|
1686
1699
|
};
|
|
1687
1700
|
}
|
|
1688
1701
|
});
|
|
@@ -1695,7 +1708,8 @@ var require_health_api = __commonJS({
|
|
|
1695
1708
|
exports.HEALTH_ROUTES = exports.HEALTH_PREFIX = void 0;
|
|
1696
1709
|
exports.HEALTH_PREFIX = "/health";
|
|
1697
1710
|
exports.HEALTH_ROUTES = {
|
|
1698
|
-
getHealth: () => exports.HEALTH_PREFIX
|
|
1711
|
+
getHealth: () => exports.HEALTH_PREFIX,
|
|
1712
|
+
getReadiness: () => `${exports.HEALTH_PREFIX}/ready`
|
|
1699
1713
|
};
|
|
1700
1714
|
}
|
|
1701
1715
|
});
|
|
@@ -1705,10 +1719,18 @@ var require_health_types = __commonJS({
|
|
|
1705
1719
|
"../shared/build/internal/health/health.types.js"(exports) {
|
|
1706
1720
|
"use strict";
|
|
1707
1721
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1708
|
-
exports.HEALTH_STATUS = void 0;
|
|
1722
|
+
exports.READINESS_CHECK_STATUS = exports.READINESS_STATUS = exports.HEALTH_STATUS = void 0;
|
|
1709
1723
|
exports.HEALTH_STATUS = {
|
|
1710
1724
|
OK: "ok"
|
|
1711
1725
|
};
|
|
1726
|
+
exports.READINESS_STATUS = {
|
|
1727
|
+
OK: "ok",
|
|
1728
|
+
ERROR: "error"
|
|
1729
|
+
};
|
|
1730
|
+
exports.READINESS_CHECK_STATUS = {
|
|
1731
|
+
UP: "up",
|
|
1732
|
+
DOWN: "down"
|
|
1733
|
+
};
|
|
1712
1734
|
}
|
|
1713
1735
|
});
|
|
1714
1736
|
|
|
@@ -1745,6 +1767,19 @@ var require_health = __commonJS({
|
|
|
1745
1767
|
}
|
|
1746
1768
|
});
|
|
1747
1769
|
|
|
1770
|
+
// ../shared/build/internal/platform/platform-status.api.js
|
|
1771
|
+
var require_platform_status_api = __commonJS({
|
|
1772
|
+
"../shared/build/internal/platform/platform-status.api.js"(exports) {
|
|
1773
|
+
"use strict";
|
|
1774
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1775
|
+
exports.PLATFORM_STATUS_ROUTES = exports.PLATFORM_STATUS_PREFIX = void 0;
|
|
1776
|
+
exports.PLATFORM_STATUS_PREFIX = "/platform/status";
|
|
1777
|
+
exports.PLATFORM_STATUS_ROUTES = {
|
|
1778
|
+
getPlatformStatus: () => exports.PLATFORM_STATUS_PREFIX
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1748
1783
|
// ../shared/build/internal/admin/admin.api.js
|
|
1749
1784
|
var require_admin_api = __commonJS({
|
|
1750
1785
|
"../shared/build/internal/admin/admin.api.js"(exports) {
|
|
@@ -1758,6 +1793,44 @@ var require_admin_api = __commonJS({
|
|
|
1758
1793
|
}
|
|
1759
1794
|
});
|
|
1760
1795
|
|
|
1796
|
+
// ../shared/build/internal/platform/platform-read-only-admin.api.js
|
|
1797
|
+
var require_platform_read_only_admin_api = __commonJS({
|
|
1798
|
+
"../shared/build/internal/platform/platform-read-only-admin.api.js"(exports) {
|
|
1799
|
+
"use strict";
|
|
1800
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1801
|
+
exports.PLATFORM_READ_ONLY_ADMIN_ROUTES = exports.PLATFORM_READ_ONLY_ADMIN_PREFIX = void 0;
|
|
1802
|
+
var admin_api_1 = require_admin_api();
|
|
1803
|
+
exports.PLATFORM_READ_ONLY_ADMIN_PREFIX = `${admin_api_1.ADMIN_PREFIX}/platform-read-only`;
|
|
1804
|
+
exports.PLATFORM_READ_ONLY_ADMIN_ROUTES = {
|
|
1805
|
+
getPlatformReadOnly: () => exports.PLATFORM_READ_ONLY_ADMIN_PREFIX,
|
|
1806
|
+
updatePlatformReadOnly: () => exports.PLATFORM_READ_ONLY_ADMIN_PREFIX
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
// ../shared/build/internal/platform/index.js
|
|
1812
|
+
var require_platform = __commonJS({
|
|
1813
|
+
"../shared/build/internal/platform/index.js"(exports) {
|
|
1814
|
+
"use strict";
|
|
1815
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1816
|
+
exports.PLATFORM_READ_ONLY_ADMIN_ROUTES = exports.PLATFORM_READ_ONLY_ADMIN_PREFIX = exports.PLATFORM_STATUS_ROUTES = exports.PLATFORM_STATUS_PREFIX = void 0;
|
|
1817
|
+
var platform_status_api_1 = require_platform_status_api();
|
|
1818
|
+
Object.defineProperty(exports, "PLATFORM_STATUS_PREFIX", { enumerable: true, get: function() {
|
|
1819
|
+
return platform_status_api_1.PLATFORM_STATUS_PREFIX;
|
|
1820
|
+
} });
|
|
1821
|
+
Object.defineProperty(exports, "PLATFORM_STATUS_ROUTES", { enumerable: true, get: function() {
|
|
1822
|
+
return platform_status_api_1.PLATFORM_STATUS_ROUTES;
|
|
1823
|
+
} });
|
|
1824
|
+
var platform_read_only_admin_api_1 = require_platform_read_only_admin_api();
|
|
1825
|
+
Object.defineProperty(exports, "PLATFORM_READ_ONLY_ADMIN_PREFIX", { enumerable: true, get: function() {
|
|
1826
|
+
return platform_read_only_admin_api_1.PLATFORM_READ_ONLY_ADMIN_PREFIX;
|
|
1827
|
+
} });
|
|
1828
|
+
Object.defineProperty(exports, "PLATFORM_READ_ONLY_ADMIN_ROUTES", { enumerable: true, get: function() {
|
|
1829
|
+
return platform_read_only_admin_api_1.PLATFORM_READ_ONLY_ADMIN_ROUTES;
|
|
1830
|
+
} });
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1761
1834
|
// ../shared/build/internal/admin/admin-notification-channel.enum.js
|
|
1762
1835
|
var require_admin_notification_channel_enum = __commonJS({
|
|
1763
1836
|
"../shared/build/internal/admin/admin-notification-channel.enum.js"(exports) {
|
|
@@ -1798,11 +1871,27 @@ var require_admin_users_api = __commonJS({
|
|
|
1798
1871
|
exports.ADMIN_USERS_ROUTES = {
|
|
1799
1872
|
listUsers: () => `${admin_api_1.ADMIN_PREFIX}/users`,
|
|
1800
1873
|
assignStaffRole: (userId) => `${admin_api_1.ADMIN_PREFIX}/users/${userId}/staff-roles`,
|
|
1801
|
-
revokeStaffRole: (userId, role) => `${admin_api_1.ADMIN_PREFIX}/users/${userId}/staff-roles/${role}
|
|
1874
|
+
revokeStaffRole: (userId, role) => `${admin_api_1.ADMIN_PREFIX}/users/${userId}/staff-roles/${role}`,
|
|
1875
|
+
blockUser: (userId) => `${admin_api_1.ADMIN_PREFIX}/users/${userId}/block`,
|
|
1876
|
+
unblockUser: (userId) => `${admin_api_1.ADMIN_PREFIX}/users/${userId}/unblock`
|
|
1802
1877
|
};
|
|
1803
1878
|
}
|
|
1804
1879
|
});
|
|
1805
1880
|
|
|
1881
|
+
// ../shared/build/internal/admin/user-block-status.enum.js
|
|
1882
|
+
var require_user_block_status_enum = __commonJS({
|
|
1883
|
+
"../shared/build/internal/admin/user-block-status.enum.js"(exports) {
|
|
1884
|
+
"use strict";
|
|
1885
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1886
|
+
exports.UserBlockStatus = void 0;
|
|
1887
|
+
var UserBlockStatus;
|
|
1888
|
+
(function(UserBlockStatus2) {
|
|
1889
|
+
UserBlockStatus2["ACTIVE"] = "ACTIVE";
|
|
1890
|
+
UserBlockStatus2["BLOCKED"] = "BLOCKED";
|
|
1891
|
+
})(UserBlockStatus || (exports.UserBlockStatus = UserBlockStatus = {}));
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1806
1895
|
// ../shared/build/internal/admin/admin-users.consts.js
|
|
1807
1896
|
var require_admin_users_consts = __commonJS({
|
|
1808
1897
|
"../shared/build/internal/admin/admin-users.consts.js"(exports) {
|
|
@@ -1842,7 +1931,7 @@ var require_admin = __commonJS({
|
|
|
1842
1931
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
|
|
1843
1932
|
};
|
|
1844
1933
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1845
|
-
exports.REGISTERED_WITHIN_DAYS_OPTIONS = exports.ADMIN_USERS_ROUTES = exports.ADMIN_NOTIFICATION_SETTINGS_ROUTES = exports.ADMIN_NOTIFICATION_SETTINGS_PREFIX = exports.ADMIN_NOTIFICATION_CHANNEL = exports.ADMIN_ROUTES = exports.ADMIN_PREFIX = void 0;
|
|
1934
|
+
exports.REGISTERED_WITHIN_DAYS_OPTIONS = exports.UserBlockStatus = exports.ADMIN_USERS_ROUTES = exports.ADMIN_NOTIFICATION_SETTINGS_ROUTES = exports.ADMIN_NOTIFICATION_SETTINGS_PREFIX = exports.ADMIN_NOTIFICATION_CHANNEL = exports.ADMIN_ROUTES = exports.ADMIN_PREFIX = void 0;
|
|
1846
1935
|
var admin_api_1 = require_admin_api();
|
|
1847
1936
|
Object.defineProperty(exports, "ADMIN_PREFIX", { enumerable: true, get: function() {
|
|
1848
1937
|
return admin_api_1.ADMIN_PREFIX;
|
|
@@ -1865,6 +1954,10 @@ var require_admin = __commonJS({
|
|
|
1865
1954
|
Object.defineProperty(exports, "ADMIN_USERS_ROUTES", { enumerable: true, get: function() {
|
|
1866
1955
|
return admin_users_api_1.ADMIN_USERS_ROUTES;
|
|
1867
1956
|
} });
|
|
1957
|
+
var user_block_status_enum_1 = require_user_block_status_enum();
|
|
1958
|
+
Object.defineProperty(exports, "UserBlockStatus", { enumerable: true, get: function() {
|
|
1959
|
+
return user_block_status_enum_1.UserBlockStatus;
|
|
1960
|
+
} });
|
|
1868
1961
|
var admin_users_consts_1 = require_admin_users_consts();
|
|
1869
1962
|
Object.defineProperty(exports, "REGISTERED_WITHIN_DAYS_OPTIONS", { enumerable: true, get: function() {
|
|
1870
1963
|
return admin_users_consts_1.REGISTERED_WITHIN_DAYS_OPTIONS;
|
|
@@ -1873,6 +1966,36 @@ var require_admin = __commonJS({
|
|
|
1873
1966
|
}
|
|
1874
1967
|
});
|
|
1875
1968
|
|
|
1969
|
+
// ../shared/build/internal/payments/admin-payments.api.js
|
|
1970
|
+
var require_admin_payments_api = __commonJS({
|
|
1971
|
+
"../shared/build/internal/payments/admin-payments.api.js"(exports) {
|
|
1972
|
+
"use strict";
|
|
1973
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1974
|
+
exports.ADMIN_PAYMENTS_ROUTES = exports.ADMIN_PAYMENTS_PREFIX = void 0;
|
|
1975
|
+
var admin_api_1 = require_admin_api();
|
|
1976
|
+
exports.ADMIN_PAYMENTS_PREFIX = `${admin_api_1.ADMIN_PREFIX}/payments`;
|
|
1977
|
+
exports.ADMIN_PAYMENTS_ROUTES = {
|
|
1978
|
+
probePaymentsConnectivity: () => `${exports.ADMIN_PAYMENTS_PREFIX}/probe`
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
});
|
|
1982
|
+
|
|
1983
|
+
// ../shared/build/internal/payments/index.js
|
|
1984
|
+
var require_payments = __commonJS({
|
|
1985
|
+
"../shared/build/internal/payments/index.js"(exports) {
|
|
1986
|
+
"use strict";
|
|
1987
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1988
|
+
exports.ADMIN_PAYMENTS_ROUTES = exports.ADMIN_PAYMENTS_PREFIX = void 0;
|
|
1989
|
+
var admin_payments_api_1 = require_admin_payments_api();
|
|
1990
|
+
Object.defineProperty(exports, "ADMIN_PAYMENTS_PREFIX", { enumerable: true, get: function() {
|
|
1991
|
+
return admin_payments_api_1.ADMIN_PAYMENTS_PREFIX;
|
|
1992
|
+
} });
|
|
1993
|
+
Object.defineProperty(exports, "ADMIN_PAYMENTS_ROUTES", { enumerable: true, get: function() {
|
|
1994
|
+
return admin_payments_api_1.ADMIN_PAYMENTS_ROUTES;
|
|
1995
|
+
} });
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1876
1999
|
// ../shared/build/internal/subscriptions/subscription-source.enum.js
|
|
1877
2000
|
var require_subscription_source_enum = __commonJS({
|
|
1878
2001
|
"../shared/build/internal/subscriptions/subscription-source.enum.js"(exports) {
|
|
@@ -2089,7 +2212,9 @@ var require_internal = __commonJS({
|
|
|
2089
2212
|
__exportStar(require_imports(), exports);
|
|
2090
2213
|
__exportStar(require_exception_codes(), exports);
|
|
2091
2214
|
__exportStar(require_health(), exports);
|
|
2215
|
+
__exportStar(require_platform(), exports);
|
|
2092
2216
|
__exportStar(require_admin(), exports);
|
|
2217
|
+
__exportStar(require_payments(), exports);
|
|
2093
2218
|
__exportStar(require_subscriptions2(), exports);
|
|
2094
2219
|
}
|
|
2095
2220
|
});
|
|
@@ -2428,7 +2553,8 @@ var require_agentic_sdlc_workflow = __commonJS({
|
|
|
2428
2553
|
"in-analysis": {
|
|
2429
2554
|
defaultNextSlug: "in-development",
|
|
2430
2555
|
outcomes: {
|
|
2431
|
-
[agent_run_outcome_enum_1.AGENT_RUN_OUTCOME.SKIP_DESIGN]: "in-development"
|
|
2556
|
+
[agent_run_outcome_enum_1.AGENT_RUN_OUTCOME.SKIP_DESIGN]: "in-development",
|
|
2557
|
+
[agent_run_outcome_enum_1.AGENT_RUN_OUTCOME.SKIP_DEV]: "released"
|
|
2432
2558
|
}
|
|
2433
2559
|
},
|
|
2434
2560
|
"in-development": {
|
|
@@ -2470,7 +2596,7 @@ var require_agentic_sdlc_workflow = __commonJS({
|
|
|
2470
2596
|
nextAgentRole
|
|
2471
2597
|
};
|
|
2472
2598
|
}
|
|
2473
|
-
function resolveInAnalysisTransition(outcome, workflowPhase) {
|
|
2599
|
+
function resolveInAnalysisTransition(outcome, workflowPhase, context) {
|
|
2474
2600
|
if (outcome === agent_run_outcome_enum_1.AGENT_RUN_OUTCOME.NEEDS_CLARIFICATION) {
|
|
2475
2601
|
return moveTransition(exports.IN_AWAITING_COLUMN_SLUG, workflowPhase, null);
|
|
2476
2602
|
}
|
|
@@ -2491,6 +2617,12 @@ var require_agentic_sdlc_workflow = __commonJS({
|
|
|
2491
2617
|
if (outcome === agent_run_outcome_enum_1.AGENT_RUN_OUTCOME.SKIP_DESIGN) {
|
|
2492
2618
|
return moveTransition("in-development", workflow_phase_enum_1.WORKFLOW_PHASE.DEVELOPMENT, null);
|
|
2493
2619
|
}
|
|
2620
|
+
if (outcome === agent_run_outcome_enum_1.AGENT_RUN_OUTCOME.SKIP_DEV) {
|
|
2621
|
+
if (context.codeChangesRequired !== false) {
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2624
|
+
return moveTransition("released", null, null);
|
|
2625
|
+
}
|
|
2494
2626
|
return null;
|
|
2495
2627
|
}
|
|
2496
2628
|
if (workflowPhase === workflow_phase_enum_1.WORKFLOW_PHASE.ARCHITECT) {
|
|
@@ -2566,7 +2698,7 @@ var require_agentic_sdlc_workflow = __commonJS({
|
|
|
2566
2698
|
}
|
|
2567
2699
|
if (currentColumnSlug === "in-analysis") {
|
|
2568
2700
|
const phase = context.workflowPhase ?? (0, workflow_phase_subagent_role_map_1.subagentRoleToWorkflowPhase)(context.agentRole);
|
|
2569
|
-
return resolveInAnalysisTransition(outcome, phase);
|
|
2701
|
+
return resolveInAnalysisTransition(outcome, phase, context);
|
|
2570
2702
|
}
|
|
2571
2703
|
if (currentColumnSlug === "in-development") {
|
|
2572
2704
|
return resolveInDevelopmentTransition(outcome, context);
|
|
@@ -3326,7 +3458,8 @@ var require_mcp_sensitive_data_util = __commonJS({
|
|
|
3326
3458
|
exports.MCP_REDACTED_HOST = "[REDACTED_HOST]";
|
|
3327
3459
|
exports.MCP_REDACTED_PORT = "[REDACTED_PORT]";
|
|
3328
3460
|
exports.MCP_WORKSPACE_PLACEHOLDER = "[workspace]";
|
|
3329
|
-
var SENSITIVE_OBJECT_KEY_PATTERN = /^(password|passwd|secret|clientsecret|client_secret|apikey|api_key|token|authorization|smtp_password|tokenpepper|token_pepper|privatekey|private_key)$/i;
|
|
3461
|
+
var SENSITIVE_OBJECT_KEY_PATTERN = /^(password|passwd|secret|clientsecret|client_secret|apikey|api_key|apisecret|api_secret|token|authorization|smtp_password|tokenpepper|token_pepper|privatekey|private_key|pan|cardnumber|card_number)$/i;
|
|
3462
|
+
var PAN_PATTERN = /\b(?:\d[ -]*?){13,19}\b/g;
|
|
3330
3463
|
var BLOCKED_ATTACHMENT_FILE_NAME_PATTERN = /^(?:\.env(?:\..*)?|secrets\.ya?ml|config\.ya?ml|.*\.pem|.*\.p12|.*\.key|id_rsa(?:\..*)?|credentials\.json|\.npmrc|docker-compose\.ya?ml|compose\.ya?ml)$/i;
|
|
3331
3464
|
var JWT_PATTERN = /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g;
|
|
3332
3465
|
var BEARER_PATTERN = /Bearer\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
@@ -3373,6 +3506,7 @@ var require_mcp_sensitive_data_util = __commonJS({
|
|
|
3373
3506
|
result = result.replace(HTTPS_REMOTE_URL_PATTERN, `${exports.MCP_REDACTED_HOST}/...`);
|
|
3374
3507
|
result = result.replace(ENV_ASSIGNMENT_PATTERN, `${exports.MCP_REDACTED}_ENV`);
|
|
3375
3508
|
result = result.replace(NPM_TOKEN_PATTERN, `NpmToken.${exports.MCP_REDACTED}`);
|
|
3509
|
+
result = result.replace(PAN_PATTERN, exports.MCP_REDACTED);
|
|
3376
3510
|
return result;
|
|
3377
3511
|
}
|
|
3378
3512
|
function redactSensitiveValueDeep(value, workspaceRoot) {
|
|
@@ -3551,43 +3685,44 @@ var require_build2 = __commonJS({
|
|
|
3551
3685
|
}
|
|
3552
3686
|
});
|
|
3553
3687
|
|
|
3554
|
-
// src/
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3688
|
+
// src/orchestrator/orchestrator-transport.ts
|
|
3689
|
+
var import_agent_runs2 = __toESM(require_agent_runs_api(), 1);
|
|
3690
|
+
var import_shared5 = __toESM(require_build2(), 1);
|
|
3558
3691
|
|
|
3559
|
-
// src/
|
|
3560
|
-
var
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3692
|
+
// src/tools/attachments.ts
|
|
3693
|
+
var import_attachments = __toESM(require_attachments_api(), 1);
|
|
3694
|
+
var import_shared3 = __toESM(require_build2(), 1);
|
|
3695
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
3696
|
+
import { z } from "zod";
|
|
3697
|
+
|
|
3698
|
+
// src/attachments/detect-mime-type.ts
|
|
3699
|
+
import { basename } from "node:path";
|
|
3700
|
+
import { lookup as lookupMimeType } from "mime-types";
|
|
3701
|
+
function detectMimeType(filePath) {
|
|
3702
|
+
const mimeType = lookupMimeType(basename(filePath));
|
|
3703
|
+
return typeof mimeType === "string" ? mimeType : "application/octet-stream";
|
|
3568
3704
|
}
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3705
|
+
|
|
3706
|
+
// src/attachments/staging.util.ts
|
|
3707
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3708
|
+
import { dirname, resolve } from "node:path";
|
|
3709
|
+
var MAX_FILE_NAME_LENGTH = 200;
|
|
3710
|
+
function sanitizeFileName(name) {
|
|
3711
|
+
const baseName = name.split(/[/\\]/).pop() ?? name;
|
|
3712
|
+
const sanitized = baseName.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3713
|
+
return sanitized.length > MAX_FILE_NAME_LENGTH ? sanitized.slice(0, MAX_FILE_NAME_LENGTH) : sanitized;
|
|
3576
3714
|
}
|
|
3577
|
-
function
|
|
3578
|
-
const
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
sanitizeResponses: resolveSanitizeResponses(),
|
|
3589
|
-
blockSensitiveAttachments: resolveBlockSensitiveAttachments()
|
|
3590
|
-
};
|
|
3715
|
+
function buildStagingPath(workspaceRoot, workItemId, attachmentId, fileName) {
|
|
3716
|
+
const sanitizedFileName = sanitizeFileName(fileName);
|
|
3717
|
+
return resolve(workspaceRoot, ".task-boards", "attachments", workItemId, `${attachmentId}_${sanitizedFileName}`);
|
|
3718
|
+
}
|
|
3719
|
+
function buildStagingDir(workspaceRoot, workItemId) {
|
|
3720
|
+
return resolve(workspaceRoot, ".task-boards", "attachments", workItemId);
|
|
3721
|
+
}
|
|
3722
|
+
async function writeStagingFile(path, data) {
|
|
3723
|
+
const absolutePath = resolve(path);
|
|
3724
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
3725
|
+
await writeFile(absolutePath, data);
|
|
3591
3726
|
}
|
|
3592
3727
|
|
|
3593
3728
|
// src/rest-client.ts
|
|
@@ -3694,6 +3829,121 @@ var RestClient = class {
|
|
|
3694
3829
|
}
|
|
3695
3830
|
};
|
|
3696
3831
|
|
|
3832
|
+
// src/workspace/confine-file-path.ts
|
|
3833
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3834
|
+
import { basename as basename2, isAbsolute, relative, resolve as resolve2 } from "node:path";
|
|
3835
|
+
|
|
3836
|
+
// src/workspace/workspace-error.ts
|
|
3837
|
+
var WorkspaceError = class extends Error {
|
|
3838
|
+
constructor(code, message) {
|
|
3839
|
+
super(message);
|
|
3840
|
+
this.code = code;
|
|
3841
|
+
this.name = "WorkspaceError";
|
|
3842
|
+
}
|
|
3843
|
+
};
|
|
3844
|
+
|
|
3845
|
+
// src/workspace/confine-file-path.ts
|
|
3846
|
+
function confineFilePath(workspaceRoot, filePath) {
|
|
3847
|
+
const resolvedRoot = resolve2(workspaceRoot);
|
|
3848
|
+
const resolvedPath = isAbsolute(filePath) ? resolve2(filePath) : resolve2(resolvedRoot, filePath);
|
|
3849
|
+
const relativePath = relative(resolvedRoot, resolvedPath);
|
|
3850
|
+
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
3851
|
+
throw new WorkspaceError(
|
|
3852
|
+
"FILE_OUT_OF_BOUNDS",
|
|
3853
|
+
`filePath is outside workspaceRoot: ${basename2(filePath) || filePath}`
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
if (!existsSync(resolvedPath)) {
|
|
3857
|
+
throw new WorkspaceError("FILE_NOT_FOUND", `filePath does not exist: ${basename2(filePath) || filePath}`);
|
|
3858
|
+
}
|
|
3859
|
+
const stats = statSync(resolvedPath);
|
|
3860
|
+
if (!stats.isFile()) {
|
|
3861
|
+
throw new WorkspaceError("FILE_NOT_FOUND", `filePath is not a file: ${basename2(filePath) || filePath}`);
|
|
3862
|
+
}
|
|
3863
|
+
return resolvedPath;
|
|
3864
|
+
}
|
|
3865
|
+
function readConfinedWorkspaceFile(workspaceRoot, filePath) {
|
|
3866
|
+
const absolutePath = confineFilePath(workspaceRoot, filePath);
|
|
3867
|
+
const stats = statSync(absolutePath);
|
|
3868
|
+
const buffer = readFileSync(absolutePath);
|
|
3869
|
+
return {
|
|
3870
|
+
absolutePath,
|
|
3871
|
+
fileName: basename2(absolutePath),
|
|
3872
|
+
sizeBytes: stats.size,
|
|
3873
|
+
buffer
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3877
|
+
// src/workspace/resolve-workspace-root.ts
|
|
3878
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "node:fs";
|
|
3879
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join, relative as relative2, resolve as resolve3 } from "node:path";
|
|
3880
|
+
var TASK_BOARDS_FILE = ".task-boards.yaml";
|
|
3881
|
+
var MAX_UPWARD_LEVELS = 20;
|
|
3882
|
+
function assertDirectory(path, source) {
|
|
3883
|
+
const absolutePath = resolve3(path);
|
|
3884
|
+
if (!existsSync2(absolutePath)) {
|
|
3885
|
+
throw new WorkspaceError("WORKSPACE_NOT_FOUND", `${source} does not exist: ${absolutePath}`);
|
|
3886
|
+
}
|
|
3887
|
+
const stats = statSync2(absolutePath);
|
|
3888
|
+
if (!stats.isDirectory()) {
|
|
3889
|
+
throw new WorkspaceError("WORKSPACE_NOT_FOUND", `${source} is not a directory: ${absolutePath}`);
|
|
3890
|
+
}
|
|
3891
|
+
return absolutePath;
|
|
3892
|
+
}
|
|
3893
|
+
function assertWithinAllowedRoot(target, allowedRoot) {
|
|
3894
|
+
const relativePath = relative2(allowedRoot, target);
|
|
3895
|
+
const isNested = relativePath !== "" && !relativePath.startsWith("..") && !isAbsolute2(relativePath);
|
|
3896
|
+
if (target !== allowedRoot && !isNested) {
|
|
3897
|
+
throw new WorkspaceError(
|
|
3898
|
+
"WORKSPACE_OUT_OF_BOUNDS",
|
|
3899
|
+
`workspaceRoot is outside the allowed WORKSPACE_ROOT: ${target}`
|
|
3900
|
+
);
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
function assertGitRepository(absolutePath) {
|
|
3904
|
+
if (!existsSync2(join(absolutePath, ".git"))) {
|
|
3905
|
+
throw new WorkspaceError("WORKSPACE_NOT_GIT_REPO", `workspaceRoot is not a git repository: ${absolutePath}`);
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
function findWorkspaceRootUpward(startDir) {
|
|
3909
|
+
let current = resolve3(startDir);
|
|
3910
|
+
for (let level = 0; level <= MAX_UPWARD_LEVELS; level += 1) {
|
|
3911
|
+
const markerPath = join(current, TASK_BOARDS_FILE);
|
|
3912
|
+
if (existsSync2(markerPath)) {
|
|
3913
|
+
return current;
|
|
3914
|
+
}
|
|
3915
|
+
const parent = dirname2(current);
|
|
3916
|
+
if (parent === current) {
|
|
3917
|
+
break;
|
|
3918
|
+
}
|
|
3919
|
+
current = parent;
|
|
3920
|
+
}
|
|
3921
|
+
return null;
|
|
3922
|
+
}
|
|
3923
|
+
function resolveWorkspaceRoot(explicitOverride, envWorkspaceRoot) {
|
|
3924
|
+
const allowedRoot = envWorkspaceRoot !== void 0 && envWorkspaceRoot.trim() !== "" ? assertDirectory(envWorkspaceRoot, "WORKSPACE_ROOT") : void 0;
|
|
3925
|
+
if (explicitOverride !== void 0 && explicitOverride.trim() !== "") {
|
|
3926
|
+
const resolved = assertDirectory(explicitOverride, "workspaceRoot");
|
|
3927
|
+
if (allowedRoot !== void 0) {
|
|
3928
|
+
assertWithinAllowedRoot(resolved, allowedRoot);
|
|
3929
|
+
} else {
|
|
3930
|
+
assertGitRepository(resolved);
|
|
3931
|
+
}
|
|
3932
|
+
return resolved;
|
|
3933
|
+
}
|
|
3934
|
+
if (allowedRoot !== void 0) {
|
|
3935
|
+
return allowedRoot;
|
|
3936
|
+
}
|
|
3937
|
+
const fromMarker = findWorkspaceRootUpward(process.cwd());
|
|
3938
|
+
if (fromMarker !== null) {
|
|
3939
|
+
return fromMarker;
|
|
3940
|
+
}
|
|
3941
|
+
return resolve3(process.cwd());
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
// src/tools/tool-utils.ts
|
|
3945
|
+
var import_shared2 = __toESM(require_build2(), 1);
|
|
3946
|
+
|
|
3697
3947
|
// src/tool-runtime.ts
|
|
3698
3948
|
var serverConfig = null;
|
|
3699
3949
|
function initToolRuntime(config) {
|
|
@@ -3706,26 +3956,579 @@ function shouldSanitizeResponses() {
|
|
|
3706
3956
|
return serverConfig.sanitizeResponses !== false;
|
|
3707
3957
|
}
|
|
3708
3958
|
|
|
3709
|
-
// src/tools/
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3959
|
+
// src/tools/tool-utils.ts
|
|
3960
|
+
function jsonResult(data) {
|
|
3961
|
+
return {
|
|
3962
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3965
|
+
function toolError(code, message) {
|
|
3966
|
+
const sanitizedMessage = shouldSanitizeResponses() ? (0, import_shared2.sanitizeMcpErrorMessage)(message) : message;
|
|
3967
|
+
return {
|
|
3968
|
+
isError: true,
|
|
3969
|
+
content: [{ type: "text", text: JSON.stringify({ code, message: sanitizedMessage }) }]
|
|
3970
|
+
};
|
|
3971
|
+
}
|
|
3972
|
+
function toToolError(error) {
|
|
3973
|
+
if (error instanceof RestClientError || error instanceof WorkspaceError) {
|
|
3974
|
+
return toolError(error.code, error.message);
|
|
3975
|
+
}
|
|
3976
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3977
|
+
return toolError("INTERNAL_ERROR", message);
|
|
3978
|
+
}
|
|
3979
|
+
async function runTool(handler, options) {
|
|
3980
|
+
try {
|
|
3981
|
+
let data = await handler();
|
|
3982
|
+
if (shouldSanitizeResponses()) {
|
|
3983
|
+
data = (0, import_shared2.sanitizeMcpToolResult)(data, { workspaceRoot: options?.workspaceRoot });
|
|
3984
|
+
}
|
|
3985
|
+
return jsonResult(data);
|
|
3986
|
+
} catch (error) {
|
|
3987
|
+
return toToolError(error);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3991
|
+
// src/tools/attachments.ts
|
|
3992
|
+
var MAX_ATTACHMENT_COUNT = 20;
|
|
3993
|
+
var MAX_TOTAL_BYTES = 200 * 1024 * 1024;
|
|
3994
|
+
async function fetchAttachments(client, workItemId) {
|
|
3995
|
+
return client.get(import_attachments.ATTACHMENTS_ROUTES.listAttachments(workItemId));
|
|
3996
|
+
}
|
|
3997
|
+
function filterAttachments(items, attachmentIds) {
|
|
3998
|
+
if (attachmentIds === void 0 || attachmentIds.length === 0) {
|
|
3999
|
+
return items;
|
|
4000
|
+
}
|
|
4001
|
+
const idSet = new Set(attachmentIds);
|
|
4002
|
+
return items.filter((item) => idSet.has(item.id));
|
|
4003
|
+
}
|
|
4004
|
+
function assertDownloadLimits(attachments) {
|
|
4005
|
+
if (attachments.length > MAX_ATTACHMENT_COUNT) {
|
|
4006
|
+
throw new RestClientError(
|
|
4007
|
+
"ATTACHMENT_LIMIT_EXCEEDED",
|
|
4008
|
+
`Cannot download more than ${MAX_ATTACHMENT_COUNT} attachments at once (requested ${attachments.length})`
|
|
4009
|
+
);
|
|
4010
|
+
}
|
|
4011
|
+
const totalBytes = attachments.reduce((sum, attachment) => sum + attachment.sizeBytes, 0);
|
|
4012
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
4013
|
+
throw new RestClientError(
|
|
4014
|
+
"ATTACHMENT_SIZE_LIMIT_EXCEEDED",
|
|
4015
|
+
`Total attachment size ${totalBytes} bytes exceeds limit of ${MAX_TOTAL_BYTES} bytes`
|
|
4016
|
+
);
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
async function downloadWorkItemAttachments(client, params) {
|
|
4020
|
+
const { workItemId, workspaceRoot, attachmentIds, overwrite = false, blockSensitiveAttachments = true } = params;
|
|
4021
|
+
const listResponse = await fetchAttachments(client, workItemId);
|
|
4022
|
+
const selected = filterAttachments(listResponse.items, attachmentIds);
|
|
4023
|
+
assertDownloadLimits(selected);
|
|
4024
|
+
const stagingDir = buildStagingDir(workspaceRoot, workItemId);
|
|
4025
|
+
const downloaded = [];
|
|
4026
|
+
const skipped = [];
|
|
4027
|
+
if (attachmentIds !== void 0 && attachmentIds.length > 0) {
|
|
4028
|
+
const selectedIds = new Set(selected.map((item) => item.id));
|
|
4029
|
+
for (const attachmentId of attachmentIds) {
|
|
4030
|
+
if (!selectedIds.has(attachmentId)) {
|
|
4031
|
+
skipped.push({ attachmentId, reason: "not found on work item" });
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
for (const attachment of selected) {
|
|
4036
|
+
if (blockSensitiveAttachments && (0, import_shared3.isBlockedMcpAttachmentFileName)(attachment.fileName)) {
|
|
4037
|
+
skipped.push({ attachmentId: attachment.id, reason: "blocked_sensitive_filename" });
|
|
4038
|
+
continue;
|
|
4039
|
+
}
|
|
4040
|
+
const stagingPath = buildStagingPath(workspaceRoot, workItemId, attachment.id, attachment.fileName);
|
|
4041
|
+
if (!overwrite && existsSync3(stagingPath)) {
|
|
4042
|
+
skipped.push({ attachmentId: attachment.id, reason: "file already exists (set overwrite=true to replace)" });
|
|
4043
|
+
continue;
|
|
4044
|
+
}
|
|
4045
|
+
const { data } = await client.downloadBinary(import_attachments.ATTACHMENTS_ROUTES.downloadAttachment(workItemId, attachment.id));
|
|
4046
|
+
await writeStagingFile(stagingPath, data);
|
|
4047
|
+
downloaded.push({
|
|
4048
|
+
attachmentId: attachment.id,
|
|
4049
|
+
fileName: attachment.fileName,
|
|
4050
|
+
path: stagingPath,
|
|
4051
|
+
sizeBytes: data.length
|
|
4052
|
+
});
|
|
4053
|
+
}
|
|
4054
|
+
return {
|
|
4055
|
+
workItemId,
|
|
4056
|
+
downloaded,
|
|
4057
|
+
skipped,
|
|
4058
|
+
stagingDir
|
|
4059
|
+
};
|
|
4060
|
+
}
|
|
4061
|
+
async function uploadWorkItemAttachment(client, params) {
|
|
4062
|
+
const { workItemId, workspaceRoot, filePath, blockSensitiveAttachments = true } = params;
|
|
4063
|
+
const confinedFile = readConfinedWorkspaceFile(workspaceRoot, filePath);
|
|
4064
|
+
if (blockSensitiveAttachments && (0, import_shared3.isBlockedMcpAttachmentFileName)(confinedFile.fileName)) {
|
|
4065
|
+
throw new RestClientError(
|
|
4066
|
+
"BLOCKED_SENSITIVE_FILENAME",
|
|
4067
|
+
`Attachment filename is blocked for MCP upload: ${confinedFile.fileName}`
|
|
4068
|
+
);
|
|
4069
|
+
}
|
|
4070
|
+
const mimeType = detectMimeType(confinedFile.absolutePath);
|
|
4071
|
+
const attachment = await client.uploadMultipart(
|
|
4072
|
+
import_attachments.ATTACHMENTS_ROUTES.uploadAttachment(workItemId),
|
|
4073
|
+
"file",
|
|
4074
|
+
{
|
|
4075
|
+
buffer: confinedFile.buffer,
|
|
4076
|
+
fileName: confinedFile.fileName,
|
|
4077
|
+
mimeType
|
|
4078
|
+
}
|
|
4079
|
+
);
|
|
4080
|
+
return {
|
|
4081
|
+
workItemId,
|
|
4082
|
+
attachment,
|
|
4083
|
+
sourcePath: confinedFile.absolutePath
|
|
4084
|
+
};
|
|
4085
|
+
}
|
|
4086
|
+
function registerAttachmentTools(server, client, config) {
|
|
4087
|
+
server.registerTool(
|
|
4088
|
+
"list_work_item_attachments",
|
|
4089
|
+
{
|
|
4090
|
+
description: "List file attachments for a work item.",
|
|
4091
|
+
inputSchema: {
|
|
4092
|
+
workItemId: z.string().uuid().describe("Work item UUID")
|
|
4093
|
+
}
|
|
4094
|
+
},
|
|
4095
|
+
async ({ workItemId }) => runTool(async () => fetchAttachments(client, workItemId))
|
|
4096
|
+
);
|
|
4097
|
+
server.registerTool(
|
|
4098
|
+
"download_work_item_attachments",
|
|
4099
|
+
{
|
|
4100
|
+
description: "Download work item attachments into the local workspace staging directory (.task-boards/attachments).",
|
|
4101
|
+
inputSchema: {
|
|
4102
|
+
workItemId: z.string().uuid().describe("Work item UUID"),
|
|
4103
|
+
attachmentIds: z.array(z.string().uuid()).optional().describe("Optional subset of attachment UUIDs; downloads all when omitted"),
|
|
4104
|
+
overwrite: z.boolean().optional().default(false).describe("Replace existing staged files when true"),
|
|
4105
|
+
workspaceRoot: z.string().optional().describe("Optional absolute path to git repo root; defaults to WORKSPACE_ROOT or upward search from cwd")
|
|
4106
|
+
}
|
|
4107
|
+
},
|
|
4108
|
+
async ({ workItemId, attachmentIds, overwrite, workspaceRoot }) => {
|
|
4109
|
+
const resolvedWorkspaceRoot = resolveWorkspaceRoot(workspaceRoot, config.workspaceRoot);
|
|
4110
|
+
return runTool(
|
|
4111
|
+
async () => downloadWorkItemAttachments(client, {
|
|
4112
|
+
workItemId,
|
|
4113
|
+
workspaceRoot: resolvedWorkspaceRoot,
|
|
4114
|
+
attachmentIds,
|
|
4115
|
+
overwrite,
|
|
4116
|
+
blockSensitiveAttachments: config.blockSensitiveAttachments
|
|
4117
|
+
}),
|
|
4118
|
+
{ workspaceRoot: resolvedWorkspaceRoot }
|
|
4119
|
+
);
|
|
4120
|
+
}
|
|
4121
|
+
);
|
|
4122
|
+
server.registerTool(
|
|
4123
|
+
"upload_work_item_attachment",
|
|
4124
|
+
{
|
|
4125
|
+
description: "Upload a local workspace file as a work item attachment (multipart field file).",
|
|
4126
|
+
inputSchema: {
|
|
4127
|
+
workItemId: z.string().uuid().describe("Work item UUID"),
|
|
4128
|
+
filePath: z.string().describe("Absolute or workspace-relative path to the file; must stay within workspaceRoot"),
|
|
4129
|
+
workspaceRoot: z.string().optional().describe("Optional absolute path to git repo root; defaults to WORKSPACE_ROOT or upward search from cwd")
|
|
4130
|
+
}
|
|
4131
|
+
},
|
|
4132
|
+
async ({ workItemId, filePath, workspaceRoot }) => {
|
|
4133
|
+
const resolvedWorkspaceRoot = resolveWorkspaceRoot(workspaceRoot, config.workspaceRoot);
|
|
4134
|
+
return runTool(
|
|
4135
|
+
async () => uploadWorkItemAttachment(client, {
|
|
4136
|
+
workItemId,
|
|
4137
|
+
workspaceRoot: resolvedWorkspaceRoot,
|
|
4138
|
+
filePath,
|
|
4139
|
+
blockSensitiveAttachments: config.blockSensitiveAttachments
|
|
4140
|
+
}),
|
|
4141
|
+
{ workspaceRoot: resolvedWorkspaceRoot }
|
|
4142
|
+
);
|
|
4143
|
+
}
|
|
4144
|
+
);
|
|
4145
|
+
}
|
|
4146
|
+
|
|
4147
|
+
// src/tools/comments.ts
|
|
4148
|
+
var import_comments = __toESM(require_comments_api(), 1);
|
|
4149
|
+
import { z as z2 } from "zod";
|
|
4150
|
+
async function fetchComments(client, workItemId) {
|
|
4151
|
+
return client.get(import_comments.COMMENTS_ROUTES.listComments(workItemId));
|
|
4152
|
+
}
|
|
4153
|
+
async function postComment(client, workItemId, body) {
|
|
4154
|
+
const request = { body };
|
|
4155
|
+
return client.post(import_comments.COMMENTS_ROUTES.createComment(workItemId), request);
|
|
4156
|
+
}
|
|
4157
|
+
function registerCommentTools(server, client) {
|
|
4158
|
+
server.registerTool(
|
|
4159
|
+
"list_comments",
|
|
4160
|
+
{
|
|
4161
|
+
description: "List comments for a work item (newest last).",
|
|
4162
|
+
inputSchema: {
|
|
4163
|
+
workItemId: z2.string().uuid().describe("Work item UUID")
|
|
4164
|
+
}
|
|
4165
|
+
},
|
|
4166
|
+
async ({ workItemId }) => runTool(async () => fetchComments(client, workItemId))
|
|
4167
|
+
);
|
|
4168
|
+
server.registerTool(
|
|
4169
|
+
"create_comment",
|
|
4170
|
+
{
|
|
4171
|
+
description: "Create a comment on a work item. Use for orchestrator NEEDS_CLARIFICATION questions before complete_agent_run.",
|
|
4172
|
+
inputSchema: {
|
|
4173
|
+
workItemId: z2.string().uuid().describe("Work item UUID"),
|
|
4174
|
+
body: z2.string().min(1).describe("Comment body (markdown supported)")
|
|
4175
|
+
}
|
|
4176
|
+
},
|
|
4177
|
+
async ({ workItemId, body }) => runTool(async () => postComment(client, workItemId, body))
|
|
4178
|
+
);
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
// src/subagents/sync-project-subagents.ts
|
|
4182
|
+
var import_shared4 = __toESM(require_build2(), 1);
|
|
4183
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4184
|
+
import { join as join2 } from "node:path";
|
|
4185
|
+
var SAFE_SUBAGENT_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
4186
|
+
var MAX_SUBAGENT_SLUG_LENGTH = 64;
|
|
4187
|
+
function isSyncableSlug(slug) {
|
|
4188
|
+
const trimmed = slug.trim();
|
|
4189
|
+
return trimmed.length > 0 && trimmed.length <= MAX_SUBAGENT_SLUG_LENGTH && SAFE_SUBAGENT_SLUG_PATTERN.test(trimmed);
|
|
4190
|
+
}
|
|
4191
|
+
function buildFileContent(subagent) {
|
|
4192
|
+
return (0, import_shared4.buildSubagentFileContentFromBody)({
|
|
4193
|
+
slug: subagent.slug,
|
|
4194
|
+
description: subagent.description,
|
|
4195
|
+
bodyMarkdown: subagent.bodyMarkdown
|
|
4196
|
+
});
|
|
4197
|
+
}
|
|
4198
|
+
async function syncProjectSubagents(client, params) {
|
|
4199
|
+
const { projectId, workspaceRoot } = params;
|
|
4200
|
+
const listResponse = await client.get(import_shared4.PROJECT_SUBAGENTS_ROUTES.listSubagents(projectId));
|
|
4201
|
+
const directory = join2(workspaceRoot, ".cursor", "agents");
|
|
4202
|
+
mkdirSync(directory, { recursive: true });
|
|
4203
|
+
const synced = [];
|
|
4204
|
+
const skipped = [];
|
|
4205
|
+
for (const subagent of listResponse.items) {
|
|
4206
|
+
if (!isSyncableSlug(subagent.slug)) {
|
|
4207
|
+
skipped.push(subagent.slug);
|
|
4208
|
+
continue;
|
|
4209
|
+
}
|
|
4210
|
+
const filePath = join2(directory, `${subagent.slug}.md`);
|
|
4211
|
+
writeFileSync(filePath, buildFileContent(subagent), "utf8");
|
|
4212
|
+
synced.push(subagent.slug);
|
|
4213
|
+
}
|
|
4214
|
+
return { synced, skipped, directory };
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
// src/tools/wait-for-agent-runs.ts
|
|
4218
|
+
var import_agent_runs = __toESM(require_agent_runs_api(), 1);
|
|
4219
|
+
var import_agent_run_status = __toESM(require_agent_run_status_enum(), 1);
|
|
4220
|
+
var DEFAULT_RETRY_BASE_MS = 500;
|
|
4221
|
+
var DEFAULT_RETRY_MAX_MS = 3e4;
|
|
4222
|
+
var DEFAULT_INFLIGHT_LIMIT = 10;
|
|
4223
|
+
function sleep(ms) {
|
|
4224
|
+
return new Promise((resolve4) => {
|
|
4225
|
+
setTimeout(resolve4, ms);
|
|
4226
|
+
});
|
|
4227
|
+
}
|
|
4228
|
+
function isTransientClaimError(error) {
|
|
4229
|
+
if (!(error instanceof RestClientError)) {
|
|
4230
|
+
return false;
|
|
4231
|
+
}
|
|
4232
|
+
if (error.status === void 0) {
|
|
4233
|
+
return error.code === "HTTP_ERROR";
|
|
4234
|
+
}
|
|
4235
|
+
return error.status >= 500;
|
|
4236
|
+
}
|
|
4237
|
+
function computeBackoffMs(consecutiveFailures, baseMs, maxMs) {
|
|
4238
|
+
const exponential = baseMs * 2 ** (consecutiveFailures - 1);
|
|
4239
|
+
return Math.min(exponential, maxMs);
|
|
4240
|
+
}
|
|
4241
|
+
function compareInflightRuns(a, b) {
|
|
4242
|
+
if (a.requiresAttention !== b.requiresAttention) {
|
|
4243
|
+
return a.requiresAttention ? -1 : 1;
|
|
4244
|
+
}
|
|
4245
|
+
const aDispatched = a.dispatchedAt ?? "";
|
|
4246
|
+
const bDispatched = b.dispatchedAt ?? "";
|
|
4247
|
+
return aDispatched.localeCompare(bDispatched);
|
|
4248
|
+
}
|
|
4249
|
+
async function fetchInflightAgentRuns(client, options) {
|
|
4250
|
+
const limit = options.limit ?? DEFAULT_INFLIGHT_LIMIT;
|
|
4251
|
+
const page = await client.get(import_agent_runs.AGENT_RUNS_ROUTES.listAgentRuns(), {
|
|
4252
|
+
projectId: options.projectId,
|
|
4253
|
+
activeOnly: "true",
|
|
4254
|
+
limit: String(limit)
|
|
4255
|
+
});
|
|
4256
|
+
const filtered = page.items.filter(
|
|
4257
|
+
(run) => run.status === import_agent_run_status.AGENT_RUN_STATUS.DISPATCHED || run.status === import_agent_run_status.AGENT_RUN_STATUS.ACKNOWLEDGED
|
|
4258
|
+
);
|
|
4259
|
+
filtered.sort(compareInflightRuns);
|
|
4260
|
+
return filtered.slice(0, limit);
|
|
4261
|
+
}
|
|
4262
|
+
async function pollAgentRuns(client, options, deps) {
|
|
4263
|
+
const { projectId, timeoutMs, pollIntervalMs, limit } = options;
|
|
4264
|
+
if (pollIntervalMs > timeoutMs) {
|
|
4265
|
+
throw new WorkspaceError("VALIDATION_ERROR", "pollInterval must not exceed timeout");
|
|
4266
|
+
}
|
|
4267
|
+
const sleepFn = deps?.sleep ?? sleep;
|
|
4268
|
+
const deadline = Date.now() + timeoutMs;
|
|
4269
|
+
const claimLimit = limit ?? 1;
|
|
4270
|
+
const retryBaseMs = options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
4271
|
+
const retryMaxMs = options.retryMaxMs ?? DEFAULT_RETRY_MAX_MS;
|
|
4272
|
+
let consecutiveFailures = 0;
|
|
4273
|
+
while (true) {
|
|
4274
|
+
let response;
|
|
4275
|
+
try {
|
|
4276
|
+
response = await client.post(import_agent_runs.AGENT_RUNS_ROUTES.claimAgentRuns(), {
|
|
4277
|
+
projectId,
|
|
4278
|
+
limit: claimLimit
|
|
4279
|
+
});
|
|
4280
|
+
consecutiveFailures = 0;
|
|
4281
|
+
} catch (error) {
|
|
4282
|
+
if (!isTransientClaimError(error)) {
|
|
4283
|
+
throw error;
|
|
4284
|
+
}
|
|
4285
|
+
if (Date.now() >= deadline) {
|
|
4286
|
+
return { items: [], inflightRuns: [], timedOut: true };
|
|
4287
|
+
}
|
|
4288
|
+
consecutiveFailures += 1;
|
|
4289
|
+
const backoffMs = computeBackoffMs(consecutiveFailures, retryBaseMs, retryMaxMs);
|
|
4290
|
+
const remainingMs2 = deadline - Date.now();
|
|
4291
|
+
await sleepFn(Math.min(backoffMs, remainingMs2));
|
|
4292
|
+
continue;
|
|
4293
|
+
}
|
|
4294
|
+
if (response.items.length > 0) {
|
|
4295
|
+
return { items: response.items, inflightRuns: [], timedOut: false };
|
|
4296
|
+
}
|
|
4297
|
+
const inflightRuns = await fetchInflightAgentRuns(client, { projectId, limit: claimLimit });
|
|
4298
|
+
if (inflightRuns.length > 0) {
|
|
4299
|
+
return { items: [], inflightRuns, timedOut: false };
|
|
4300
|
+
}
|
|
4301
|
+
if (Date.now() >= deadline) {
|
|
4302
|
+
return { items: [], inflightRuns: [], timedOut: true };
|
|
4303
|
+
}
|
|
4304
|
+
const remainingMs = deadline - Date.now();
|
|
4305
|
+
const waitMs = Math.min(pollIntervalMs, remainingMs);
|
|
4306
|
+
await sleepFn(waitMs);
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
// src/orchestrator/orchestrator-transport.ts
|
|
4311
|
+
var OrchestratorTransport = class {
|
|
4312
|
+
constructor(client, options = {}) {
|
|
4313
|
+
this.client = client;
|
|
4314
|
+
this.options = options;
|
|
4315
|
+
}
|
|
4316
|
+
async claimAndPoll(options, deps) {
|
|
4317
|
+
return pollAgentRuns(this.client, options, deps);
|
|
4318
|
+
}
|
|
4319
|
+
async fetchInflightRuns(projectId, limit) {
|
|
4320
|
+
return fetchInflightAgentRuns(this.client, { projectId, limit });
|
|
4321
|
+
}
|
|
4322
|
+
async ackRun(agentRunId, note) {
|
|
4323
|
+
const body = note !== void 0 ? { note } : void 0;
|
|
4324
|
+
return this.client.patch(import_agent_runs2.AGENT_RUNS_ROUTES.acknowledgeAgentRun(agentRunId), body);
|
|
4325
|
+
}
|
|
4326
|
+
async setAttention(agentRunId, requiresAttention, message) {
|
|
4327
|
+
const body = requiresAttention ? { requiresAttention: true, attentionMessage: message ?? "" } : { requiresAttention: false };
|
|
4328
|
+
return this.client.patch(import_agent_runs2.AGENT_RUNS_ROUTES.setAgentRunAttention(agentRunId), body);
|
|
4329
|
+
}
|
|
4330
|
+
async completeRun(agentRunId, body) {
|
|
4331
|
+
return this.client.post(import_agent_runs2.AGENT_RUNS_ROUTES.completeAgentRun(agentRunId), body);
|
|
4332
|
+
}
|
|
4333
|
+
async listAttachments(workItemId) {
|
|
4334
|
+
return fetchAttachments(this.client, workItemId);
|
|
4335
|
+
}
|
|
4336
|
+
async downloadAttachments(workItemId, workspaceRoot, attachmentIds) {
|
|
4337
|
+
return downloadWorkItemAttachments(this.client, {
|
|
4338
|
+
workItemId,
|
|
4339
|
+
workspaceRoot,
|
|
4340
|
+
attachmentIds,
|
|
4341
|
+
blockSensitiveAttachments: this.options.blockSensitiveAttachments ?? true
|
|
4342
|
+
});
|
|
4343
|
+
}
|
|
4344
|
+
async syncSubagents(projectId, workspaceRoot) {
|
|
4345
|
+
return syncProjectSubagents(this.client, { projectId, workspaceRoot });
|
|
4346
|
+
}
|
|
4347
|
+
async createComment(workItemId, body) {
|
|
4348
|
+
return postComment(this.client, workItemId, body);
|
|
4349
|
+
}
|
|
4350
|
+
};
|
|
4351
|
+
function createOrchestratorTransport(client, config) {
|
|
4352
|
+
return new OrchestratorTransport(client, {
|
|
4353
|
+
workspaceRoot: config?.workspaceRoot,
|
|
4354
|
+
blockSensitiveAttachments: config?.blockSensitiveAttachments
|
|
4355
|
+
});
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
// src/orchestrator/active-run-state.ts
|
|
4359
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4360
|
+
import { join as join3 } from "node:path";
|
|
4361
|
+
var ACTIVE_RUN_STATE_RELATIVE_PATH = ".cursor/orchestrator-active-run.json";
|
|
4362
|
+
var ATTENTION_PENDING_RELATIVE_PATH = ".cursor/orchestrator-attention-pending.json";
|
|
4363
|
+
var ACTIVE_RUN_LEASE_MINUTES = 30;
|
|
4364
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4365
|
+
function isRecord(value) {
|
|
4366
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4367
|
+
}
|
|
4368
|
+
function isUuid(value) {
|
|
4369
|
+
return UUID_PATTERN.test(value);
|
|
4370
|
+
}
|
|
4371
|
+
function isIsoDateTimeString(value) {
|
|
4372
|
+
return !Number.isNaN(Date.parse(value));
|
|
4373
|
+
}
|
|
4374
|
+
function getActiveRunStatePath(workspaceRoot) {
|
|
4375
|
+
return join3(workspaceRoot, ACTIVE_RUN_STATE_RELATIVE_PATH);
|
|
4376
|
+
}
|
|
4377
|
+
function getAttentionPendingPath(workspaceRoot) {
|
|
4378
|
+
return join3(workspaceRoot, ATTENTION_PENDING_RELATIVE_PATH);
|
|
4379
|
+
}
|
|
4380
|
+
function computeExpiresAt(acknowledgedAt, leaseMinutes = ACTIVE_RUN_LEASE_MINUTES) {
|
|
4381
|
+
const expiresMs = Date.parse(acknowledgedAt) + leaseMinutes * 60 * 1e3;
|
|
4382
|
+
return new Date(expiresMs).toISOString();
|
|
4383
|
+
}
|
|
4384
|
+
function ensureCursorDir(workspaceRoot) {
|
|
4385
|
+
const cursorDir = join3(workspaceRoot, ".cursor");
|
|
4386
|
+
if (!existsSync4(cursorDir)) {
|
|
4387
|
+
mkdirSync2(cursorDir, { recursive: true });
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
function parseActiveRunState(raw) {
|
|
4391
|
+
if (!isRecord(raw)) {
|
|
4392
|
+
return null;
|
|
4393
|
+
}
|
|
4394
|
+
const version = raw.version;
|
|
4395
|
+
const agentRunId = raw.agentRunId;
|
|
4396
|
+
const workItemId = raw.workItemId;
|
|
4397
|
+
const projectId = raw.projectId;
|
|
4398
|
+
const acknowledgedAt = raw.acknowledgedAt;
|
|
4399
|
+
const expiresAt = raw.expiresAt;
|
|
4400
|
+
if (version !== 1 || typeof agentRunId !== "string" || typeof workItemId !== "string" || typeof projectId !== "string" || typeof acknowledgedAt !== "string" || typeof expiresAt !== "string" || !isUuid(agentRunId) || !isUuid(workItemId) || !isUuid(projectId) || !isIsoDateTimeString(acknowledgedAt) || !isIsoDateTimeString(expiresAt)) {
|
|
4401
|
+
return null;
|
|
4402
|
+
}
|
|
4403
|
+
return {
|
|
4404
|
+
version: 1,
|
|
4405
|
+
agentRunId,
|
|
4406
|
+
workItemId,
|
|
4407
|
+
projectId,
|
|
4408
|
+
acknowledgedAt,
|
|
4409
|
+
expiresAt
|
|
4410
|
+
};
|
|
4411
|
+
}
|
|
4412
|
+
function parseAttentionPendingState(raw) {
|
|
4413
|
+
if (!isRecord(raw)) {
|
|
4414
|
+
return null;
|
|
4415
|
+
}
|
|
4416
|
+
const agentRunId = raw.agentRunId;
|
|
4417
|
+
const reportedAt = raw.reportedAt;
|
|
4418
|
+
if (typeof agentRunId !== "string" || typeof reportedAt !== "string" || !isUuid(agentRunId) || !isIsoDateTimeString(reportedAt)) {
|
|
4419
|
+
return null;
|
|
4420
|
+
}
|
|
4421
|
+
return { agentRunId, reportedAt };
|
|
4422
|
+
}
|
|
4423
|
+
function isActiveRunStateUsable(state, nowMs = Date.now()) {
|
|
4424
|
+
if (state === null) {
|
|
4425
|
+
return false;
|
|
4426
|
+
}
|
|
4427
|
+
const expiresMs = Date.parse(state.expiresAt);
|
|
4428
|
+
if (Number.isNaN(expiresMs)) {
|
|
4429
|
+
return false;
|
|
4430
|
+
}
|
|
4431
|
+
return nowMs < expiresMs;
|
|
4432
|
+
}
|
|
4433
|
+
function readActiveRunState(workspaceRoot) {
|
|
4434
|
+
const filePath = getActiveRunStatePath(workspaceRoot);
|
|
4435
|
+
if (!existsSync4(filePath)) {
|
|
4436
|
+
return null;
|
|
4437
|
+
}
|
|
4438
|
+
try {
|
|
4439
|
+
const content = readFileSync2(filePath, "utf8");
|
|
4440
|
+
const parsed = JSON.parse(content);
|
|
4441
|
+
return parseActiveRunState(parsed);
|
|
4442
|
+
} catch {
|
|
4443
|
+
return null;
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
function writeActiveRunState(workspaceRoot, input) {
|
|
4447
|
+
ensureCursorDir(workspaceRoot);
|
|
4448
|
+
const state = {
|
|
4449
|
+
version: 1,
|
|
4450
|
+
agentRunId: input.agentRunId,
|
|
4451
|
+
workItemId: input.workItemId,
|
|
4452
|
+
projectId: input.projectId,
|
|
4453
|
+
acknowledgedAt: input.acknowledgedAt,
|
|
4454
|
+
expiresAt: computeExpiresAt(input.acknowledgedAt)
|
|
4455
|
+
};
|
|
4456
|
+
writeFileSync2(getActiveRunStatePath(workspaceRoot), `${JSON.stringify(state, null, 2)}
|
|
4457
|
+
`, "utf8");
|
|
4458
|
+
return state;
|
|
4459
|
+
}
|
|
4460
|
+
function clearActiveRunState(workspaceRoot) {
|
|
4461
|
+
const filePath = getActiveRunStatePath(workspaceRoot);
|
|
4462
|
+
if (existsSync4(filePath)) {
|
|
4463
|
+
rmSync(filePath, { force: true });
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
function readAttentionPendingState(workspaceRoot) {
|
|
4467
|
+
const filePath = getAttentionPendingPath(workspaceRoot);
|
|
4468
|
+
if (!existsSync4(filePath)) {
|
|
4469
|
+
return null;
|
|
4470
|
+
}
|
|
4471
|
+
try {
|
|
4472
|
+
const content = readFileSync2(filePath, "utf8");
|
|
4473
|
+
const parsed = JSON.parse(content);
|
|
4474
|
+
return parseAttentionPendingState(parsed);
|
|
4475
|
+
} catch {
|
|
4476
|
+
return null;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
function writeAttentionPendingState(workspaceRoot, agentRunId) {
|
|
4480
|
+
ensureCursorDir(workspaceRoot);
|
|
4481
|
+
const pending = {
|
|
4482
|
+
agentRunId,
|
|
4483
|
+
reportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4484
|
+
};
|
|
4485
|
+
writeFileSync2(getAttentionPendingPath(workspaceRoot), `${JSON.stringify(pending, null, 2)}
|
|
4486
|
+
`, "utf8");
|
|
4487
|
+
return pending;
|
|
4488
|
+
}
|
|
4489
|
+
function clearAttentionPendingState(workspaceRoot) {
|
|
4490
|
+
const filePath = getAttentionPendingPath(workspaceRoot);
|
|
4491
|
+
if (existsSync4(filePath)) {
|
|
4492
|
+
rmSync(filePath, { force: true });
|
|
4493
|
+
}
|
|
4494
|
+
}
|
|
4495
|
+
function clearOrchestratorBridgeState(workspaceRoot) {
|
|
4496
|
+
clearActiveRunState(workspaceRoot);
|
|
4497
|
+
clearAttentionPendingState(workspaceRoot);
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
// src/workspace/parse-task-boards-yaml.ts
|
|
4501
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
4502
|
+
import { join as join4 } from "node:path";
|
|
4503
|
+
var TASK_BOARDS_FILE2 = ".task-boards.yaml";
|
|
4504
|
+
var VERSION_PATTERN = /^\s*version\s*:\s*1\s*$/m;
|
|
4505
|
+
var PROJECT_ID_PATTERN = /^\s*projectId\s*:\s*["']?([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})["']?\s*$/im;
|
|
4506
|
+
function parseTaskBoardsYaml(workspaceRoot) {
|
|
4507
|
+
const filePath = join4(workspaceRoot, TASK_BOARDS_FILE2);
|
|
4508
|
+
if (!existsSync5(filePath)) {
|
|
4509
|
+
return null;
|
|
4510
|
+
}
|
|
4511
|
+
const content = readFileSync3(filePath, "utf8");
|
|
4512
|
+
if (!VERSION_PATTERN.test(content)) {
|
|
4513
|
+
return null;
|
|
4514
|
+
}
|
|
4515
|
+
const match = PROJECT_ID_PATTERN.exec(content);
|
|
4516
|
+
if (match === null) {
|
|
4517
|
+
return null;
|
|
4518
|
+
}
|
|
4519
|
+
return match[1] ?? null;
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4522
|
+
// src/tools/orchestrator-attention.util.ts
|
|
4523
|
+
var import_agent_run_status2 = __toESM(require_agent_run_status_enum(), 1);
|
|
4524
|
+
var ATTENTION_MESSAGE_CATEGORY = {
|
|
4525
|
+
SHELL: "shell",
|
|
4526
|
+
GIT: "git",
|
|
4527
|
+
NETWORK: "network",
|
|
4528
|
+
SMART_MODE: "smart-mode",
|
|
4529
|
+
MCP: "mcp",
|
|
4530
|
+
FILE: "file",
|
|
4531
|
+
OTHER: "other"
|
|
3729
4532
|
};
|
|
3730
4533
|
var ATTENTION_MESSAGE_PREFIX_PATTERN = /^\[([^\]]+)\]\s*/;
|
|
3731
4534
|
var ATTENTION_MESSAGE_TEMPLATES = {
|
|
@@ -3780,10 +4583,10 @@ function deriveOrchestratorRunPhase(run) {
|
|
|
3780
4583
|
if (run.requiresAttention) {
|
|
3781
4584
|
return "awaiting_ide_approval";
|
|
3782
4585
|
}
|
|
3783
|
-
if (run.status ===
|
|
4586
|
+
if (run.status === import_agent_run_status2.AGENT_RUN_STATUS.DISPATCHED) {
|
|
3784
4587
|
return "dispatched";
|
|
3785
4588
|
}
|
|
3786
|
-
if (run.status ===
|
|
4589
|
+
if (run.status === import_agent_run_status2.AGENT_RUN_STATUS.ACKNOWLEDGED) {
|
|
3787
4590
|
return "acknowledged";
|
|
3788
4591
|
}
|
|
3789
4592
|
if (run.acknowledgedAt !== null) {
|
|
@@ -3802,1114 +4605,1067 @@ function parseAttentionMessageCategory(message) {
|
|
|
3802
4605
|
}
|
|
3803
4606
|
return ATTENTION_MESSAGE_CATEGORY.OTHER;
|
|
3804
4607
|
}
|
|
4608
|
+
function formatAttentionMessage(category, detail) {
|
|
4609
|
+
return `[${category}] ${detail.trim()}`;
|
|
4610
|
+
}
|
|
3805
4611
|
function buildAttentionGuidance(run) {
|
|
3806
4612
|
const phase = deriveOrchestratorRunPhase(run);
|
|
3807
4613
|
const detectedCategory = run.attentionMessage !== null ? parseAttentionMessageCategory(run.attentionMessage) : null;
|
|
3808
|
-
const reportStep = phase === "awaiting_ide_approval" ? null : `report_agent_attention(${run.id}, message) \u2014 use formatAttentionMessage(category, detail); replaces prior message on same ACK run`;
|
|
3809
|
-
const clearStep = run.requiresAttention ? `clear_agent_attention(${run.id}) after resolving: ${run.attentionMessage ?? "attention flag"}` : null;
|
|
3810
|
-
return {
|
|
3811
|
-
phase,
|
|
3812
|
-
reportStep,
|
|
3813
|
-
clearStep,
|
|
3814
|
-
detectedCategory,
|
|
3815
|
-
templateExamples: ALL_TEMPLATE_CATEGORIES
|
|
3816
|
-
};
|
|
3817
|
-
}
|
|
3818
|
-
|
|
3819
|
-
// src/tools/orchestrator-hints.ts
|
|
3820
|
-
var SLEEPER_POLICY = {
|
|
3821
|
-
singleBlockingPoll: true,
|
|
3822
|
-
noParallelPoll: true,
|
|
3823
|
-
noDuplicateWake: true,
|
|
3824
|
-
guidance: "Run exactly one blocking poll (run_orchestrator_once or wait_for_agent_runs) per workspace at a time; do not overlap polls or stack duplicate automation wakes."
|
|
3825
|
-
};
|
|
3826
|
-
var IDLE_INFLIGHT_CHECK_STEP = "Checked inflight runs (DISPATCHED/ACKNOWLEDGED); none found \u2014 true idle.";
|
|
3827
|
-
var IDLE_NO_DUPLICATE_WAKE_STEP = "Schedule at most one follow-up automation wake; do not stack duplicate sleepers while a poll is in flight.";
|
|
3828
|
-
var PROCESSING_NO_PARALLEL_POLL_STEP = "Do not start another poll until this batch finishes.";
|
|
3829
|
-
var INFLIGHT_DISPATCHED_RECOVERY_STEP = "Orphan DISPATCHED \u2014 resume Task+ack; avoid duplicate Task if subagent already running in this session";
|
|
3830
|
-
var IDLE_TIMEOUT_DO_NOT_CLEAR_ATTENTION = "Do not clear_agent_attention on timeout; badge remains until human resolves in IDE";
|
|
3831
|
-
var IDLE_TIMEOUT_NEXT_STEPS = [
|
|
3832
|
-
"Call sync_git_releases(projectId) to sync work-item commits",
|
|
3833
|
-
IDLE_TIMEOUT_DO_NOT_CLEAR_ATTENTION,
|
|
3834
|
-
"Call wait_for_agent_runs again (or run_orchestrator_once) to continue monitoring"
|
|
3835
|
-
];
|
|
3836
|
-
var IDE_ATTENTION_NOT_IN_AWAITING = "IDE attention is not in-awaiting; do not move column; replace attention message on re-report";
|
|
3837
|
-
var PROCESSING_NEXT_STEPS = [
|
|
3838
|
-
"Call sync_project_subagents(projectId) before Task dispatch when the project uses custom subagent slugs",
|
|
3839
|
-
"Agent runs only for STORY items assigned to \u0410\u0433\u0435\u043D\u0442 \u0418\u0418 (SERVICE user); assign via update_work_item before moving to agent columns",
|
|
3840
|
-
"When a subagent is blocked, call report_agent_attention(agentRunId, message); clear with clear_agent_attention after resolution",
|
|
3841
|
-
"After processing all runs: local git commit with work-item:{uuid} tag (always; no push on master/main)",
|
|
3842
|
-
"On feature branch (not master/main): git push origin HEAD then sync_git_releases(projectId)",
|
|
3843
|
-
"Parallel Task runs OK for same workItemId when agentRole differs (e.g. FRONTEND_DEVELOPER + DEVELOPER)",
|
|
3844
|
-
"Claim priority: in-development > in-qa > in-analysis; in-analysis blocked while SERVICE-assigned stories exist in dev/qa",
|
|
3845
|
-
"Call run_orchestrator_once or wait_for_agent_runs to continue monitoring"
|
|
3846
|
-
];
|
|
3847
|
-
var ATTENTION_PROCESSING_NEXT_STEPS = [
|
|
3848
|
-
"Resolve requiresAttention runs before dispatching new Task subagents",
|
|
3849
|
-
IDE_ATTENTION_NOT_IN_AWAITING,
|
|
3850
|
-
"Call clear_agent_attention(agentRunId) after each blocker is resolved"
|
|
3851
|
-
];
|
|
3852
|
-
var RUN_LIFECYCLE_DOC = {
|
|
3853
|
-
phases: ["dispatched", "acknowledged", "awaiting_ide_approval", "ready_to_complete"],
|
|
3854
|
-
terminalPhase: "complete",
|
|
3855
|
-
ideAttentionVsWorkflowAwaiting: "IDE attention (report_agent_attention on ACKNOWLEDGED runs) is not in-awaiting \u2014 do not move the story column for shell/git/network/MCP/Smart Mode approvals. Use complete_agent_run(NEEDS_CLARIFICATION) and in-awaiting only for workflow clarifications."
|
|
3856
|
-
};
|
|
3857
|
-
function buildTaskStep(run) {
|
|
3858
|
-
const subagent = run.payload.suggestedSubagentType ?? "subagent";
|
|
3859
|
-
return `Task(subagent_type=${subagent}, prompt=payload.contextSummary)`;
|
|
3860
|
-
}
|
|
3861
|
-
function buildCompleteStep(run) {
|
|
3862
|
-
if (run.agentRole === import_subagent_role.SUBAGENT_ROLE.ARCHITECT) {
|
|
3863
|
-
return "complete_agent_run after work; architect may set workItemPatch.designerRequired (boolean, default false)";
|
|
3864
|
-
}
|
|
3865
|
-
return "complete_agent_run after work";
|
|
3866
|
-
}
|
|
3867
|
-
function buildReportAttentionStep(run) {
|
|
3868
|
-
return `report_agent_attention(${run.id}, message) if blocked awaiting human input \u2014 ${ATTENTION_MESSAGE_TEMPLATES.shell.messagePattern.replace("{detail}", "<detail>")}; replaces prior message on re-report`;
|
|
3869
|
-
}
|
|
3870
|
-
function buildProcessRunAttentionHint(run) {
|
|
3871
|
-
const guidance = buildAttentionGuidance(run);
|
|
3872
|
-
return {
|
|
3873
|
-
phase: guidance.phase,
|
|
3874
|
-
requiresAttention: run.requiresAttention,
|
|
3875
|
-
message: run.attentionMessage,
|
|
3876
|
-
detectedCategory: guidance.detectedCategory,
|
|
3877
|
-
reportGuidance: `report_agent_attention(${run.id}, message) using formatAttentionMessage(category, detail); replaces prior message on same ACK run`,
|
|
3878
|
-
clearGuidance: guidance.clearStep,
|
|
3879
|
-
templateCategories: guidance.templateExamples
|
|
3880
|
-
};
|
|
3881
|
-
}
|
|
3882
|
-
function buildDispatchedProcessRunSteps(run) {
|
|
3883
|
-
return [
|
|
3884
|
-
"sync_project_subagents(projectId) when payload.suggestedSubagentType is a project custom slug",
|
|
3885
|
-
`list_work_item_attachments(${run.workItemId})`,
|
|
3886
|
-
"download_work_item_attachments if needed",
|
|
3887
|
-
buildTaskStep(run),
|
|
3888
|
-
`ack_agent_run(${run.id})`,
|
|
3889
|
-
buildReportAttentionStep(run),
|
|
3890
|
-
`clear_agent_attention(${run.id}) after human resolves in IDE`,
|
|
3891
|
-
buildCompleteStep(run),
|
|
3892
|
-
"git commit with work-item tag (local; push only on feature branch)"
|
|
3893
|
-
];
|
|
3894
|
-
}
|
|
3895
|
-
function buildAwaitingIdeApprovalProcessRunSteps(run) {
|
|
3896
|
-
return [
|
|
3897
|
-
`clear_agent_attention(${run.id}) after resolving: ${run.attentionMessage ?? "attention flag"}`,
|
|
3898
|
-
buildCompleteStep(run),
|
|
3899
|
-
"git commit with work-item tag (local; push only on feature branch)"
|
|
3900
|
-
];
|
|
3901
|
-
}
|
|
3902
|
-
function buildAcknowledgedProcessRunSteps(run) {
|
|
3903
|
-
return [
|
|
3904
|
-
"Resume subagent work (Task may already be running in this session)",
|
|
3905
|
-
buildReportAttentionStep(run),
|
|
3906
|
-
`clear_agent_attention(${run.id}) after human resolves in IDE`,
|
|
3907
|
-
buildCompleteStep(run),
|
|
3908
|
-
"git commit with work-item tag (local; push only on feature branch)"
|
|
3909
|
-
];
|
|
3910
|
-
}
|
|
3911
|
-
function buildProcessRunHint(run, source) {
|
|
3912
|
-
const phase = deriveOrchestratorRunPhase(run);
|
|
3913
|
-
if (phase === "awaiting_ide_approval") {
|
|
3914
|
-
return {
|
|
3915
|
-
agentRunId: run.id,
|
|
3916
|
-
workItemId: run.workItemId,
|
|
3917
|
-
phase,
|
|
3918
|
-
source,
|
|
3919
|
-
steps: buildAwaitingIdeApprovalProcessRunSteps(run),
|
|
3920
|
-
attention: buildProcessRunAttentionHint(run)
|
|
3921
|
-
};
|
|
3922
|
-
}
|
|
3923
|
-
if (phase === "acknowledged") {
|
|
3924
|
-
return {
|
|
3925
|
-
agentRunId: run.id,
|
|
3926
|
-
workItemId: run.workItemId,
|
|
3927
|
-
phase,
|
|
3928
|
-
source,
|
|
3929
|
-
steps: buildAcknowledgedProcessRunSteps(run)
|
|
3930
|
-
};
|
|
3931
|
-
}
|
|
3932
|
-
const steps = buildDispatchedProcessRunSteps(run);
|
|
3933
|
-
if (source === "inflight") {
|
|
3934
|
-
return {
|
|
3935
|
-
agentRunId: run.id,
|
|
3936
|
-
workItemId: run.workItemId,
|
|
3937
|
-
phase,
|
|
3938
|
-
source,
|
|
3939
|
-
steps: [INFLIGHT_DISPATCHED_RECOVERY_STEP, ...steps]
|
|
3940
|
-
};
|
|
3941
|
-
}
|
|
3942
|
-
return {
|
|
3943
|
-
agentRunId: run.id,
|
|
3944
|
-
workItemId: run.workItemId,
|
|
3945
|
-
phase,
|
|
3946
|
-
source,
|
|
3947
|
-
steps
|
|
3948
|
-
};
|
|
3949
|
-
}
|
|
3950
|
-
function buildProcessRuns(items, inflightRuns) {
|
|
3951
|
-
const hints = [];
|
|
3952
|
-
for (const run of items) {
|
|
3953
|
-
if (run.requiresAttention) {
|
|
3954
|
-
hints.push(buildProcessRunHint(run, "claimed"));
|
|
3955
|
-
}
|
|
3956
|
-
}
|
|
3957
|
-
for (const run of inflightRuns) {
|
|
3958
|
-
if (run.requiresAttention) {
|
|
3959
|
-
hints.push(buildProcessRunHint(run, "inflight"));
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
for (const run of inflightRuns) {
|
|
3963
|
-
if (!run.requiresAttention) {
|
|
3964
|
-
hints.push(buildProcessRunHint(run, "inflight"));
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
for (const run of items) {
|
|
3968
|
-
if (!run.requiresAttention) {
|
|
3969
|
-
hints.push(buildProcessRunHint(run, "claimed"));
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
return hints;
|
|
3973
|
-
}
|
|
3974
|
-
function buildAttentionSummary(runs) {
|
|
3975
|
-
const blockedRuns = runs.filter((run) => run.requiresAttention);
|
|
3976
|
-
return {
|
|
3977
|
-
blockedRunCount: blockedRuns.length,
|
|
3978
|
-
blockedAgentRunIds: blockedRuns.map((run) => run.id),
|
|
3979
|
-
resolveBeforeNewDispatch: blockedRuns.length > 0
|
|
3980
|
-
};
|
|
3981
|
-
}
|
|
3982
|
-
function buildInflightSummary(inflightRuns) {
|
|
3983
|
-
if (inflightRuns.length === 0) {
|
|
3984
|
-
return void 0;
|
|
3985
|
-
}
|
|
3986
|
-
return {
|
|
3987
|
-
dispatchedCount: inflightRuns.filter((run) => run.status === import_agent_run_status2.AGENT_RUN_STATUS.DISPATCHED).length,
|
|
3988
|
-
acknowledgedCount: inflightRuns.filter((run) => run.status === import_agent_run_status2.AGENT_RUN_STATUS.ACKNOWLEDGED).length,
|
|
3989
|
-
requiresAttentionCount: inflightRuns.filter((run) => run.requiresAttention).length,
|
|
3990
|
-
agentRunIds: inflightRuns.map((run) => run.id)
|
|
3991
|
-
};
|
|
3992
|
-
}
|
|
3993
|
-
function derivePollResultSource(items, inflightRuns) {
|
|
3994
|
-
if (items.length > 0) {
|
|
3995
|
-
return "claimed";
|
|
3996
|
-
}
|
|
3997
|
-
if (inflightRuns.length > 0) {
|
|
3998
|
-
return "inflight";
|
|
3999
|
-
}
|
|
4000
|
-
return "idle";
|
|
4001
|
-
}
|
|
4002
|
-
function buildOrchestratorHints(items, inflightRuns, timedOut) {
|
|
4003
|
-
const pollResultSource = derivePollResultSource(items, inflightRuns);
|
|
4004
|
-
const allRuns = [...items, ...inflightRuns];
|
|
4005
|
-
if (timedOut) {
|
|
4006
|
-
return {
|
|
4007
|
-
phase: "idle",
|
|
4008
|
-
pollResultSource,
|
|
4009
|
-
sleeperPolicy: SLEEPER_POLICY,
|
|
4010
|
-
nextSteps: [IDLE_INFLIGHT_CHECK_STEP, ...IDLE_TIMEOUT_NEXT_STEPS, IDLE_NO_DUPLICATE_WAKE_STEP]
|
|
4011
|
-
};
|
|
4012
|
-
}
|
|
4013
|
-
if (allRuns.length === 0) {
|
|
4014
|
-
return {
|
|
4015
|
-
phase: "idle",
|
|
4016
|
-
pollResultSource,
|
|
4017
|
-
sleeperPolicy: SLEEPER_POLICY,
|
|
4018
|
-
nextSteps: [IDLE_INFLIGHT_CHECK_STEP, ...IDLE_TIMEOUT_NEXT_STEPS, IDLE_NO_DUPLICATE_WAKE_STEP]
|
|
4019
|
-
};
|
|
4020
|
-
}
|
|
4021
|
-
const hasAttentionRuns = allRuns.some((run) => run.requiresAttention);
|
|
4022
|
-
return {
|
|
4023
|
-
phase: "processing",
|
|
4024
|
-
pollResultSource,
|
|
4025
|
-
sleeperPolicy: SLEEPER_POLICY,
|
|
4026
|
-
inflightSummary: buildInflightSummary(inflightRuns),
|
|
4027
|
-
processRuns: buildProcessRuns(items, inflightRuns),
|
|
4028
|
-
attentionSummary: buildAttentionSummary(allRuns),
|
|
4029
|
-
runLifecycle: RUN_LIFECYCLE_DOC,
|
|
4030
|
-
nextSteps: hasAttentionRuns ? [PROCESSING_NO_PARALLEL_POLL_STEP, ...ATTENTION_PROCESSING_NEXT_STEPS, ...PROCESSING_NEXT_STEPS] : [PROCESSING_NO_PARALLEL_POLL_STEP, ...PROCESSING_NEXT_STEPS]
|
|
4031
|
-
};
|
|
4032
|
-
}
|
|
4033
|
-
function enrichPollResult(items, inflightRuns, timedOut) {
|
|
4614
|
+
const reportStep = phase === "awaiting_ide_approval" ? null : `report_agent_attention(${run.id}, message) \u2014 use formatAttentionMessage(category, detail); replaces prior message on same ACK run`;
|
|
4615
|
+
const clearStep = run.requiresAttention ? `clear_agent_attention(${run.id}) after resolving: ${run.attentionMessage ?? "attention flag"}` : null;
|
|
4034
4616
|
return {
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4617
|
+
phase,
|
|
4618
|
+
reportStep,
|
|
4619
|
+
clearStep,
|
|
4620
|
+
detectedCategory,
|
|
4621
|
+
templateExamples: ALL_TEMPLATE_CATEGORIES
|
|
4039
4622
|
};
|
|
4040
4623
|
}
|
|
4041
4624
|
|
|
4042
|
-
// src/
|
|
4043
|
-
var
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
this.name = "WorkspaceError";
|
|
4625
|
+
// src/attention/shell-category.util.ts
|
|
4626
|
+
var READONLY_SHELL_PATTERN = /^\s*(?:ls(?:\s|$)|cat\s|pwd(?:\s|$)|echo\s|git\s+status(?:\s|$)|git\s+diff(?:\s|$)|git\s+log(?:\s|$))/i;
|
|
4627
|
+
var GIT_DESTRUCTIVE_PATTERN = /\bgit\b.*\b(?:push|commit|checkout|reset|rebase|merge|tag|stash\s+pop|clean|fetch|pull|clone|remote|submodule)\b|\b(?:push|commit|checkout|reset|rebase|merge|tag|stash\s+pop|clean|fetch|pull|clone|remote|submodule)\b.*\bgit\b/i;
|
|
4628
|
+
var NETWORK_PATTERN = /\b(?:curl|wget|npm\s+(?:install|ci|publish|login)|pnpm\s+(?:install|add|i)|yarn\s+(?:install|add)|pip3?\s+install|docker\s+(?:pull|push|run|build|compose)|gh\s+api|npx\s+-y|nc\s|telnet\s|ssh\s)\b/i;
|
|
4629
|
+
function truncateCommand(command, maxLength) {
|
|
4630
|
+
const trimmed = command.trim();
|
|
4631
|
+
if (trimmed.length <= maxLength) {
|
|
4632
|
+
return trimmed;
|
|
4051
4633
|
}
|
|
4052
|
-
}
|
|
4634
|
+
return `${trimmed.slice(0, maxLength - 1)}\u2026`;
|
|
4635
|
+
}
|
|
4636
|
+
function classifyShellCommand(command) {
|
|
4637
|
+
const trimmed = command.trim();
|
|
4638
|
+
if (trimmed === "") {
|
|
4639
|
+
return null;
|
|
4640
|
+
}
|
|
4641
|
+
if (READONLY_SHELL_PATTERN.test(trimmed)) {
|
|
4642
|
+
return null;
|
|
4643
|
+
}
|
|
4644
|
+
if (GIT_DESTRUCTIVE_PATTERN.test(trimmed)) {
|
|
4645
|
+
return "git";
|
|
4646
|
+
}
|
|
4647
|
+
if (NETWORK_PATTERN.test(trimmed)) {
|
|
4648
|
+
return "network";
|
|
4649
|
+
}
|
|
4650
|
+
return "shell";
|
|
4651
|
+
}
|
|
4053
4652
|
|
|
4054
|
-
// src/
|
|
4055
|
-
function
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4653
|
+
// src/attention/attention-message.util.ts
|
|
4654
|
+
function buildShellAttentionMessage(category, command) {
|
|
4655
|
+
const truncated = truncateCommand(command, 200);
|
|
4656
|
+
switch (category) {
|
|
4657
|
+
case "git":
|
|
4658
|
+
return formatAttentionMessage(ATTENTION_MESSAGE_CATEGORY.GIT, `Approve git operation: ${truncated}`);
|
|
4659
|
+
case "network":
|
|
4660
|
+
return formatAttentionMessage(ATTENTION_MESSAGE_CATEGORY.NETWORK, `Approve network access: ${truncated}`);
|
|
4661
|
+
case "shell":
|
|
4662
|
+
return formatAttentionMessage(ATTENTION_MESSAGE_CATEGORY.SHELL, `Approve shell command: ${truncated}`);
|
|
4663
|
+
}
|
|
4059
4664
|
}
|
|
4060
|
-
function
|
|
4061
|
-
const
|
|
4062
|
-
return {
|
|
4063
|
-
isError: true,
|
|
4064
|
-
content: [{ type: "text", text: JSON.stringify({ code, message: sanitizedMessage }) }]
|
|
4065
|
-
};
|
|
4665
|
+
function buildMcpAttentionMessage(server, toolName) {
|
|
4666
|
+
const detail = `${server}/${toolName}`.trim();
|
|
4667
|
+
return formatAttentionMessage(ATTENTION_MESSAGE_CATEGORY.MCP, `Approve MCP / API action: ${detail}`);
|
|
4066
4668
|
}
|
|
4067
|
-
function
|
|
4068
|
-
|
|
4069
|
-
|
|
4669
|
+
function buildSmartModeAttentionMessage(toolName, reason) {
|
|
4670
|
+
const detailReason = reason !== void 0 && reason !== null && reason.trim() !== "" ? reason.trim() : "elevated permissions required";
|
|
4671
|
+
return formatAttentionMessage(
|
|
4672
|
+
ATTENTION_MESSAGE_CATEGORY.SMART_MODE,
|
|
4673
|
+
`Approve Smart Mode: ${toolName} \u2014 ${detailReason}`
|
|
4674
|
+
);
|
|
4675
|
+
}
|
|
4676
|
+
|
|
4677
|
+
// src/attention/hook-payload.util.ts
|
|
4678
|
+
function isRecord2(value) {
|
|
4679
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4680
|
+
}
|
|
4681
|
+
function readString(record, key) {
|
|
4682
|
+
const value = record[key];
|
|
4683
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
4684
|
+
return null;
|
|
4070
4685
|
}
|
|
4071
|
-
|
|
4072
|
-
return toolError("INTERNAL_ERROR", message);
|
|
4686
|
+
return value;
|
|
4073
4687
|
}
|
|
4074
|
-
|
|
4688
|
+
function parseHookPayloadJson(raw) {
|
|
4689
|
+
const trimmed = raw.trim();
|
|
4690
|
+
if (trimmed === "") {
|
|
4691
|
+
return null;
|
|
4692
|
+
}
|
|
4075
4693
|
try {
|
|
4076
|
-
|
|
4077
|
-
if (
|
|
4078
|
-
|
|
4694
|
+
const parsed = JSON.parse(trimmed);
|
|
4695
|
+
if (!isRecord2(parsed)) {
|
|
4696
|
+
return null;
|
|
4079
4697
|
}
|
|
4080
|
-
return
|
|
4081
|
-
} catch
|
|
4082
|
-
return
|
|
4698
|
+
return parsed;
|
|
4699
|
+
} catch {
|
|
4700
|
+
return null;
|
|
4083
4701
|
}
|
|
4084
4702
|
}
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
var import_agent_runs = __toESM(require_agent_runs_api(), 1);
|
|
4088
|
-
var import_agent_run_status3 = __toESM(require_agent_run_status_enum(), 1);
|
|
4089
|
-
var DEFAULT_RETRY_BASE_MS = 500;
|
|
4090
|
-
var DEFAULT_RETRY_MAX_MS = 3e4;
|
|
4091
|
-
var DEFAULT_INFLIGHT_LIMIT = 10;
|
|
4092
|
-
function sleep(ms) {
|
|
4093
|
-
return new Promise((resolve4) => {
|
|
4094
|
-
setTimeout(resolve4, ms);
|
|
4095
|
-
});
|
|
4703
|
+
function extractShellCommand(payload) {
|
|
4704
|
+
return readString(payload, "command");
|
|
4096
4705
|
}
|
|
4097
|
-
function
|
|
4098
|
-
|
|
4099
|
-
|
|
4706
|
+
function extractMcpExecution(payload) {
|
|
4707
|
+
const server = readString(payload, "server") ?? readString(payload, "serverName") ?? readString(payload, "mcpServer");
|
|
4708
|
+
const toolName = readString(payload, "toolName") ?? readString(payload, "tool") ?? readString(payload, "mcpToolName");
|
|
4709
|
+
if (server === null || toolName === null) {
|
|
4710
|
+
return null;
|
|
4100
4711
|
}
|
|
4101
|
-
|
|
4102
|
-
|
|
4712
|
+
return { server, toolName };
|
|
4713
|
+
}
|
|
4714
|
+
function extractPreToolUse(payload) {
|
|
4715
|
+
const toolName = readString(payload, "tool_name") ?? readString(payload, "toolName") ?? readString(payload, "tool") ?? readString(payload, "name");
|
|
4716
|
+
if (toolName === null) {
|
|
4717
|
+
return null;
|
|
4103
4718
|
}
|
|
4104
|
-
|
|
4719
|
+
const smartModeReason = readString(payload, "block_reason") ?? readString(payload, "smartModeBlockReason") ?? readString(payload, "smart_mode_block_reason") ?? readString(payload, "reason");
|
|
4720
|
+
const permission = readString(payload, "permission");
|
|
4721
|
+
const smartMode = payload.smart_mode ?? payload.smartMode;
|
|
4722
|
+
const hasSmartModeSignal = smartModeReason !== null || permission === "ask" || smartMode === true || smartMode === "blocked";
|
|
4723
|
+
if (!hasSmartModeSignal) {
|
|
4724
|
+
return null;
|
|
4725
|
+
}
|
|
4726
|
+
return { toolName, smartModeReason };
|
|
4105
4727
|
}
|
|
4106
|
-
function
|
|
4107
|
-
|
|
4108
|
-
return Math.min(exponential, maxMs);
|
|
4728
|
+
function isAfterHookEvent(event) {
|
|
4729
|
+
return event === "afterShellExecution" || event === "afterMCPExecution" || event === "postToolUse";
|
|
4109
4730
|
}
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4731
|
+
|
|
4732
|
+
// src/attention/attention-cli.ts
|
|
4733
|
+
function defaultReadStdin() {
|
|
4734
|
+
return new Promise((resolve4, reject) => {
|
|
4735
|
+
const chunks = [];
|
|
4736
|
+
process.stdin.setEncoding("utf8");
|
|
4737
|
+
process.stdin.on("data", (chunk) => {
|
|
4738
|
+
chunks.push(Buffer.from(chunk));
|
|
4739
|
+
});
|
|
4740
|
+
process.stdin.on("end", () => {
|
|
4741
|
+
resolve4(Buffer.concat(chunks).toString("utf8"));
|
|
4742
|
+
});
|
|
4743
|
+
process.stdin.on("error", reject);
|
|
4744
|
+
});
|
|
4745
|
+
}
|
|
4746
|
+
function resolveBridgeWorkspaceRoot(config) {
|
|
4747
|
+
try {
|
|
4748
|
+
return resolveWorkspaceRoot(void 0, config.workspaceRoot);
|
|
4749
|
+
} catch {
|
|
4750
|
+
return null;
|
|
4113
4751
|
}
|
|
4114
|
-
const aDispatched = a.dispatchedAt ?? "";
|
|
4115
|
-
const bDispatched = b.dispatchedAt ?? "";
|
|
4116
|
-
return aDispatched.localeCompare(bDispatched);
|
|
4117
4752
|
}
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
const page = await client.get(import_agent_runs.AGENT_RUNS_ROUTES.listAgentRuns(), {
|
|
4121
|
-
projectId: options.projectId,
|
|
4122
|
-
activeOnly: "true",
|
|
4123
|
-
limit: String(limit)
|
|
4124
|
-
});
|
|
4125
|
-
const filtered = page.items.filter(
|
|
4126
|
-
(run) => run.status === import_agent_run_status3.AGENT_RUN_STATUS.DISPATCHED || run.status === import_agent_run_status3.AGENT_RUN_STATUS.ACKNOWLEDGED
|
|
4127
|
-
);
|
|
4128
|
-
filtered.sort(compareInflightRuns);
|
|
4129
|
-
return filtered.slice(0, limit);
|
|
4753
|
+
function resolveProjectId(workspaceRoot) {
|
|
4754
|
+
return parseTaskBoardsYaml(workspaceRoot);
|
|
4130
4755
|
}
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
throw new WorkspaceError("VALIDATION_ERROR", "pollInterval must not exceed timeout");
|
|
4756
|
+
function writeActiveRunFromAck(config, run) {
|
|
4757
|
+
if (config.workspaceRoot === void 0) {
|
|
4758
|
+
return;
|
|
4135
4759
|
}
|
|
4136
|
-
const
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
const retryBaseMs = options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;
|
|
4140
|
-
const retryMaxMs = options.retryMaxMs ?? DEFAULT_RETRY_MAX_MS;
|
|
4141
|
-
let consecutiveFailures = 0;
|
|
4142
|
-
while (true) {
|
|
4143
|
-
let response;
|
|
4144
|
-
try {
|
|
4145
|
-
response = await client.post(import_agent_runs.AGENT_RUNS_ROUTES.claimAgentRuns(), {
|
|
4146
|
-
projectId,
|
|
4147
|
-
limit: claimLimit
|
|
4148
|
-
});
|
|
4149
|
-
consecutiveFailures = 0;
|
|
4150
|
-
} catch (error) {
|
|
4151
|
-
if (!isTransientClaimError(error)) {
|
|
4152
|
-
throw error;
|
|
4153
|
-
}
|
|
4154
|
-
if (Date.now() >= deadline) {
|
|
4155
|
-
return { items: [], inflightRuns: [], timedOut: true };
|
|
4156
|
-
}
|
|
4157
|
-
consecutiveFailures += 1;
|
|
4158
|
-
const backoffMs = computeBackoffMs(consecutiveFailures, retryBaseMs, retryMaxMs);
|
|
4159
|
-
const remainingMs2 = deadline - Date.now();
|
|
4160
|
-
await sleepFn(Math.min(backoffMs, remainingMs2));
|
|
4161
|
-
continue;
|
|
4162
|
-
}
|
|
4163
|
-
if (response.items.length > 0) {
|
|
4164
|
-
return { items: response.items, inflightRuns: [], timedOut: false };
|
|
4165
|
-
}
|
|
4166
|
-
const inflightRuns = await fetchInflightAgentRuns(client, { projectId, limit: claimLimit });
|
|
4167
|
-
if (inflightRuns.length > 0) {
|
|
4168
|
-
return { items: [], inflightRuns, timedOut: false };
|
|
4169
|
-
}
|
|
4170
|
-
if (Date.now() >= deadline) {
|
|
4171
|
-
return { items: [], inflightRuns: [], timedOut: true };
|
|
4172
|
-
}
|
|
4173
|
-
const remainingMs = deadline - Date.now();
|
|
4174
|
-
const waitMs = Math.min(pollIntervalMs, remainingMs);
|
|
4175
|
-
await sleepFn(waitMs);
|
|
4760
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(config);
|
|
4761
|
+
if (workspaceRoot === null) {
|
|
4762
|
+
return;
|
|
4176
4763
|
}
|
|
4764
|
+
const projectId = resolveProjectId(workspaceRoot);
|
|
4765
|
+
if (projectId === null) {
|
|
4766
|
+
return;
|
|
4767
|
+
}
|
|
4768
|
+
const acknowledgedAt = run.acknowledgedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4769
|
+
writeActiveRunState(workspaceRoot, {
|
|
4770
|
+
agentRunId: run.id,
|
|
4771
|
+
workItemId: run.workItemId,
|
|
4772
|
+
projectId,
|
|
4773
|
+
acknowledgedAt
|
|
4774
|
+
});
|
|
4177
4775
|
}
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
description: "List agent runs for a project (default status PENDING). Prefer wait_for_agent_runs for orchestrator long-polling.",
|
|
4189
|
-
inputSchema: {
|
|
4190
|
-
projectId: z.string().uuid().describe("Project UUID (required)"),
|
|
4191
|
-
status: z.enum(agentRunStatusValues).optional().describe("Filter by agent run status")
|
|
4192
|
-
}
|
|
4193
|
-
},
|
|
4194
|
-
async ({ status, projectId }) => runTool(async () => {
|
|
4195
|
-
const effectiveStatus = status ?? import_agent_run_status4.AGENT_RUN_STATUS.PENDING;
|
|
4196
|
-
return client.get(import_agent_runs2.AGENT_RUNS_ROUTES.listAgentRuns(), {
|
|
4197
|
-
status: effectiveStatus,
|
|
4198
|
-
projectId
|
|
4199
|
-
});
|
|
4200
|
-
})
|
|
4201
|
-
);
|
|
4202
|
-
server.registerTool(
|
|
4203
|
-
"wait_for_agent_runs",
|
|
4204
|
-
{
|
|
4205
|
-
description: "Long-poll agent runs for a project: atomically claims PENDING runs (POST /agent-runs/claim). On empty claim, immediately lists inflight DISPATCHED/ACKNOWLEDGED runs (GET activeOnly) for orphan recovery before sleeping. Returns items (newly claimed), inflightRuns (resume candidates), and orchestrator hints. First claim is immediate; subsequent polls sleep pollInterval seconds only when both claim and inflight are empty.",
|
|
4206
|
-
inputSchema: {
|
|
4207
|
-
projectId: z.string().uuid().describe("Project UUID"),
|
|
4208
|
-
timeout: z.number().int().min(1).max(300).default(120).describe("Maximum wait in seconds (1\u2013300, default 120)"),
|
|
4209
|
-
pollInterval: z.number().int().min(1).max(60).default(2).describe("Seconds between polls (1\u201360, default 2); must not exceed timeout"),
|
|
4210
|
-
limit: z.number().int().min(1).max(10).default(1).describe("Max runs to claim per poll (1\u201310, default 1)")
|
|
4211
|
-
}
|
|
4212
|
-
},
|
|
4213
|
-
async ({ projectId, timeout, pollInterval, limit }) => runTool(async () => {
|
|
4214
|
-
const pollResult = await pollAgentRuns(client, {
|
|
4215
|
-
projectId,
|
|
4216
|
-
timeoutMs: timeout * 1e3,
|
|
4217
|
-
pollIntervalMs: pollInterval * 1e3,
|
|
4218
|
-
limit
|
|
4219
|
-
});
|
|
4220
|
-
return enrichPollResult(pollResult.items, pollResult.inflightRuns, pollResult.timedOut);
|
|
4221
|
-
})
|
|
4222
|
-
);
|
|
4223
|
-
server.registerTool(
|
|
4224
|
-
"ack_agent_run",
|
|
4225
|
-
{
|
|
4226
|
-
description: "Acknowledge agent run delivery after Task subagent has been dispatched. Requires status DISPATCHED (DISPATCHED \u2192 ACKNOWLEDGED).",
|
|
4227
|
-
inputSchema: {
|
|
4228
|
-
agentRunId: z.string().uuid().describe("Agent run UUID"),
|
|
4229
|
-
note: z.string().nullable().optional().describe("Optional acknowledgment note")
|
|
4230
|
-
}
|
|
4231
|
-
},
|
|
4232
|
-
async ({ agentRunId, note }) => runTool(async () => {
|
|
4233
|
-
const body = note !== void 0 ? { note } : void 0;
|
|
4234
|
-
return client.patch(import_agent_runs2.AGENT_RUNS_ROUTES.acknowledgeAgentRun(agentRunId), body);
|
|
4235
|
-
})
|
|
4236
|
-
);
|
|
4237
|
-
server.registerTool(
|
|
4238
|
-
"report_agent_attention",
|
|
4239
|
-
{
|
|
4240
|
-
description: "Signal that the in-flight subagent needs human attention while the run is ACKNOWLEDGED. Sets requiresAttention on the agent run for orchestrator/UI visibility.",
|
|
4241
|
-
inputSchema: {
|
|
4242
|
-
agentRunId: z.string().uuid().describe("Agent run UUID"),
|
|
4243
|
-
message: z.string().min(1).describe("Why human attention is needed")
|
|
4244
|
-
}
|
|
4245
|
-
},
|
|
4246
|
-
async ({ agentRunId, message }) => runTool(async () => {
|
|
4247
|
-
const body = {
|
|
4248
|
-
requiresAttention: true,
|
|
4249
|
-
attentionMessage: message
|
|
4250
|
-
};
|
|
4251
|
-
return client.patch(import_agent_runs2.AGENT_RUNS_ROUTES.setAgentRunAttention(agentRunId), body);
|
|
4252
|
-
})
|
|
4253
|
-
);
|
|
4254
|
-
server.registerTool(
|
|
4255
|
-
"clear_agent_attention",
|
|
4256
|
-
{
|
|
4257
|
-
description: "Clear the human-attention flag after the blocker is resolved. Requires the run to still be ACKNOWLEDGED.",
|
|
4258
|
-
inputSchema: {
|
|
4259
|
-
agentRunId: z.string().uuid().describe("Agent run UUID")
|
|
4260
|
-
}
|
|
4261
|
-
},
|
|
4262
|
-
async ({ agentRunId }) => runTool(async () => {
|
|
4263
|
-
const body = {
|
|
4264
|
-
requiresAttention: false
|
|
4265
|
-
};
|
|
4266
|
-
return client.patch(import_agent_runs2.AGENT_RUNS_ROUTES.setAgentRunAttention(agentRunId), body);
|
|
4267
|
-
})
|
|
4268
|
-
);
|
|
4269
|
-
server.registerTool(
|
|
4270
|
-
"complete_agent_run",
|
|
4271
|
-
{
|
|
4272
|
-
description: `Complete agent run: apply AGENTIC_SDLC workflow transition and optional work item patch. ${AGENTIC_SDLC_COLUMNS_SUMMARY} Outcomes: ${COMPLETE_AGENT_RUN_OUTCOME_SUMMARY}`,
|
|
4273
|
-
inputSchema: {
|
|
4274
|
-
agentRunId: z.string().uuid().describe("Agent run UUID"),
|
|
4275
|
-
outcome: z.enum(agentRunOutcomeValues).optional().describe(
|
|
4276
|
-
`Completion outcome (DEFAULT, SKIP_DESIGN, HAS_BUGS, NEEDS_CLARIFICATION, FAILED). ${COMPLETE_AGENT_RUN_OUTCOME_SUMMARY}`
|
|
4277
|
-
),
|
|
4278
|
-
note: z.string().nullable().optional().describe("Optional completion note"),
|
|
4279
|
-
workItemPatch: z.object({
|
|
4280
|
-
description: z.string().nullable().optional().describe("Updated work item description"),
|
|
4281
|
-
acceptanceCriteria: z.string().nullable().optional().describe("Updated acceptance criteria"),
|
|
4282
|
-
designerRequired: z.boolean().optional().describe("Architect only: whether DESIGNER phase runs before development")
|
|
4283
|
-
}).nullable().optional().describe("Optional work item field updates")
|
|
4284
|
-
}
|
|
4285
|
-
},
|
|
4286
|
-
async ({ agentRunId, outcome, note, workItemPatch }) => runTool(async () => {
|
|
4287
|
-
const body = {};
|
|
4288
|
-
if (outcome !== void 0) {
|
|
4289
|
-
body.outcome = outcome;
|
|
4290
|
-
}
|
|
4291
|
-
if (note !== void 0) {
|
|
4292
|
-
body.note = note;
|
|
4293
|
-
}
|
|
4294
|
-
if (workItemPatch !== void 0 && workItemPatch !== null) {
|
|
4295
|
-
body.workItemPatch = workItemPatch;
|
|
4296
|
-
}
|
|
4297
|
-
const hasBody = outcome !== void 0 || note !== void 0 || workItemPatch !== void 0 && workItemPatch !== null;
|
|
4298
|
-
return client.post(
|
|
4299
|
-
import_agent_runs2.AGENT_RUNS_ROUTES.completeAgentRun(agentRunId),
|
|
4300
|
-
hasBody ? body : void 0
|
|
4301
|
-
);
|
|
4302
|
-
})
|
|
4303
|
-
);
|
|
4304
|
-
}
|
|
4305
|
-
|
|
4306
|
-
// src/tools/orchestrator.ts
|
|
4307
|
-
import { z as z2 } from "zod";
|
|
4308
|
-
|
|
4309
|
-
// src/workspace/resolve-project.ts
|
|
4310
|
-
var import_projects = __toESM(require_projects_api(), 1);
|
|
4311
|
-
import { basename } from "node:path";
|
|
4312
|
-
|
|
4313
|
-
// src/workspace/normalize-token.ts
|
|
4314
|
-
function normalizeToken(value) {
|
|
4315
|
-
return value.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
|
4776
|
+
function clearActiveRunAfterComplete(config) {
|
|
4777
|
+
if (config.workspaceRoot === void 0) {
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(config);
|
|
4781
|
+
if (workspaceRoot === null) {
|
|
4782
|
+
return;
|
|
4783
|
+
}
|
|
4784
|
+
clearActiveRunState(workspaceRoot);
|
|
4785
|
+
clearAttentionPendingState(workspaceRoot);
|
|
4316
4786
|
}
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
key: project.key,
|
|
4324
|
-
presetCode: project.presetCode,
|
|
4325
|
-
matchedBy
|
|
4326
|
-
};
|
|
4787
|
+
function logCliError(deps, message) {
|
|
4788
|
+
if (deps.logError !== void 0) {
|
|
4789
|
+
deps.logError(message);
|
|
4790
|
+
return;
|
|
4791
|
+
}
|
|
4792
|
+
console.error(message);
|
|
4327
4793
|
}
|
|
4328
|
-
function
|
|
4329
|
-
const
|
|
4330
|
-
if (
|
|
4331
|
-
return
|
|
4794
|
+
function readFlagValue(args, flag) {
|
|
4795
|
+
const index = args.indexOf(flag);
|
|
4796
|
+
if (index === -1 || index + 1 >= args.length) {
|
|
4797
|
+
return null;
|
|
4332
4798
|
}
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4799
|
+
return args[index + 1] ?? null;
|
|
4800
|
+
}
|
|
4801
|
+
function removeConsumedFlags(args) {
|
|
4802
|
+
const flagsWithValues = /* @__PURE__ */ new Set([
|
|
4803
|
+
"--category",
|
|
4804
|
+
"--detail",
|
|
4805
|
+
"--event",
|
|
4806
|
+
"--agent-run-id",
|
|
4807
|
+
"--work-item-id",
|
|
4808
|
+
"--project-id",
|
|
4809
|
+
"--acknowledged-at"
|
|
4810
|
+
]);
|
|
4811
|
+
const result = [];
|
|
4812
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
4813
|
+
const arg = args[index];
|
|
4814
|
+
if (flagsWithValues.has(arg)) {
|
|
4815
|
+
index += 1;
|
|
4816
|
+
continue;
|
|
4347
4817
|
}
|
|
4818
|
+
result.push(arg);
|
|
4348
4819
|
}
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4820
|
+
return result;
|
|
4821
|
+
}
|
|
4822
|
+
async function reportAttention(deps, agentRunId, message, workspaceRoot) {
|
|
4823
|
+
const transport = createOrchestratorTransport(deps.client);
|
|
4824
|
+
await transport.setAttention(agentRunId, true, message);
|
|
4825
|
+
writeAttentionPendingState(workspaceRoot, agentRunId);
|
|
4826
|
+
}
|
|
4827
|
+
async function clearAttention(deps, workspaceRoot) {
|
|
4828
|
+
const pending = readAttentionPendingState(workspaceRoot);
|
|
4829
|
+
if (pending === null) {
|
|
4830
|
+
return;
|
|
4352
4831
|
}
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
}
|
|
4832
|
+
const active = readActiveRunState(workspaceRoot);
|
|
4833
|
+
if (!isActiveRunStateUsable(active) || active.agentRunId !== pending.agentRunId) {
|
|
4834
|
+
return;
|
|
4357
4835
|
}
|
|
4358
|
-
const
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4836
|
+
const transport = createOrchestratorTransport(deps.client);
|
|
4837
|
+
try {
|
|
4838
|
+
await transport.setAttention(active.agentRunId, false);
|
|
4839
|
+
} catch (error) {
|
|
4840
|
+
const message = error instanceof Error ? error.message : "Failed to clear agent attention";
|
|
4841
|
+
logCliError(deps, message);
|
|
4842
|
+
} finally {
|
|
4843
|
+
clearAttentionPendingState(workspaceRoot);
|
|
4365
4844
|
}
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4845
|
+
}
|
|
4846
|
+
async function handleAttentionReport(deps, args) {
|
|
4847
|
+
const category = readFlagValue(args, "--category");
|
|
4848
|
+
const detail = readFlagValue(args, "--detail");
|
|
4849
|
+
if (category === null || detail === null) {
|
|
4850
|
+
logCliError(deps, "attention report requires --category and --detail");
|
|
4851
|
+
return 0;
|
|
4852
|
+
}
|
|
4853
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(deps.config);
|
|
4854
|
+
if (workspaceRoot === null) {
|
|
4855
|
+
return 0;
|
|
4856
|
+
}
|
|
4857
|
+
const active = readActiveRunState(workspaceRoot);
|
|
4858
|
+
if (!isActiveRunStateUsable(active)) {
|
|
4859
|
+
return 0;
|
|
4860
|
+
}
|
|
4861
|
+
const message = `[${category}] ${detail.trim()}`;
|
|
4862
|
+
try {
|
|
4863
|
+
await reportAttention(deps, active.agentRunId, message, workspaceRoot);
|
|
4864
|
+
} catch (error) {
|
|
4865
|
+
const messageText = error instanceof Error ? error.message : "Failed to report agent attention";
|
|
4866
|
+
logCliError(deps, messageText);
|
|
4371
4867
|
}
|
|
4372
|
-
return
|
|
4868
|
+
return 0;
|
|
4373
4869
|
}
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4870
|
+
async function handleAttentionReportFromHook(deps, args) {
|
|
4871
|
+
const eventRaw = readFlagValue(args, "--event");
|
|
4872
|
+
if (eventRaw === null) {
|
|
4873
|
+
logCliError(deps, "attention report-from-hook requires --event");
|
|
4874
|
+
return 0;
|
|
4875
|
+
}
|
|
4876
|
+
const event = eventRaw;
|
|
4877
|
+
if (isAfterHookEvent(event)) {
|
|
4878
|
+
return 0;
|
|
4879
|
+
}
|
|
4880
|
+
const readStdin = deps.readStdin ?? defaultReadStdin;
|
|
4881
|
+
const stdin = await readStdin();
|
|
4882
|
+
const payload = parseHookPayloadJson(stdin);
|
|
4883
|
+
if (payload === null) {
|
|
4884
|
+
return 0;
|
|
4885
|
+
}
|
|
4886
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(deps.config);
|
|
4887
|
+
if (workspaceRoot === null) {
|
|
4888
|
+
return 0;
|
|
4889
|
+
}
|
|
4890
|
+
const active = readActiveRunState(workspaceRoot);
|
|
4891
|
+
if (!isActiveRunStateUsable(active)) {
|
|
4892
|
+
return 0;
|
|
4893
|
+
}
|
|
4894
|
+
let message = null;
|
|
4895
|
+
if (event === "beforeShellExecution") {
|
|
4896
|
+
const command = extractShellCommand(payload);
|
|
4897
|
+
if (command === null) {
|
|
4898
|
+
return 0;
|
|
4391
4899
|
}
|
|
4900
|
+
const category = classifyShellCommand(command);
|
|
4901
|
+
if (category === null) {
|
|
4902
|
+
return 0;
|
|
4903
|
+
}
|
|
4904
|
+
message = buildShellAttentionMessage(category, command);
|
|
4905
|
+
} else if (event === "beforeMCPExecution") {
|
|
4906
|
+
const mcp = extractMcpExecution(payload);
|
|
4907
|
+
if (mcp === null) {
|
|
4908
|
+
return 0;
|
|
4909
|
+
}
|
|
4910
|
+
message = buildMcpAttentionMessage(mcp.server, mcp.toolName);
|
|
4911
|
+
} else if (event === "preToolUse") {
|
|
4912
|
+
const preTool = extractPreToolUse(payload);
|
|
4913
|
+
if (preTool === null) {
|
|
4914
|
+
return 0;
|
|
4915
|
+
}
|
|
4916
|
+
message = buildSmartModeAttentionMessage(preTool.toolName, preTool.smartModeReason);
|
|
4392
4917
|
}
|
|
4393
|
-
|
|
4918
|
+
if (message === null) {
|
|
4919
|
+
return 0;
|
|
4920
|
+
}
|
|
4921
|
+
try {
|
|
4922
|
+
await reportAttention(deps, active.agentRunId, message, workspaceRoot);
|
|
4923
|
+
} catch (error) {
|
|
4924
|
+
const messageText = error instanceof Error ? error.message : "Failed to report agent attention from hook";
|
|
4925
|
+
logCliError(deps, messageText);
|
|
4926
|
+
}
|
|
4927
|
+
return 0;
|
|
4394
4928
|
}
|
|
4395
|
-
function
|
|
4396
|
-
const
|
|
4397
|
-
if (
|
|
4398
|
-
return
|
|
4929
|
+
async function handleAttentionClear(deps) {
|
|
4930
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(deps.config);
|
|
4931
|
+
if (workspaceRoot === null) {
|
|
4932
|
+
return 0;
|
|
4399
4933
|
}
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4934
|
+
await clearAttention(deps, workspaceRoot);
|
|
4935
|
+
return 0;
|
|
4936
|
+
}
|
|
4937
|
+
function handleOrchestratorWriteActiveRun(deps, args) {
|
|
4938
|
+
const agentRunId = readFlagValue(args, "--agent-run-id");
|
|
4939
|
+
const workItemId = readFlagValue(args, "--work-item-id");
|
|
4940
|
+
const projectId = readFlagValue(args, "--project-id");
|
|
4941
|
+
const acknowledgedAt = readFlagValue(args, "--acknowledged-at") ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4942
|
+
if (agentRunId === null || workItemId === null || projectId === null) {
|
|
4943
|
+
logCliError(deps, "orchestrator write-active-run requires --agent-run-id, --work-item-id, --project-id");
|
|
4944
|
+
return 0;
|
|
4945
|
+
}
|
|
4946
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(deps.config);
|
|
4947
|
+
if (workspaceRoot === null) {
|
|
4948
|
+
return 0;
|
|
4949
|
+
}
|
|
4950
|
+
writeActiveRunState(workspaceRoot, {
|
|
4951
|
+
agentRunId,
|
|
4952
|
+
workItemId,
|
|
4953
|
+
projectId,
|
|
4954
|
+
acknowledgedAt
|
|
4955
|
+
});
|
|
4956
|
+
return 0;
|
|
4957
|
+
}
|
|
4958
|
+
function handleOrchestratorClearActiveRun(deps) {
|
|
4959
|
+
const workspaceRoot = resolveBridgeWorkspaceRoot(deps.config);
|
|
4960
|
+
if (workspaceRoot === null) {
|
|
4961
|
+
return 0;
|
|
4962
|
+
}
|
|
4963
|
+
clearOrchestratorBridgeState(workspaceRoot);
|
|
4964
|
+
return 0;
|
|
4965
|
+
}
|
|
4966
|
+
async function runAttentionCli(argv, deps) {
|
|
4967
|
+
const args = removeConsumedFlags(argv);
|
|
4968
|
+
const topLevel = args[0];
|
|
4969
|
+
if (topLevel === "attention") {
|
|
4970
|
+
const subcommand = args[1];
|
|
4971
|
+
if (subcommand === "report") {
|
|
4972
|
+
return handleAttentionReport(deps, argv);
|
|
4405
4973
|
}
|
|
4406
|
-
if (
|
|
4407
|
-
|
|
4974
|
+
if (subcommand === "report-from-hook") {
|
|
4975
|
+
return handleAttentionReportFromHook(deps, argv);
|
|
4408
4976
|
}
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
allIds.add(id);
|
|
4977
|
+
if (subcommand === "clear") {
|
|
4978
|
+
return handleAttentionClear(deps);
|
|
4412
4979
|
}
|
|
4980
|
+
logCliError(deps, `Unknown attention subcommand: ${subcommand ?? "(missing)"}`);
|
|
4981
|
+
return 0;
|
|
4413
4982
|
}
|
|
4414
|
-
if (
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
const projectId = [...allIds][0];
|
|
4419
|
-
if (projectId === void 0) {
|
|
4420
|
-
return { status: "missing" };
|
|
4983
|
+
if (topLevel === "orchestrator") {
|
|
4984
|
+
const subcommand = args[1];
|
|
4985
|
+
if (subcommand === "write-active-run") {
|
|
4986
|
+
return handleOrchestratorWriteActiveRun(deps, argv);
|
|
4421
4987
|
}
|
|
4422
|
-
|
|
4988
|
+
if (subcommand === "clear-active-run") {
|
|
4989
|
+
return handleOrchestratorClearActiveRun(deps);
|
|
4990
|
+
}
|
|
4991
|
+
logCliError(deps, `Unknown orchestrator subcommand: ${subcommand ?? "(missing)"}`);
|
|
4992
|
+
return 0;
|
|
4423
4993
|
}
|
|
4424
|
-
return
|
|
4994
|
+
return 1;
|
|
4995
|
+
}
|
|
4996
|
+
function isAttentionCliInvocation(argv) {
|
|
4997
|
+
const topLevel = argv[0];
|
|
4998
|
+
return topLevel === "attention" || topLevel === "orchestrator";
|
|
4425
4999
|
}
|
|
4426
5000
|
|
|
4427
|
-
// src/
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
function parseTaskBoardsYaml(workspaceRoot) {
|
|
4434
|
-
const filePath = join2(workspaceRoot, TASK_BOARDS_FILE);
|
|
4435
|
-
if (!existsSync2(filePath)) {
|
|
4436
|
-
return null;
|
|
5001
|
+
// src/config.ts
|
|
5002
|
+
var import_api = __toESM(require_build(), 1);
|
|
5003
|
+
function resolveSanitizeResponses() {
|
|
5004
|
+
const raw = process.env.TASK_BOARDS_MCP_SANITIZE;
|
|
5005
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
5006
|
+
return true;
|
|
4437
5007
|
}
|
|
4438
|
-
const
|
|
4439
|
-
|
|
4440
|
-
|
|
5008
|
+
const normalized = raw.trim().toLowerCase();
|
|
5009
|
+
return normalized !== "false" && normalized !== "0" && normalized !== "no";
|
|
5010
|
+
}
|
|
5011
|
+
function resolveBlockSensitiveAttachments() {
|
|
5012
|
+
const raw = process.env.TASK_BOARDS_MCP_BLOCK_SENSITIVE_ATTACHMENTS;
|
|
5013
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
5014
|
+
return true;
|
|
4441
5015
|
}
|
|
4442
|
-
const
|
|
4443
|
-
|
|
4444
|
-
|
|
5016
|
+
const normalized = raw.trim().toLowerCase();
|
|
5017
|
+
return normalized !== "false" && normalized !== "0" && normalized !== "no";
|
|
5018
|
+
}
|
|
5019
|
+
function loadConfig() {
|
|
5020
|
+
const apiUrl = process.env.TASK_BOARDS_API_URL ?? "http://localhost:3000";
|
|
5021
|
+
const apiToken = process.env.TASK_BOARDS_API_TOKEN;
|
|
5022
|
+
const workspaceRoot = process.env.WORKSPACE_ROOT;
|
|
5023
|
+
if (process.env.NODE_ENV === "production" && (apiToken === void 0 || apiToken.trim() === "")) {
|
|
5024
|
+
console.error("WARNING: TASK_BOARDS_API_TOKEN is not set. MCP server cannot authenticate to the Task Boards API.");
|
|
4445
5025
|
}
|
|
4446
|
-
return
|
|
5026
|
+
return {
|
|
5027
|
+
apiUrl: `${apiUrl.replace(/\/$/, "")}${import_api.API_V1_PREFIX}`,
|
|
5028
|
+
apiToken,
|
|
5029
|
+
workspaceRoot: workspaceRoot !== void 0 && workspaceRoot.trim() !== "" ? workspaceRoot : void 0,
|
|
5030
|
+
sanitizeResponses: resolveSanitizeResponses(),
|
|
5031
|
+
blockSensitiveAttachments: resolveBlockSensitiveAttachments()
|
|
5032
|
+
};
|
|
5033
|
+
}
|
|
5034
|
+
|
|
5035
|
+
// src/index.ts
|
|
5036
|
+
import { createRequire } from "node:module";
|
|
5037
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5038
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5039
|
+
|
|
5040
|
+
// src/tools/agent-runs.ts
|
|
5041
|
+
var import_agent_runs3 = __toESM(require_agent_runs_api(), 1);
|
|
5042
|
+
var import_agent_run_outcome = __toESM(require_agent_run_outcome_enum(), 1);
|
|
5043
|
+
var import_agent_run_status4 = __toESM(require_agent_run_status_enum(), 1);
|
|
5044
|
+
import { z as z3 } from "zod";
|
|
5045
|
+
|
|
5046
|
+
// src/tools/orchestrator-hints.ts
|
|
5047
|
+
var import_agent_run_status3 = __toESM(require_agent_run_status_enum(), 1);
|
|
5048
|
+
var import_subagent_role = __toESM(require_subagent_role_enum(), 1);
|
|
5049
|
+
var SLEEPER_POLICY = {
|
|
5050
|
+
singleBlockingPoll: true,
|
|
5051
|
+
noParallelPoll: true,
|
|
5052
|
+
noDuplicateWake: true,
|
|
5053
|
+
guidance: "Run exactly one blocking poll (run_orchestrator_once or wait_for_agent_runs) per workspace at a time; do not overlap polls or stack duplicate automation wakes."
|
|
5054
|
+
};
|
|
5055
|
+
var IDLE_INFLIGHT_CHECK_STEP = "Checked inflight runs (DISPATCHED/ACKNOWLEDGED); none found \u2014 true idle.";
|
|
5056
|
+
var IDLE_NO_DUPLICATE_WAKE_STEP = "Schedule at most one follow-up automation wake; do not stack duplicate sleepers while a poll is in flight.";
|
|
5057
|
+
var PROCESSING_NO_PARALLEL_POLL_STEP = "Do not start another poll until this batch finishes.";
|
|
5058
|
+
var INFLIGHT_DISPATCHED_RECOVERY_STEP = "Orphan DISPATCHED \u2014 resume Task+ack; avoid duplicate Task if subagent already running in this session";
|
|
5059
|
+
var IDLE_TIMEOUT_DO_NOT_CLEAR_ATTENTION = "Do not clear_agent_attention on timeout; badge remains until human resolves in IDE";
|
|
5060
|
+
var IDLE_TIMEOUT_NEXT_STEPS = [
|
|
5061
|
+
"Call sync_git_releases(projectId) to sync work-item commits",
|
|
5062
|
+
IDLE_TIMEOUT_DO_NOT_CLEAR_ATTENTION,
|
|
5063
|
+
"Call wait_for_agent_runs again (or run_orchestrator_once) to continue monitoring"
|
|
5064
|
+
];
|
|
5065
|
+
var IDE_ATTENTION_NOT_IN_AWAITING = "IDE attention is not in-awaiting; do not move column; replace attention message on re-report";
|
|
5066
|
+
var PROCESSING_NEXT_STEPS = [
|
|
5067
|
+
"Call sync_project_subagents(projectId) before Task dispatch when the project uses custom subagent slugs",
|
|
5068
|
+
"Agent runs only for STORY items assigned to \u0410\u0433\u0435\u043D\u0442 \u0418\u0418 (SERVICE user); assign via update_work_item before moving to agent columns",
|
|
5069
|
+
"When a subagent is blocked, call report_agent_attention(agentRunId, message); clear with clear_agent_attention after resolution",
|
|
5070
|
+
"After processing all runs: local git commit with work-item:{uuid} tag (skip commit and sync_git_releases when any run completed with SKIP_DEV)",
|
|
5071
|
+
"On feature branch (not master/main): git push origin HEAD then sync_git_releases(projectId)",
|
|
5072
|
+
"Parallel Task runs OK for same workItemId when agentRole differs (e.g. FRONTEND_DEVELOPER + DEVELOPER)",
|
|
5073
|
+
"Claim priority: in-development > in-qa > in-analysis; in-analysis blocked while SERVICE-assigned stories exist in dev/qa",
|
|
5074
|
+
"Call run_orchestrator_once or wait_for_agent_runs to continue monitoring"
|
|
5075
|
+
];
|
|
5076
|
+
var ATTENTION_PROCESSING_NEXT_STEPS = [
|
|
5077
|
+
"Resolve requiresAttention runs before dispatching new Task subagents",
|
|
5078
|
+
IDE_ATTENTION_NOT_IN_AWAITING,
|
|
5079
|
+
"Call clear_agent_attention(agentRunId) after each blocker is resolved"
|
|
5080
|
+
];
|
|
5081
|
+
var RUN_LIFECYCLE_DOC = {
|
|
5082
|
+
phases: ["dispatched", "acknowledged", "awaiting_ide_approval", "ready_to_complete"],
|
|
5083
|
+
terminalPhase: "complete",
|
|
5084
|
+
ideAttentionVsWorkflowAwaiting: "IDE attention (report_agent_attention on ACKNOWLEDGED runs) is not in-awaiting \u2014 do not move the story column for shell/git/network/MCP/Smart Mode approvals. Use complete_agent_run(NEEDS_CLARIFICATION) and in-awaiting only for workflow clarifications."
|
|
5085
|
+
};
|
|
5086
|
+
function buildTaskStep(run) {
|
|
5087
|
+
const subagent = run.payload.suggestedSubagentType ?? "subagent";
|
|
5088
|
+
return `Task(subagent_type=${subagent}, prompt=payload.contextSummary)`;
|
|
5089
|
+
}
|
|
5090
|
+
function buildCompleteStep(run) {
|
|
5091
|
+
if (run.agentRole === import_subagent_role.SUBAGENT_ROLE.ARCHITECT) {
|
|
5092
|
+
return "complete_agent_run after work; architect may set workItemPatch.designerRequired (boolean, default false)";
|
|
5093
|
+
}
|
|
5094
|
+
if (run.agentRole === import_subagent_role.SUBAGENT_ROLE.ANALYST) {
|
|
5095
|
+
return "complete_agent_run after work; use outcome SKIP_DEV when codeChangesRequired=false (released without git commit)";
|
|
5096
|
+
}
|
|
5097
|
+
return "complete_agent_run after work";
|
|
5098
|
+
}
|
|
5099
|
+
function buildGitCommitStep(run) {
|
|
5100
|
+
if (run.agentRole === import_subagent_role.SUBAGENT_ROLE.ANALYST) {
|
|
5101
|
+
return "skip git commit and sync_git_releases when outcome SKIP_DEV; otherwise git commit with work-item tag (local; push only on feature branch)";
|
|
5102
|
+
}
|
|
5103
|
+
return "git commit with work-item tag (local; push only on feature branch)";
|
|
5104
|
+
}
|
|
5105
|
+
function buildReportAttentionStep(run) {
|
|
5106
|
+
return `report_agent_attention(${run.id}, message) if blocked awaiting human input \u2014 ${ATTENTION_MESSAGE_TEMPLATES.shell.messagePattern.replace("{detail}", "<detail>")}; replaces prior message on re-report`;
|
|
5107
|
+
}
|
|
5108
|
+
function buildProcessRunAttentionHint(run) {
|
|
5109
|
+
const guidance = buildAttentionGuidance(run);
|
|
5110
|
+
return {
|
|
5111
|
+
phase: guidance.phase,
|
|
5112
|
+
requiresAttention: run.requiresAttention,
|
|
5113
|
+
message: run.attentionMessage,
|
|
5114
|
+
detectedCategory: guidance.detectedCategory,
|
|
5115
|
+
reportGuidance: `report_agent_attention(${run.id}, message) using formatAttentionMessage(category, detail); replaces prior message on same ACK run`,
|
|
5116
|
+
clearGuidance: guidance.clearStep,
|
|
5117
|
+
templateCategories: guidance.templateExamples
|
|
5118
|
+
};
|
|
5119
|
+
}
|
|
5120
|
+
function buildDispatchedProcessRunSteps(run) {
|
|
5121
|
+
return [
|
|
5122
|
+
"sync_project_subagents(projectId) when payload.suggestedSubagentType is a project custom slug",
|
|
5123
|
+
`list_work_item_attachments(${run.workItemId})`,
|
|
5124
|
+
"download_work_item_attachments if needed",
|
|
5125
|
+
buildTaskStep(run),
|
|
5126
|
+
`ack_agent_run(${run.id})`,
|
|
5127
|
+
buildReportAttentionStep(run),
|
|
5128
|
+
`clear_agent_attention(${run.id}) after human resolves in IDE`,
|
|
5129
|
+
buildCompleteStep(run),
|
|
5130
|
+
buildGitCommitStep(run)
|
|
5131
|
+
];
|
|
4447
5132
|
}
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
function assertDirectory(path, source) {
|
|
4455
|
-
const absolutePath = resolve(path);
|
|
4456
|
-
if (!existsSync3(absolutePath)) {
|
|
4457
|
-
throw new WorkspaceError("WORKSPACE_NOT_FOUND", `${source} does not exist: ${absolutePath}`);
|
|
4458
|
-
}
|
|
4459
|
-
const stats = statSync(absolutePath);
|
|
4460
|
-
if (!stats.isDirectory()) {
|
|
4461
|
-
throw new WorkspaceError("WORKSPACE_NOT_FOUND", `${source} is not a directory: ${absolutePath}`);
|
|
4462
|
-
}
|
|
4463
|
-
return absolutePath;
|
|
5133
|
+
function buildAwaitingIdeApprovalProcessRunSteps(run) {
|
|
5134
|
+
return [
|
|
5135
|
+
`clear_agent_attention(${run.id}) after resolving: ${run.attentionMessage ?? "attention flag"}`,
|
|
5136
|
+
buildCompleteStep(run),
|
|
5137
|
+
buildGitCommitStep(run)
|
|
5138
|
+
];
|
|
4464
5139
|
}
|
|
4465
|
-
function
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
}
|
|
5140
|
+
function buildAcknowledgedProcessRunSteps(run) {
|
|
5141
|
+
return [
|
|
5142
|
+
"Resume subagent work (Task may already be running in this session)",
|
|
5143
|
+
buildReportAttentionStep(run),
|
|
5144
|
+
`clear_agent_attention(${run.id}) after human resolves in IDE`,
|
|
5145
|
+
buildCompleteStep(run),
|
|
5146
|
+
buildGitCommitStep(run)
|
|
5147
|
+
];
|
|
4474
5148
|
}
|
|
4475
|
-
function
|
|
4476
|
-
|
|
4477
|
-
|
|
5149
|
+
function buildProcessRunHint(run, source) {
|
|
5150
|
+
const phase = deriveOrchestratorRunPhase(run);
|
|
5151
|
+
if (phase === "awaiting_ide_approval") {
|
|
5152
|
+
return {
|
|
5153
|
+
agentRunId: run.id,
|
|
5154
|
+
workItemId: run.workItemId,
|
|
5155
|
+
phase,
|
|
5156
|
+
source,
|
|
5157
|
+
steps: buildAwaitingIdeApprovalProcessRunSteps(run),
|
|
5158
|
+
attention: buildProcessRunAttentionHint(run)
|
|
5159
|
+
};
|
|
5160
|
+
}
|
|
5161
|
+
if (phase === "acknowledged") {
|
|
5162
|
+
return {
|
|
5163
|
+
agentRunId: run.id,
|
|
5164
|
+
workItemId: run.workItemId,
|
|
5165
|
+
phase,
|
|
5166
|
+
source,
|
|
5167
|
+
steps: buildAcknowledgedProcessRunSteps(run)
|
|
5168
|
+
};
|
|
5169
|
+
}
|
|
5170
|
+
const steps = buildDispatchedProcessRunSteps(run);
|
|
5171
|
+
if (source === "inflight") {
|
|
5172
|
+
return {
|
|
5173
|
+
agentRunId: run.id,
|
|
5174
|
+
workItemId: run.workItemId,
|
|
5175
|
+
phase,
|
|
5176
|
+
source,
|
|
5177
|
+
steps: [INFLIGHT_DISPATCHED_RECOVERY_STEP, ...steps]
|
|
5178
|
+
};
|
|
4478
5179
|
}
|
|
5180
|
+
return {
|
|
5181
|
+
agentRunId: run.id,
|
|
5182
|
+
workItemId: run.workItemId,
|
|
5183
|
+
phase,
|
|
5184
|
+
source,
|
|
5185
|
+
steps
|
|
5186
|
+
};
|
|
4479
5187
|
}
|
|
4480
|
-
function
|
|
4481
|
-
|
|
4482
|
-
for (
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
return current;
|
|
4486
|
-
}
|
|
4487
|
-
const parent = dirname(current);
|
|
4488
|
-
if (parent === current) {
|
|
4489
|
-
break;
|
|
5188
|
+
function buildProcessRuns(items, inflightRuns) {
|
|
5189
|
+
const hints = [];
|
|
5190
|
+
for (const run of items) {
|
|
5191
|
+
if (run.requiresAttention) {
|
|
5192
|
+
hints.push(buildProcessRunHint(run, "claimed"));
|
|
4490
5193
|
}
|
|
4491
|
-
current = parent;
|
|
4492
5194
|
}
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
const allowedRoot = envWorkspaceRoot !== void 0 && envWorkspaceRoot.trim() !== "" ? assertDirectory(envWorkspaceRoot, "WORKSPACE_ROOT") : void 0;
|
|
4497
|
-
if (explicitOverride !== void 0 && explicitOverride.trim() !== "") {
|
|
4498
|
-
const resolved = assertDirectory(explicitOverride, "workspaceRoot");
|
|
4499
|
-
if (allowedRoot !== void 0) {
|
|
4500
|
-
assertWithinAllowedRoot(resolved, allowedRoot);
|
|
4501
|
-
} else {
|
|
4502
|
-
assertGitRepository(resolved);
|
|
5195
|
+
for (const run of inflightRuns) {
|
|
5196
|
+
if (run.requiresAttention) {
|
|
5197
|
+
hints.push(buildProcessRunHint(run, "inflight"));
|
|
4503
5198
|
}
|
|
4504
|
-
return resolved;
|
|
4505
5199
|
}
|
|
4506
|
-
|
|
4507
|
-
|
|
5200
|
+
for (const run of inflightRuns) {
|
|
5201
|
+
if (!run.requiresAttention) {
|
|
5202
|
+
hints.push(buildProcessRunHint(run, "inflight"));
|
|
5203
|
+
}
|
|
4508
5204
|
}
|
|
4509
|
-
const
|
|
4510
|
-
|
|
4511
|
-
|
|
5205
|
+
for (const run of items) {
|
|
5206
|
+
if (!run.requiresAttention) {
|
|
5207
|
+
hints.push(buildProcessRunHint(run, "claimed"));
|
|
5208
|
+
}
|
|
4512
5209
|
}
|
|
4513
|
-
return
|
|
5210
|
+
return hints;
|
|
4514
5211
|
}
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
function emptyResponse(workspaceRoot) {
|
|
5212
|
+
function buildAttentionSummary(runs) {
|
|
5213
|
+
const blockedRuns = runs.filter((run) => run.requiresAttention);
|
|
4518
5214
|
return {
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
name: null,
|
|
4523
|
-
key: null,
|
|
4524
|
-
presetCode: null,
|
|
4525
|
-
hint: "No project binding found. Add .task-boards.yaml, an IDE rule (.cursor/rules) with projectId, or rename the folder to match a project key/name."
|
|
5215
|
+
blockedRunCount: blockedRuns.length,
|
|
5216
|
+
blockedAgentRunIds: blockedRuns.map((run) => run.id),
|
|
5217
|
+
resolveBeforeNewDispatch: blockedRuns.length > 0
|
|
4526
5218
|
};
|
|
4527
5219
|
}
|
|
4528
|
-
function
|
|
5220
|
+
function buildInflightSummary(inflightRuns) {
|
|
5221
|
+
if (inflightRuns.length === 0) {
|
|
5222
|
+
return void 0;
|
|
5223
|
+
}
|
|
4529
5224
|
return {
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
key: project.key,
|
|
4535
|
-
presetCode: project.presetCode
|
|
5225
|
+
dispatchedCount: inflightRuns.filter((run) => run.status === import_agent_run_status3.AGENT_RUN_STATUS.DISPATCHED).length,
|
|
5226
|
+
acknowledgedCount: inflightRuns.filter((run) => run.status === import_agent_run_status3.AGENT_RUN_STATUS.ACKNOWLEDGED).length,
|
|
5227
|
+
requiresAttentionCount: inflightRuns.filter((run) => run.requiresAttention).length,
|
|
5228
|
+
agentRunIds: inflightRuns.map((run) => run.id)
|
|
4536
5229
|
};
|
|
4537
5230
|
}
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
}
|
|
4542
|
-
async function resolveProject(client, options = {}) {
|
|
4543
|
-
const workspaceRoot = resolveWorkspaceRoot(options.workspaceRootOverride, options.envWorkspaceRoot);
|
|
4544
|
-
const yamlProjectId = parseTaskBoardsYaml(workspaceRoot);
|
|
4545
|
-
if (yamlProjectId !== null) {
|
|
4546
|
-
return enrichProject(client, workspaceRoot, "yaml", yamlProjectId);
|
|
5231
|
+
function derivePollResultSource(items, inflightRuns) {
|
|
5232
|
+
if (items.length > 0) {
|
|
5233
|
+
return "claimed";
|
|
4547
5234
|
}
|
|
4548
|
-
|
|
4549
|
-
|
|
5235
|
+
if (inflightRuns.length > 0) {
|
|
5236
|
+
return "inflight";
|
|
5237
|
+
}
|
|
5238
|
+
return "idle";
|
|
5239
|
+
}
|
|
5240
|
+
function buildOrchestratorHints(items, inflightRuns, timedOut) {
|
|
5241
|
+
const pollResultSource = derivePollResultSource(items, inflightRuns);
|
|
5242
|
+
const allRuns = [...items, ...inflightRuns];
|
|
5243
|
+
if (timedOut) {
|
|
4550
5244
|
return {
|
|
4551
|
-
|
|
4552
|
-
|
|
5245
|
+
phase: "idle",
|
|
5246
|
+
pollResultSource,
|
|
5247
|
+
sleeperPolicy: SLEEPER_POLICY,
|
|
5248
|
+
nextSteps: [IDLE_INFLIGHT_CHECK_STEP, ...IDLE_TIMEOUT_NEXT_STEPS, IDLE_NO_DUPLICATE_WAKE_STEP]
|
|
4553
5249
|
};
|
|
4554
5250
|
}
|
|
4555
|
-
if (
|
|
4556
|
-
return enrichProject(client, workspaceRoot, "rule", rulesResult.projectId);
|
|
4557
|
-
}
|
|
4558
|
-
const folderToken = basename(workspaceRoot);
|
|
4559
|
-
const listResponse = await client.get(import_projects.PROJECTS_ROUTES.listProjects());
|
|
4560
|
-
const autoMatch = autoMatchProject(folderToken, listResponse.items);
|
|
4561
|
-
if (autoMatch.kind === "single") {
|
|
4562
|
-
return enrichProject(client, workspaceRoot, "auto_match", autoMatch.project.id);
|
|
4563
|
-
}
|
|
4564
|
-
if (autoMatch.kind === "candidates") {
|
|
5251
|
+
if (allRuns.length === 0) {
|
|
4565
5252
|
return {
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
key: null,
|
|
4571
|
-
presetCode: null,
|
|
4572
|
-
candidates: autoMatch.candidates,
|
|
4573
|
-
hint: `Multiple projects match folder name "${folderToken}". Pick one candidate or add .task-boards.yaml.`
|
|
5253
|
+
phase: "idle",
|
|
5254
|
+
pollResultSource,
|
|
5255
|
+
sleeperPolicy: SLEEPER_POLICY,
|
|
5256
|
+
nextSteps: [IDLE_INFLIGHT_CHECK_STEP, ...IDLE_TIMEOUT_NEXT_STEPS, IDLE_NO_DUPLICATE_WAKE_STEP]
|
|
4574
5257
|
};
|
|
4575
5258
|
}
|
|
4576
|
-
|
|
5259
|
+
const hasAttentionRuns = allRuns.some((run) => run.requiresAttention);
|
|
5260
|
+
return {
|
|
5261
|
+
phase: "processing",
|
|
5262
|
+
pollResultSource,
|
|
5263
|
+
sleeperPolicy: SLEEPER_POLICY,
|
|
5264
|
+
inflightSummary: buildInflightSummary(inflightRuns),
|
|
5265
|
+
processRuns: buildProcessRuns(items, inflightRuns),
|
|
5266
|
+
attentionSummary: buildAttentionSummary(allRuns),
|
|
5267
|
+
runLifecycle: RUN_LIFECYCLE_DOC,
|
|
5268
|
+
nextSteps: hasAttentionRuns ? [PROCESSING_NO_PARALLEL_POLL_STEP, ...ATTENTION_PROCESSING_NEXT_STEPS, ...PROCESSING_NEXT_STEPS] : [PROCESSING_NO_PARALLEL_POLL_STEP, ...PROCESSING_NEXT_STEPS]
|
|
5269
|
+
};
|
|
4577
5270
|
}
|
|
4578
|
-
|
|
4579
|
-
// src/tools/orchestrator.ts
|
|
4580
|
-
async function runOrchestratorOnce(client, options) {
|
|
4581
|
-
let projectId = options.projectId;
|
|
4582
|
-
let resolutionSource = "explicit";
|
|
4583
|
-
if (projectId === void 0) {
|
|
4584
|
-
const resolved = await resolveProject(client, {
|
|
4585
|
-
workspaceRootOverride: options.workspaceRootOverride,
|
|
4586
|
-
envWorkspaceRoot: options.envWorkspaceRoot
|
|
4587
|
-
});
|
|
4588
|
-
if (resolved.projectId === null) {
|
|
4589
|
-
const hint = resolved.hint ?? "Could not resolve project for workspace.";
|
|
4590
|
-
throw new WorkspaceError("PROJECT_NOT_FOUND", hint);
|
|
4591
|
-
}
|
|
4592
|
-
projectId = resolved.projectId;
|
|
4593
|
-
resolutionSource = resolved.resolutionSource;
|
|
4594
|
-
}
|
|
4595
|
-
const pollResult = await pollAgentRuns(client, {
|
|
4596
|
-
projectId,
|
|
4597
|
-
timeoutMs: options.timeoutMs,
|
|
4598
|
-
pollIntervalMs: options.pollIntervalMs,
|
|
4599
|
-
limit: options.limit
|
|
4600
|
-
});
|
|
4601
|
-
const enriched = enrichPollResult(pollResult.items, pollResult.inflightRuns, pollResult.timedOut);
|
|
5271
|
+
function enrichPollResult(items, inflightRuns, timedOut) {
|
|
4602
5272
|
return {
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
timedOut: enriched.timedOut,
|
|
4608
|
-
orchestrator: enriched.orchestrator
|
|
5273
|
+
items,
|
|
5274
|
+
inflightRuns,
|
|
5275
|
+
timedOut,
|
|
5276
|
+
orchestrator: buildOrchestratorHints(items, inflightRuns, timedOut)
|
|
4609
5277
|
};
|
|
4610
5278
|
}
|
|
4611
|
-
|
|
5279
|
+
|
|
5280
|
+
// src/tools/agent-runs.ts
|
|
5281
|
+
var agentRunStatusValues = Object.values(import_agent_run_status4.AGENT_RUN_STATUS);
|
|
5282
|
+
var agentRunOutcomeValues = Object.values(import_agent_run_outcome.AGENT_RUN_OUTCOME);
|
|
5283
|
+
var AGENTIC_SDLC_COLUMNS_SUMMARY = "AGENTIC_SDLC has 7 columns: backlog, in-analysis (PRODUCT\u2192ANALYST\u2192ARCHITECT handoffs), in-development (DESIGNER optional, then parallel FRONTEND_DEVELOPER+DEVELOPER), in-qa, in-awaiting, done, released.";
|
|
5284
|
+
var COMPLETE_AGENT_RUN_OUTCOME_SUMMARY = "Prerequisite: STORY assignee must be \u0410\u0433\u0435\u043D\u0442 \u0418\u0418 (SERVICE user) \u2014 otherwise no agent_run is enqueued or claimed. in-analysis: PRODUCT/ANALYST DEFAULT \u2192 HANDOFF next phase (skip unbound roles); ANALYST SKIP_DESIGN \u2192 in-development; ANALYST SKIP_DEV \u2192 released when codeChangesRequired=false (no git commit); ARCHITECT DEFAULT \u2192 in-development (set workItemPatch.designerRequired). in-development: DESIGNER DEFAULT \u2192 DEVELOPMENT; dev role DEFAULT removes role from pendingDevRoles, moves to in-qa when empty; SKIP_DESIGN skips DESIGNER. in-qa: DEFAULT \u2192 done; HAS_BUGS \u2192 in-development (re-init pendingDevRoles). NEEDS_CLARIFICATION \u2192 in-awaiting. FAILED \u2192 no move. Claim priority: in-development > in-qa > in-analysis (SERVICE-assigned stories only block analysis).";
|
|
5285
|
+
function registerAgentRunTools(server, client, config) {
|
|
5286
|
+
server.registerTool(
|
|
5287
|
+
"list_agent_runs",
|
|
5288
|
+
{
|
|
5289
|
+
description: "List agent runs for a project (default status PENDING). Prefer wait_for_agent_runs for orchestrator long-polling.",
|
|
5290
|
+
inputSchema: {
|
|
5291
|
+
projectId: z3.string().uuid().describe("Project UUID (required)"),
|
|
5292
|
+
status: z3.enum(agentRunStatusValues).optional().describe("Filter by agent run status")
|
|
5293
|
+
}
|
|
5294
|
+
},
|
|
5295
|
+
async ({ status, projectId }) => runTool(async () => {
|
|
5296
|
+
const effectiveStatus = status ?? import_agent_run_status4.AGENT_RUN_STATUS.PENDING;
|
|
5297
|
+
return client.get(import_agent_runs3.AGENT_RUNS_ROUTES.listAgentRuns(), {
|
|
5298
|
+
status: effectiveStatus,
|
|
5299
|
+
projectId
|
|
5300
|
+
});
|
|
5301
|
+
})
|
|
5302
|
+
);
|
|
4612
5303
|
server.registerTool(
|
|
4613
|
-
"
|
|
5304
|
+
"wait_for_agent_runs",
|
|
4614
5305
|
{
|
|
4615
|
-
description: "
|
|
5306
|
+
description: "Long-poll agent runs for a project: atomically claims PENDING runs (POST /agent-runs/claim). On empty claim, immediately lists inflight DISPATCHED/ACKNOWLEDGED runs (GET activeOnly) for orphan recovery before sleeping. Returns items (newly claimed), inflightRuns (resume candidates), and orchestrator hints. First claim is immediate; subsequent polls sleep pollInterval seconds only when both claim and inflight are empty.",
|
|
4616
5307
|
inputSchema: {
|
|
4617
|
-
projectId:
|
|
4618
|
-
timeout:
|
|
4619
|
-
pollInterval:
|
|
4620
|
-
limit:
|
|
4621
|
-
workspaceRoot: z2.string().optional().describe("Optional absolute workspace root; defaults to WORKSPACE_ROOT or upward search from cwd")
|
|
5308
|
+
projectId: z3.string().uuid().describe("Project UUID"),
|
|
5309
|
+
timeout: z3.number().int().min(1).max(300).default(120).describe("Maximum wait in seconds (1\u2013300, default 120)"),
|
|
5310
|
+
pollInterval: z3.number().int().min(1).max(60).default(2).describe("Seconds between polls (1\u201360, default 2); must not exceed timeout"),
|
|
5311
|
+
limit: z3.number().int().min(1).max(10).default(1).describe("Max runs to claim per poll (1\u201310, default 1)")
|
|
4622
5312
|
}
|
|
4623
5313
|
},
|
|
4624
|
-
async ({ projectId, timeout, pollInterval, limit
|
|
4625
|
-
|
|
5314
|
+
async ({ projectId, timeout, pollInterval, limit }) => runTool(async () => {
|
|
5315
|
+
const pollResult = await pollAgentRuns(client, {
|
|
4626
5316
|
projectId,
|
|
4627
5317
|
timeoutMs: timeout * 1e3,
|
|
4628
5318
|
pollIntervalMs: pollInterval * 1e3,
|
|
4629
|
-
limit
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
5319
|
+
limit
|
|
5320
|
+
});
|
|
5321
|
+
return enrichPollResult(pollResult.items, pollResult.inflightRuns, pollResult.timedOut);
|
|
5322
|
+
})
|
|
5323
|
+
);
|
|
5324
|
+
server.registerTool(
|
|
5325
|
+
"ack_agent_run",
|
|
5326
|
+
{
|
|
5327
|
+
description: "Acknowledge agent run delivery after Task subagent has been dispatched. Requires status DISPATCHED (DISPATCHED \u2192 ACKNOWLEDGED).",
|
|
5328
|
+
inputSchema: {
|
|
5329
|
+
agentRunId: z3.string().uuid().describe("Agent run UUID"),
|
|
5330
|
+
note: z3.string().nullable().optional().describe("Optional acknowledgment note")
|
|
5331
|
+
}
|
|
5332
|
+
},
|
|
5333
|
+
async ({ agentRunId, note }) => runTool(async () => {
|
|
5334
|
+
const body = note !== void 0 ? { note } : void 0;
|
|
5335
|
+
const response = await client.patch(import_agent_runs3.AGENT_RUNS_ROUTES.acknowledgeAgentRun(agentRunId), body);
|
|
5336
|
+
writeActiveRunFromAck(config, response);
|
|
5337
|
+
return response;
|
|
5338
|
+
})
|
|
5339
|
+
);
|
|
5340
|
+
server.registerTool(
|
|
5341
|
+
"report_agent_attention",
|
|
5342
|
+
{
|
|
5343
|
+
description: "Signal that the in-flight subagent needs human attention while the run is ACKNOWLEDGED. Sets requiresAttention on the agent run for orchestrator/UI visibility.",
|
|
5344
|
+
inputSchema: {
|
|
5345
|
+
agentRunId: z3.string().uuid().describe("Agent run UUID"),
|
|
5346
|
+
message: z3.string().min(1).describe("Why human attention is needed")
|
|
5347
|
+
}
|
|
5348
|
+
},
|
|
5349
|
+
async ({ agentRunId, message }) => runTool(async () => {
|
|
5350
|
+
const body = {
|
|
5351
|
+
requiresAttention: true,
|
|
5352
|
+
attentionMessage: message
|
|
5353
|
+
};
|
|
5354
|
+
return client.patch(import_agent_runs3.AGENT_RUNS_ROUTES.setAgentRunAttention(agentRunId), body);
|
|
5355
|
+
})
|
|
5356
|
+
);
|
|
5357
|
+
server.registerTool(
|
|
5358
|
+
"clear_agent_attention",
|
|
5359
|
+
{
|
|
5360
|
+
description: "Clear the human-attention flag after the blocker is resolved. Requires the run to still be ACKNOWLEDGED.",
|
|
5361
|
+
inputSchema: {
|
|
5362
|
+
agentRunId: z3.string().uuid().describe("Agent run UUID")
|
|
5363
|
+
}
|
|
5364
|
+
},
|
|
5365
|
+
async ({ agentRunId }) => runTool(async () => {
|
|
5366
|
+
const body = {
|
|
5367
|
+
requiresAttention: false
|
|
5368
|
+
};
|
|
5369
|
+
return client.patch(import_agent_runs3.AGENT_RUNS_ROUTES.setAgentRunAttention(agentRunId), body);
|
|
5370
|
+
})
|
|
5371
|
+
);
|
|
5372
|
+
server.registerTool(
|
|
5373
|
+
"complete_agent_run",
|
|
5374
|
+
{
|
|
5375
|
+
description: `Complete agent run: apply AGENTIC_SDLC workflow transition and optional work item patch. ${AGENTIC_SDLC_COLUMNS_SUMMARY} Outcomes: ${COMPLETE_AGENT_RUN_OUTCOME_SUMMARY}`,
|
|
5376
|
+
inputSchema: {
|
|
5377
|
+
agentRunId: z3.string().uuid().describe("Agent run UUID"),
|
|
5378
|
+
outcome: z3.enum(agentRunOutcomeValues).optional().describe(
|
|
5379
|
+
`Completion outcome (DEFAULT, SKIP_DESIGN, SKIP_DEV, HAS_BUGS, NEEDS_CLARIFICATION, FAILED). ${COMPLETE_AGENT_RUN_OUTCOME_SUMMARY}`
|
|
5380
|
+
),
|
|
5381
|
+
note: z3.string().nullable().optional().describe("Optional completion note"),
|
|
5382
|
+
workItemPatch: z3.object({
|
|
5383
|
+
description: z3.string().nullable().optional().describe("Updated work item description"),
|
|
5384
|
+
acceptanceCriteria: z3.string().nullable().optional().describe("Updated acceptance criteria"),
|
|
5385
|
+
designerRequired: z3.boolean().optional().describe("Architect only: whether DESIGNER phase runs before development")
|
|
5386
|
+
}).nullable().optional().describe("Optional work item field updates")
|
|
5387
|
+
}
|
|
5388
|
+
},
|
|
5389
|
+
async ({ agentRunId, outcome, note, workItemPatch }) => runTool(async () => {
|
|
5390
|
+
const body = {};
|
|
5391
|
+
if (outcome !== void 0) {
|
|
5392
|
+
body.outcome = outcome;
|
|
5393
|
+
}
|
|
5394
|
+
if (note !== void 0) {
|
|
5395
|
+
body.note = note;
|
|
5396
|
+
}
|
|
5397
|
+
if (workItemPatch !== void 0 && workItemPatch !== null) {
|
|
5398
|
+
body.workItemPatch = workItemPatch;
|
|
5399
|
+
}
|
|
5400
|
+
const hasBody = outcome !== void 0 || note !== void 0 || workItemPatch !== void 0 && workItemPatch !== null;
|
|
5401
|
+
const response = await client.post(
|
|
5402
|
+
import_agent_runs3.AGENT_RUNS_ROUTES.completeAgentRun(agentRunId),
|
|
5403
|
+
hasBody ? body : void 0
|
|
5404
|
+
);
|
|
5405
|
+
clearActiveRunAfterComplete(config);
|
|
5406
|
+
return response;
|
|
5407
|
+
})
|
|
4634
5408
|
);
|
|
4635
5409
|
}
|
|
4636
5410
|
|
|
4637
|
-
// src/tools/
|
|
4638
|
-
|
|
4639
|
-
var import_shared3 = __toESM(require_build2(), 1);
|
|
4640
|
-
import { existsSync as existsSync5 } from "node:fs";
|
|
4641
|
-
import { z as z3 } from "zod";
|
|
5411
|
+
// src/tools/orchestrator.ts
|
|
5412
|
+
import { z as z4 } from "zod";
|
|
4642
5413
|
|
|
4643
|
-
// src/
|
|
4644
|
-
|
|
4645
|
-
import {
|
|
4646
|
-
function detectMimeType(filePath) {
|
|
4647
|
-
const mimeType = lookupMimeType(basename2(filePath));
|
|
4648
|
-
return typeof mimeType === "string" ? mimeType : "application/octet-stream";
|
|
4649
|
-
}
|
|
5414
|
+
// src/workspace/resolve-project.ts
|
|
5415
|
+
var import_projects = __toESM(require_projects_api(), 1);
|
|
5416
|
+
import { basename as basename3 } from "node:path";
|
|
4650
5417
|
|
|
4651
|
-
// src/
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
var MAX_FILE_NAME_LENGTH = 200;
|
|
4655
|
-
function sanitizeFileName(name) {
|
|
4656
|
-
const baseName = name.split(/[/\\]/).pop() ?? name;
|
|
4657
|
-
const sanitized = baseName.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
4658
|
-
return sanitized.length > MAX_FILE_NAME_LENGTH ? sanitized.slice(0, MAX_FILE_NAME_LENGTH) : sanitized;
|
|
4659
|
-
}
|
|
4660
|
-
function buildStagingPath(workspaceRoot, workItemId, attachmentId, fileName) {
|
|
4661
|
-
const sanitizedFileName = sanitizeFileName(fileName);
|
|
4662
|
-
return resolve2(workspaceRoot, ".task-boards", "attachments", workItemId, `${attachmentId}_${sanitizedFileName}`);
|
|
4663
|
-
}
|
|
4664
|
-
function buildStagingDir(workspaceRoot, workItemId) {
|
|
4665
|
-
return resolve2(workspaceRoot, ".task-boards", "attachments", workItemId);
|
|
4666
|
-
}
|
|
4667
|
-
async function writeStagingFile(path, data) {
|
|
4668
|
-
const absolutePath = resolve2(path);
|
|
4669
|
-
await mkdir(dirname2(absolutePath), { recursive: true });
|
|
4670
|
-
await writeFile(absolutePath, data);
|
|
5418
|
+
// src/workspace/normalize-token.ts
|
|
5419
|
+
function normalizeToken(value) {
|
|
5420
|
+
return value.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
|
4671
5421
|
}
|
|
4672
5422
|
|
|
4673
|
-
// src/workspace/
|
|
4674
|
-
|
|
4675
|
-
import { basename as basename3, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "node:path";
|
|
4676
|
-
function confineFilePath(workspaceRoot, filePath) {
|
|
4677
|
-
const resolvedRoot = resolve3(workspaceRoot);
|
|
4678
|
-
const resolvedPath = isAbsolute2(filePath) ? resolve3(filePath) : resolve3(resolvedRoot, filePath);
|
|
4679
|
-
const relativePath = relative2(resolvedRoot, resolvedPath);
|
|
4680
|
-
if (relativePath.startsWith("..") || isAbsolute2(relativePath)) {
|
|
4681
|
-
throw new WorkspaceError(
|
|
4682
|
-
"FILE_OUT_OF_BOUNDS",
|
|
4683
|
-
`filePath is outside workspaceRoot: ${basename3(filePath) || filePath}`
|
|
4684
|
-
);
|
|
4685
|
-
}
|
|
4686
|
-
if (!existsSync4(resolvedPath)) {
|
|
4687
|
-
throw new WorkspaceError("FILE_NOT_FOUND", `filePath does not exist: ${basename3(filePath) || filePath}`);
|
|
4688
|
-
}
|
|
4689
|
-
const stats = statSync2(resolvedPath);
|
|
4690
|
-
if (!stats.isFile()) {
|
|
4691
|
-
throw new WorkspaceError("FILE_NOT_FOUND", `filePath is not a file: ${basename3(filePath) || filePath}`);
|
|
4692
|
-
}
|
|
4693
|
-
return resolvedPath;
|
|
4694
|
-
}
|
|
4695
|
-
function readConfinedWorkspaceFile(workspaceRoot, filePath) {
|
|
4696
|
-
const absolutePath = confineFilePath(workspaceRoot, filePath);
|
|
4697
|
-
const stats = statSync2(absolutePath);
|
|
4698
|
-
const buffer = readFileSync3(absolutePath);
|
|
5423
|
+
// src/workspace/auto-match-project.ts
|
|
5424
|
+
function toCandidate(project, matchedBy) {
|
|
4699
5425
|
return {
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
5426
|
+
projectId: project.id,
|
|
5427
|
+
name: project.name,
|
|
5428
|
+
key: project.key,
|
|
5429
|
+
presetCode: project.presetCode,
|
|
5430
|
+
matchedBy
|
|
4704
5431
|
};
|
|
4705
5432
|
}
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
async function fetchAttachments(client, workItemId) {
|
|
4711
|
-
return client.get(import_attachments.ATTACHMENTS_ROUTES.listAttachments(workItemId));
|
|
4712
|
-
}
|
|
4713
|
-
function filterAttachments(items, attachmentIds) {
|
|
4714
|
-
if (attachmentIds === void 0 || attachmentIds.length === 0) {
|
|
4715
|
-
return items;
|
|
5433
|
+
function autoMatchProject(folderToken, projects) {
|
|
5434
|
+
const normalizedFolder = normalizeToken(folderToken);
|
|
5435
|
+
if (normalizedFolder.length === 0) {
|
|
5436
|
+
return { kind: "none" };
|
|
4716
5437
|
}
|
|
4717
|
-
const
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
5438
|
+
const keyMatches = [];
|
|
5439
|
+
const nameMatches = [];
|
|
5440
|
+
for (const project of projects) {
|
|
5441
|
+
const input = {
|
|
5442
|
+
id: project.id,
|
|
5443
|
+
name: project.name,
|
|
5444
|
+
key: project.key,
|
|
5445
|
+
presetCode: project.presetCode
|
|
5446
|
+
};
|
|
5447
|
+
if (normalizeToken(project.key) === normalizedFolder) {
|
|
5448
|
+
keyMatches.push(input);
|
|
5449
|
+
}
|
|
5450
|
+
if (normalizeToken(project.name) === normalizedFolder) {
|
|
5451
|
+
nameMatches.push(input);
|
|
5452
|
+
}
|
|
4726
5453
|
}
|
|
4727
|
-
const
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
)
|
|
5454
|
+
const uniqueById = /* @__PURE__ */ new Map();
|
|
5455
|
+
for (const project of keyMatches) {
|
|
5456
|
+
uniqueById.set(project.id, { project, matchedBy: "key" });
|
|
5457
|
+
}
|
|
5458
|
+
for (const project of nameMatches) {
|
|
5459
|
+
if (!uniqueById.has(project.id)) {
|
|
5460
|
+
uniqueById.set(project.id, { project, matchedBy: "name" });
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
const matches = [...uniqueById.values()];
|
|
5464
|
+
if (matches.length === 1) {
|
|
5465
|
+
const match = matches[0];
|
|
5466
|
+
if (match === void 0) {
|
|
5467
|
+
return { kind: "none" };
|
|
5468
|
+
}
|
|
5469
|
+
return { kind: "single", project: match.project, matchedBy: match.matchedBy };
|
|
5470
|
+
}
|
|
5471
|
+
if (matches.length > 1) {
|
|
5472
|
+
return {
|
|
5473
|
+
kind: "candidates",
|
|
5474
|
+
candidates: matches.map((match) => toCandidate(match.project, match.matchedBy))
|
|
5475
|
+
};
|
|
4733
5476
|
}
|
|
5477
|
+
return { kind: "none" };
|
|
4734
5478
|
}
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
const
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
5479
|
+
|
|
5480
|
+
// src/workspace/parse-ide-rules.ts
|
|
5481
|
+
import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync4 } from "node:fs";
|
|
5482
|
+
import { join as join5 } from "node:path";
|
|
5483
|
+
var PROJECT_ID_LINE_PATTERN = /projectId\s*[:=]\s*["']?([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/gi;
|
|
5484
|
+
var TASK_BOARDS_UUID_PATTERN = /task-boards[^\n\r]*?([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/gi;
|
|
5485
|
+
function extractUuids(content) {
|
|
5486
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5487
|
+
for (const pattern of [PROJECT_ID_LINE_PATTERN, TASK_BOARDS_UUID_PATTERN]) {
|
|
5488
|
+
pattern.lastIndex = 0;
|
|
5489
|
+
let match = pattern.exec(content);
|
|
5490
|
+
while (match !== null) {
|
|
5491
|
+
const id = match[1];
|
|
5492
|
+
if (id !== void 0) {
|
|
5493
|
+
ids.add(id.toLowerCase());
|
|
4748
5494
|
}
|
|
5495
|
+
match = pattern.exec(content);
|
|
4749
5496
|
}
|
|
4750
5497
|
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
5498
|
+
return ids;
|
|
5499
|
+
}
|
|
5500
|
+
function parseIdeRules(workspaceRoot) {
|
|
5501
|
+
const rulesDir = join5(workspaceRoot, ".cursor", "rules");
|
|
5502
|
+
if (!existsSync6(rulesDir)) {
|
|
5503
|
+
return { status: "missing" };
|
|
5504
|
+
}
|
|
5505
|
+
const entries = readdirSync(rulesDir, { withFileTypes: true });
|
|
5506
|
+
const allIds = /* @__PURE__ */ new Set();
|
|
5507
|
+
for (const entry of entries) {
|
|
5508
|
+
if (!entry.isFile()) {
|
|
4754
5509
|
continue;
|
|
4755
5510
|
}
|
|
4756
|
-
|
|
4757
|
-
if (!overwrite && existsSync5(stagingPath)) {
|
|
4758
|
-
skipped.push({ attachmentId: attachment.id, reason: "file already exists (set overwrite=true to replace)" });
|
|
5511
|
+
if (!entry.name.endsWith(".mdc") && !entry.name.endsWith(".md")) {
|
|
4759
5512
|
continue;
|
|
4760
5513
|
}
|
|
4761
|
-
const
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
5514
|
+
const content = readFileSync4(join5(rulesDir, entry.name), "utf8");
|
|
5515
|
+
for (const id of extractUuids(content)) {
|
|
5516
|
+
allIds.add(id);
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
if (allIds.size === 0) {
|
|
5520
|
+
return { status: "missing" };
|
|
5521
|
+
}
|
|
5522
|
+
if (allIds.size === 1) {
|
|
5523
|
+
const projectId = [...allIds][0];
|
|
5524
|
+
if (projectId === void 0) {
|
|
5525
|
+
return { status: "missing" };
|
|
5526
|
+
}
|
|
5527
|
+
return { status: "found", projectId };
|
|
4769
5528
|
}
|
|
5529
|
+
return { status: "ambiguous" };
|
|
5530
|
+
}
|
|
5531
|
+
|
|
5532
|
+
// src/workspace/resolve-project.ts
|
|
5533
|
+
function emptyResponse(workspaceRoot) {
|
|
4770
5534
|
return {
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
5535
|
+
resolutionSource: "ambiguous",
|
|
5536
|
+
workspaceRoot,
|
|
5537
|
+
projectId: null,
|
|
5538
|
+
name: null,
|
|
5539
|
+
key: null,
|
|
5540
|
+
presetCode: null,
|
|
5541
|
+
hint: "No project binding found. Add .task-boards.yaml, an IDE rule (.cursor/rules) with projectId, or rename the folder to match a project key/name."
|
|
4775
5542
|
};
|
|
4776
5543
|
}
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
5544
|
+
function fromProject(workspaceRoot, resolutionSource, project) {
|
|
5545
|
+
return {
|
|
5546
|
+
resolutionSource,
|
|
5547
|
+
workspaceRoot,
|
|
5548
|
+
projectId: project.id,
|
|
5549
|
+
name: project.name,
|
|
5550
|
+
key: project.key,
|
|
5551
|
+
presetCode: project.presetCode
|
|
5552
|
+
};
|
|
5553
|
+
}
|
|
5554
|
+
async function enrichProject(client, workspaceRoot, resolutionSource, projectId) {
|
|
5555
|
+
const project = await client.get(import_projects.PROJECTS_ROUTES.getProject(projectId));
|
|
5556
|
+
return fromProject(workspaceRoot, resolutionSource, project);
|
|
5557
|
+
}
|
|
5558
|
+
async function resolveProject(client, options = {}) {
|
|
5559
|
+
const workspaceRoot = resolveWorkspaceRoot(options.workspaceRootOverride, options.envWorkspaceRoot);
|
|
5560
|
+
const yamlProjectId = parseTaskBoardsYaml(workspaceRoot);
|
|
5561
|
+
if (yamlProjectId !== null) {
|
|
5562
|
+
return enrichProject(client, workspaceRoot, "yaml", yamlProjectId);
|
|
4785
5563
|
}
|
|
4786
|
-
const
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
5564
|
+
const rulesResult = parseIdeRules(workspaceRoot);
|
|
5565
|
+
if (rulesResult.status === "ambiguous") {
|
|
5566
|
+
return {
|
|
5567
|
+
...emptyResponse(workspaceRoot),
|
|
5568
|
+
hint: "Multiple distinct projectId values found in .cursor/rules. Use .task-boards.yaml or set WORKSPACE_ROOT to a single-project workspace."
|
|
5569
|
+
};
|
|
5570
|
+
}
|
|
5571
|
+
if (rulesResult.status === "found") {
|
|
5572
|
+
return enrichProject(client, workspaceRoot, "rule", rulesResult.projectId);
|
|
5573
|
+
}
|
|
5574
|
+
const folderToken = basename3(workspaceRoot);
|
|
5575
|
+
const listResponse = await client.get(import_projects.PROJECTS_ROUTES.listProjects());
|
|
5576
|
+
const autoMatch = autoMatchProject(folderToken, listResponse.items);
|
|
5577
|
+
if (autoMatch.kind === "single") {
|
|
5578
|
+
return enrichProject(client, workspaceRoot, "auto_match", autoMatch.project.id);
|
|
5579
|
+
}
|
|
5580
|
+
if (autoMatch.kind === "candidates") {
|
|
5581
|
+
return {
|
|
5582
|
+
resolutionSource: "ambiguous",
|
|
5583
|
+
workspaceRoot,
|
|
5584
|
+
projectId: null,
|
|
5585
|
+
name: null,
|
|
5586
|
+
key: null,
|
|
5587
|
+
presetCode: null,
|
|
5588
|
+
candidates: autoMatch.candidates,
|
|
5589
|
+
hint: `Multiple projects match folder name "${folderToken}". Pick one candidate or add .task-boards.yaml.`
|
|
5590
|
+
};
|
|
5591
|
+
}
|
|
5592
|
+
return emptyResponse(workspaceRoot);
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
// src/tools/orchestrator.ts
|
|
5596
|
+
async function runOrchestratorOnce(client, options) {
|
|
5597
|
+
let projectId = options.projectId;
|
|
5598
|
+
let resolutionSource = "explicit";
|
|
5599
|
+
if (projectId === void 0) {
|
|
5600
|
+
const resolved = await resolveProject(client, {
|
|
5601
|
+
workspaceRootOverride: options.workspaceRootOverride,
|
|
5602
|
+
envWorkspaceRoot: options.envWorkspaceRoot
|
|
5603
|
+
});
|
|
5604
|
+
if (resolved.projectId === null) {
|
|
5605
|
+
const hint = resolved.hint ?? "Could not resolve project for workspace.";
|
|
5606
|
+
throw new WorkspaceError("PROJECT_NOT_FOUND", hint);
|
|
4794
5607
|
}
|
|
4795
|
-
|
|
5608
|
+
projectId = resolved.projectId;
|
|
5609
|
+
resolutionSource = resolved.resolutionSource;
|
|
5610
|
+
}
|
|
5611
|
+
const pollResult = await pollAgentRuns(client, {
|
|
5612
|
+
projectId,
|
|
5613
|
+
timeoutMs: options.timeoutMs,
|
|
5614
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
5615
|
+
limit: options.limit
|
|
5616
|
+
});
|
|
5617
|
+
const enriched = enrichPollResult(pollResult.items, pollResult.inflightRuns, pollResult.timedOut);
|
|
4796
5618
|
return {
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
5619
|
+
projectId,
|
|
5620
|
+
resolutionSource,
|
|
5621
|
+
items: enriched.items,
|
|
5622
|
+
inflightRuns: enriched.inflightRuns,
|
|
5623
|
+
timedOut: enriched.timedOut,
|
|
5624
|
+
orchestrator: enriched.orchestrator
|
|
4800
5625
|
};
|
|
4801
5626
|
}
|
|
4802
|
-
function
|
|
4803
|
-
server.registerTool(
|
|
4804
|
-
"list_work_item_attachments",
|
|
4805
|
-
{
|
|
4806
|
-
description: "List file attachments for a work item.",
|
|
4807
|
-
inputSchema: {
|
|
4808
|
-
workItemId: z3.string().uuid().describe("Work item UUID")
|
|
4809
|
-
}
|
|
4810
|
-
},
|
|
4811
|
-
async ({ workItemId }) => runTool(async () => fetchAttachments(client, workItemId))
|
|
4812
|
-
);
|
|
4813
|
-
server.registerTool(
|
|
4814
|
-
"download_work_item_attachments",
|
|
4815
|
-
{
|
|
4816
|
-
description: "Download work item attachments into the local workspace staging directory (.task-boards/attachments).",
|
|
4817
|
-
inputSchema: {
|
|
4818
|
-
workItemId: z3.string().uuid().describe("Work item UUID"),
|
|
4819
|
-
attachmentIds: z3.array(z3.string().uuid()).optional().describe("Optional subset of attachment UUIDs; downloads all when omitted"),
|
|
4820
|
-
overwrite: z3.boolean().optional().default(false).describe("Replace existing staged files when true"),
|
|
4821
|
-
workspaceRoot: z3.string().optional().describe("Optional absolute path to git repo root; defaults to WORKSPACE_ROOT or upward search from cwd")
|
|
4822
|
-
}
|
|
4823
|
-
},
|
|
4824
|
-
async ({ workItemId, attachmentIds, overwrite, workspaceRoot }) => {
|
|
4825
|
-
const resolvedWorkspaceRoot = resolveWorkspaceRoot(workspaceRoot, config.workspaceRoot);
|
|
4826
|
-
return runTool(
|
|
4827
|
-
async () => downloadWorkItemAttachments(client, {
|
|
4828
|
-
workItemId,
|
|
4829
|
-
workspaceRoot: resolvedWorkspaceRoot,
|
|
4830
|
-
attachmentIds,
|
|
4831
|
-
overwrite,
|
|
4832
|
-
blockSensitiveAttachments: config.blockSensitiveAttachments
|
|
4833
|
-
}),
|
|
4834
|
-
{ workspaceRoot: resolvedWorkspaceRoot }
|
|
4835
|
-
);
|
|
4836
|
-
}
|
|
4837
|
-
);
|
|
5627
|
+
function registerOrchestratorTools(server, client, config) {
|
|
4838
5628
|
server.registerTool(
|
|
4839
|
-
"
|
|
5629
|
+
"run_orchestrator_once",
|
|
4840
5630
|
{
|
|
4841
|
-
description: "
|
|
5631
|
+
description: "Resolve project (if needed), long-poll agent runs with atomic claim, and return orchestrator hints. Claims only STORY items assigned to \u0410\u0433\u0435\u043D\u0442 \u0418\u0418 (SERVICE user). Does not call sync_git_releases \u2014 the orchestrator agent decides when to sync.",
|
|
4842
5632
|
inputSchema: {
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
5633
|
+
projectId: z4.string().uuid().optional().describe("Project UUID; when omitted, resolves via .task-boards.yaml / .cursor/rules / folder name"),
|
|
5634
|
+
timeout: z4.number().int().min(1).max(300).default(120).describe("Maximum wait in seconds (1\u2013300, default 120)"),
|
|
5635
|
+
pollInterval: z4.number().int().min(1).max(60).default(2).describe("Seconds between polls (1\u201360, default 2); must not exceed timeout"),
|
|
5636
|
+
limit: z4.number().int().min(1).max(10).default(1).describe("Max runs to claim per poll (1\u201310, default 1)"),
|
|
5637
|
+
workspaceRoot: z4.string().optional().describe("Optional absolute workspace root; defaults to WORKSPACE_ROOT or upward search from cwd")
|
|
4846
5638
|
}
|
|
4847
5639
|
},
|
|
4848
|
-
async ({
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
);
|
|
4859
|
-
}
|
|
5640
|
+
async ({ projectId, timeout, pollInterval, limit, workspaceRoot }) => runTool(
|
|
5641
|
+
async () => runOrchestratorOnce(client, {
|
|
5642
|
+
projectId,
|
|
5643
|
+
timeoutMs: timeout * 1e3,
|
|
5644
|
+
pollIntervalMs: pollInterval * 1e3,
|
|
5645
|
+
limit,
|
|
5646
|
+
workspaceRootOverride: workspaceRoot,
|
|
5647
|
+
envWorkspaceRoot: config.workspaceRoot
|
|
5648
|
+
})
|
|
5649
|
+
)
|
|
4860
5650
|
);
|
|
4861
5651
|
}
|
|
4862
5652
|
|
|
4863
5653
|
// src/tools/board.ts
|
|
4864
5654
|
var import_boards = __toESM(require_boards_api(), 1);
|
|
4865
|
-
import { z as
|
|
5655
|
+
import { z as z5 } from "zod";
|
|
4866
5656
|
function registerBoardTools(server, client) {
|
|
4867
5657
|
server.registerTool(
|
|
4868
5658
|
"get_board",
|
|
4869
5659
|
{
|
|
4870
5660
|
description: "Get board projection for a project (columns and on-board work items).",
|
|
4871
5661
|
inputSchema: {
|
|
4872
|
-
projectId:
|
|
5662
|
+
projectId: z5.string().uuid().describe("Project UUID")
|
|
4873
5663
|
}
|
|
4874
5664
|
},
|
|
4875
5665
|
async ({ projectId }) => runTool(async () => client.get(import_boards.BOARDS_ROUTES.getBoard(projectId)))
|
|
4876
5666
|
);
|
|
4877
5667
|
}
|
|
4878
5668
|
|
|
4879
|
-
// src/tools/comments.ts
|
|
4880
|
-
var import_comments = __toESM(require_comments_api(), 1);
|
|
4881
|
-
import { z as z5 } from "zod";
|
|
4882
|
-
async function fetchComments(client, workItemId) {
|
|
4883
|
-
return client.get(import_comments.COMMENTS_ROUTES.listComments(workItemId));
|
|
4884
|
-
}
|
|
4885
|
-
async function postComment(client, workItemId, body) {
|
|
4886
|
-
const request = { body };
|
|
4887
|
-
return client.post(import_comments.COMMENTS_ROUTES.createComment(workItemId), request);
|
|
4888
|
-
}
|
|
4889
|
-
function registerCommentTools(server, client) {
|
|
4890
|
-
server.registerTool(
|
|
4891
|
-
"list_comments",
|
|
4892
|
-
{
|
|
4893
|
-
description: "List comments for a work item (newest last).",
|
|
4894
|
-
inputSchema: {
|
|
4895
|
-
workItemId: z5.string().uuid().describe("Work item UUID")
|
|
4896
|
-
}
|
|
4897
|
-
},
|
|
4898
|
-
async ({ workItemId }) => runTool(async () => fetchComments(client, workItemId))
|
|
4899
|
-
);
|
|
4900
|
-
server.registerTool(
|
|
4901
|
-
"create_comment",
|
|
4902
|
-
{
|
|
4903
|
-
description: "Create a comment on a work item. Use for orchestrator NEEDS_CLARIFICATION questions before complete_agent_run.",
|
|
4904
|
-
inputSchema: {
|
|
4905
|
-
workItemId: z5.string().uuid().describe("Work item UUID"),
|
|
4906
|
-
body: z5.string().min(1).describe("Comment body (markdown supported)")
|
|
4907
|
-
}
|
|
4908
|
-
},
|
|
4909
|
-
async ({ workItemId, body }) => runTool(async () => postComment(client, workItemId, body))
|
|
4910
|
-
);
|
|
4911
|
-
}
|
|
4912
|
-
|
|
4913
5669
|
// src/tools/labels.ts
|
|
4914
5670
|
var import_label_color = __toESM(require_label_color_enum(), 1);
|
|
4915
5671
|
var import_labels = __toESM(require_labels_api(), 1);
|
|
@@ -5306,44 +6062,6 @@ function registerSyncGitReleasesTools(server, client, config) {
|
|
|
5306
6062
|
|
|
5307
6063
|
// src/tools/sync-project-subagents.ts
|
|
5308
6064
|
import { z as z10 } from "zod";
|
|
5309
|
-
|
|
5310
|
-
// src/subagents/sync-project-subagents.ts
|
|
5311
|
-
var import_shared4 = __toESM(require_build2(), 1);
|
|
5312
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
5313
|
-
import { join as join4 } from "node:path";
|
|
5314
|
-
var SAFE_SUBAGENT_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
5315
|
-
var MAX_SUBAGENT_SLUG_LENGTH = 64;
|
|
5316
|
-
function isSyncableSlug(slug) {
|
|
5317
|
-
const trimmed = slug.trim();
|
|
5318
|
-
return trimmed.length > 0 && trimmed.length <= MAX_SUBAGENT_SLUG_LENGTH && SAFE_SUBAGENT_SLUG_PATTERN.test(trimmed);
|
|
5319
|
-
}
|
|
5320
|
-
function buildFileContent(subagent) {
|
|
5321
|
-
return (0, import_shared4.buildSubagentFileContentFromBody)({
|
|
5322
|
-
slug: subagent.slug,
|
|
5323
|
-
description: subagent.description,
|
|
5324
|
-
bodyMarkdown: subagent.bodyMarkdown
|
|
5325
|
-
});
|
|
5326
|
-
}
|
|
5327
|
-
async function syncProjectSubagents(client, params) {
|
|
5328
|
-
const { projectId, workspaceRoot } = params;
|
|
5329
|
-
const listResponse = await client.get(import_shared4.PROJECT_SUBAGENTS_ROUTES.listSubagents(projectId));
|
|
5330
|
-
const directory = join4(workspaceRoot, ".cursor", "agents");
|
|
5331
|
-
mkdirSync(directory, { recursive: true });
|
|
5332
|
-
const synced = [];
|
|
5333
|
-
const skipped = [];
|
|
5334
|
-
for (const subagent of listResponse.items) {
|
|
5335
|
-
if (!isSyncableSlug(subagent.slug)) {
|
|
5336
|
-
skipped.push(subagent.slug);
|
|
5337
|
-
continue;
|
|
5338
|
-
}
|
|
5339
|
-
const filePath = join4(directory, `${subagent.slug}.md`);
|
|
5340
|
-
writeFileSync(filePath, buildFileContent(subagent), "utf8");
|
|
5341
|
-
synced.push(subagent.slug);
|
|
5342
|
-
}
|
|
5343
|
-
return { synced, skipped, directory };
|
|
5344
|
-
}
|
|
5345
|
-
|
|
5346
|
-
// src/tools/sync-project-subagents.ts
|
|
5347
6065
|
function registerSyncProjectSubagentsTools(server, client, config) {
|
|
5348
6066
|
server.registerTool(
|
|
5349
6067
|
"sync_project_subagents",
|
|
@@ -5499,6 +6217,8 @@ function registerWorkItemTools(server, client) {
|
|
|
5499
6217
|
assigneeUserId: z11.string().uuid().nullable().optional().describe(
|
|
5500
6218
|
"Project member user id assignee; set to \xAB\u0410\u0433\u0435\u043D\u0442 \u0418\u0418\xBB (SERVICE user) for AI orchestrator to process the STORY. Changing assignee from SERVICE to a human cancels pending agent runs for that work item."
|
|
5501
6219
|
),
|
|
6220
|
+
codeChangesRequired: z11.boolean().optional().describe("STORY only: when false, ANALYST may complete with SKIP_DEV to release without code/git"),
|
|
6221
|
+
completed: z11.boolean().optional().describe("SUBTASK | EPIC: toggle completion (sets or clears releasedAt)"),
|
|
5502
6222
|
labelIds: z11.array(z11.string().uuid()).max(10).optional().describe("Replace work item labels (up to 10)")
|
|
5503
6223
|
}
|
|
5504
6224
|
},
|
|
@@ -5513,6 +6233,8 @@ function registerWorkItemTools(server, client) {
|
|
|
5513
6233
|
storyPoints,
|
|
5514
6234
|
estimate,
|
|
5515
6235
|
assigneeUserId,
|
|
6236
|
+
codeChangesRequired,
|
|
6237
|
+
completed,
|
|
5516
6238
|
labelIds
|
|
5517
6239
|
}) => runTool(async () => {
|
|
5518
6240
|
const current = await client.get(import_work_items.WORK_ITEMS_ROUTES.getWorkItem(workItemId));
|
|
@@ -5526,6 +6248,8 @@ function registerWorkItemTools(server, client) {
|
|
|
5526
6248
|
storyPoints,
|
|
5527
6249
|
estimate,
|
|
5528
6250
|
assigneeUserId,
|
|
6251
|
+
codeChangesRequired,
|
|
6252
|
+
completed,
|
|
5529
6253
|
labelIds,
|
|
5530
6254
|
version: current.version
|
|
5531
6255
|
};
|
|
@@ -5584,7 +6308,7 @@ function registerTools(server, client, config) {
|
|
|
5584
6308
|
registerLabelTools(server, client);
|
|
5585
6309
|
registerAttachmentTools(server, client, config);
|
|
5586
6310
|
registerBoardTools(server, client);
|
|
5587
|
-
registerAgentRunTools(server, client);
|
|
6311
|
+
registerAgentRunTools(server, client, config);
|
|
5588
6312
|
registerOrchestratorTools(server, client, config);
|
|
5589
6313
|
registerSyncGitReleasesTools(server, client, config);
|
|
5590
6314
|
registerSyncProjectSubagentsTools(server, client, config);
|
|
@@ -5607,7 +6331,17 @@ async function main() {
|
|
|
5607
6331
|
}
|
|
5608
6332
|
|
|
5609
6333
|
// src/cli.ts
|
|
5610
|
-
|
|
6334
|
+
async function main2() {
|
|
6335
|
+
const argv = process.argv.slice(2);
|
|
6336
|
+
if (isAttentionCliInvocation(argv)) {
|
|
6337
|
+
const config = loadConfig();
|
|
6338
|
+
const client = new RestClient(config.apiUrl, config.apiToken);
|
|
6339
|
+
const exitCode = await runAttentionCli(argv, { config, client });
|
|
6340
|
+
process.exit(exitCode);
|
|
6341
|
+
}
|
|
6342
|
+
await main();
|
|
6343
|
+
}
|
|
6344
|
+
main2().catch((error) => {
|
|
5611
6345
|
const message = error instanceof Error ? error.message : "Failed to start MCP server";
|
|
5612
6346
|
console.error(message);
|
|
5613
6347
|
process.exit(1);
|