@ouro.bot/cli 0.1.0-alpha.15 → 0.1.0-alpha.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/heart/config.js +29 -1
- package/dist/heart/core.js +64 -1
- package/dist/heart/daemon/daemon-cli.js +2 -1
- package/dist/heart/streaming.js +55 -1
- package/dist/mind/prompt.js +3 -3
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +134 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +61 -2
- package/dist/repertoire/coding/spawner.js +3 -3
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/tools-base.js +1 -1
- package/dist/repertoire/tools-teams.js +57 -4
- package/dist/repertoire/tools.js +40 -9
- package/dist/senses/bluebubbles-client.js +159 -5
- package/dist/senses/bluebubbles-media.js +244 -0
- package/dist/senses/bluebubbles.js +108 -19
- package/dist/senses/debug-activity.js +107 -0
- package/dist/senses/teams.js +173 -54
- package/package.json +1 -1
- package/subagents/work-doer.md +26 -24
- package/subagents/work-merger.md +24 -30
- package/subagents/work-planner.md +34 -25
|
@@ -61,6 +61,20 @@ const codingStatusTool = {
|
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
|
+
const codingTailTool = {
|
|
65
|
+
type: "function",
|
|
66
|
+
function: {
|
|
67
|
+
name: "coding_tail",
|
|
68
|
+
description: "show recent stdout/stderr tail for a coding session in a readable format",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
sessionId: { type: "string" },
|
|
73
|
+
},
|
|
74
|
+
required: ["sessionId"],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
64
78
|
const codingSendInputTool = {
|
|
65
79
|
type: "function",
|
|
66
80
|
function: {
|
|
@@ -93,7 +107,7 @@ const codingKillTool = {
|
|
|
93
107
|
exports.codingToolDefinitions = [
|
|
94
108
|
{
|
|
95
109
|
tool: codingSpawnTool,
|
|
96
|
-
handler: async (args) => {
|
|
110
|
+
handler: async (args, ctx) => {
|
|
97
111
|
emitCodingToolEvent("coding_spawn");
|
|
98
112
|
const rawRunner = requireArg(args, "runner");
|
|
99
113
|
if (!rawRunner)
|
|
@@ -122,7 +136,19 @@ exports.codingToolDefinitions = [
|
|
|
122
136
|
const stateFile = optionalArg(args, "stateFile");
|
|
123
137
|
if (stateFile)
|
|
124
138
|
request.stateFile = stateFile;
|
|
125
|
-
const
|
|
139
|
+
const manager = (0, index_1.getCodingSessionManager)();
|
|
140
|
+
const session = await manager.spawnSession(request);
|
|
141
|
+
if (args.runner === "codex" && args.taskRef) {
|
|
142
|
+
(0, runtime_1.emitNervesEvent)({
|
|
143
|
+
component: "repertoire",
|
|
144
|
+
event: "repertoire.coding_codex_spawned",
|
|
145
|
+
message: "spawned codex coding session",
|
|
146
|
+
meta: { sessionId: session.id, taskRef: args.taskRef },
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (ctx?.codingFeedback) {
|
|
150
|
+
(0, index_1.attachCodingSessionFeedback)(manager, session, ctx.codingFeedback);
|
|
151
|
+
}
|
|
126
152
|
return JSON.stringify(session);
|
|
127
153
|
},
|
|
128
154
|
},
|
|
@@ -141,6 +167,19 @@ exports.codingToolDefinitions = [
|
|
|
141
167
|
return JSON.stringify(session);
|
|
142
168
|
},
|
|
143
169
|
},
|
|
170
|
+
{
|
|
171
|
+
tool: codingTailTool,
|
|
172
|
+
handler: (args) => {
|
|
173
|
+
emitCodingToolEvent("coding_tail");
|
|
174
|
+
const sessionId = requireArg(args, "sessionId");
|
|
175
|
+
if (!sessionId)
|
|
176
|
+
return "sessionId is required";
|
|
177
|
+
const session = (0, index_1.getCodingSessionManager)().getSession(sessionId);
|
|
178
|
+
if (!session)
|
|
179
|
+
return `session not found: ${sessionId}`;
|
|
180
|
+
return (0, index_1.formatCodingTail)(session);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
144
183
|
{
|
|
145
184
|
tool: codingSendInputTool,
|
|
146
185
|
handler: (args) => {
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
"description": "Delete a work item (moves to recycle bin)",
|
|
36
36
|
"params": "destroy (boolean, permanently delete)"
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
"path": "/{project}/_apis/wit/workitemtypes",
|
|
40
|
+
"method": "GET",
|
|
41
|
+
"description": "List all work item types available in a project (Bug, Task, Epic, User Story, etc.)",
|
|
42
|
+
"params": ""
|
|
43
|
+
},
|
|
38
44
|
{
|
|
39
45
|
"path": "/_apis/git/repositories",
|
|
40
46
|
"method": "GET",
|
|
@@ -118,5 +124,187 @@
|
|
|
118
124
|
"method": "GET",
|
|
119
125
|
"description": "List saved work item queries (shared and personal)",
|
|
120
126
|
"params": "$depth, $expand"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"path": "/_apis/groupentitlements?api-version=7.1",
|
|
130
|
+
"method": "GET",
|
|
131
|
+
"host": "vsaex.dev.azure.com",
|
|
132
|
+
"description": "List group entitlements (group rules that auto-assign licenses). Use host vsaex.dev.azure.com.",
|
|
133
|
+
"params": ""
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"path": "/_apis/groupentitlements?api-version=7.1",
|
|
137
|
+
"method": "POST",
|
|
138
|
+
"host": "vsaex.dev.azure.com",
|
|
139
|
+
"description": "Create a group entitlement rule — maps an AAD group to an access level (e.g. Basic) and project membership. All members of the AAD group automatically get the specified license. Use host vsaex.dev.azure.com. This is the best way to bulk-provision users.",
|
|
140
|
+
"params": "body: { group: { origin: 'aad', originId: '<AAD-group-object-id>', subjectKind: 'group' }, licenseRule: { licensingSource: 'account', accountLicenseType: 'express', licenseDisplayName: 'Basic' }, projectEntitlements: [{ group: { groupType: 'projectContributor' }, projectRef: { id: '<project-id>' } }] }"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"path": "/_apis/groupentitlements/{groupId}?api-version=7.1",
|
|
144
|
+
"method": "GET",
|
|
145
|
+
"host": "vsaex.dev.azure.com",
|
|
146
|
+
"description": "Get a specific group entitlement by ID. Use host vsaex.dev.azure.com.",
|
|
147
|
+
"params": ""
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"path": "/_apis/groupentitlements/{groupId}?api-version=7.1",
|
|
151
|
+
"method": "PATCH",
|
|
152
|
+
"host": "vsaex.dev.azure.com",
|
|
153
|
+
"description": "Update a group entitlement (change license rule, project access). Use host vsaex.dev.azure.com.",
|
|
154
|
+
"params": "JSON Patch array: [{op, path, value}]"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"path": "/_apis/groupentitlements/{groupId}?api-version=7.1",
|
|
158
|
+
"method": "DELETE",
|
|
159
|
+
"host": "vsaex.dev.azure.com",
|
|
160
|
+
"description": "Delete a group entitlement rule. Use host vsaex.dev.azure.com.",
|
|
161
|
+
"params": ""
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements?api-version=7.1-preview.3",
|
|
165
|
+
"method": "GET",
|
|
166
|
+
"host": "vsapm.dev.azure.com",
|
|
167
|
+
"description": "List individual member entitlements (users and their access levels). Use host vsapm.dev.azure.com. For bulk provisioning, prefer the Group Entitlements API on vsaex.dev.azure.com instead.",
|
|
168
|
+
"params": "$top, $skip, $filter, $orderBy, $select"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements?api-version=7.1-preview.3",
|
|
172
|
+
"method": "POST",
|
|
173
|
+
"host": "vsapm.dev.azure.com",
|
|
174
|
+
"description": "Add a single member entitlement. Use host vsapm.dev.azure.com. For bulk provisioning, prefer the Group Entitlements API on vsaex.dev.azure.com instead.",
|
|
175
|
+
"params": "body: { accessLevel: { accountLicenseType: 'express'|'stakeholder', licensingSource: 'account' }, user: { principalName: 'user@domain.com', subjectKind: 'user' }, projectEntitlements: [{ group: { groupType: 'projectContributor' }, projectRef: { id: projectId } }] }"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements/{memberId}?api-version=7.1-preview.3",
|
|
179
|
+
"method": "PATCH",
|
|
180
|
+
"host": "vsapm.dev.azure.com",
|
|
181
|
+
"description": "Update a member entitlement (change access level, project access). Use host vsapm.dev.azure.com.",
|
|
182
|
+
"params": "JSON Patch array: [{op, path, value}]"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements/{memberId}?api-version=7.1-preview.3",
|
|
186
|
+
"method": "DELETE",
|
|
187
|
+
"host": "vsapm.dev.azure.com",
|
|
188
|
+
"description": "Remove a member entitlement (revoke user access). Use host vsapm.dev.azure.com.",
|
|
189
|
+
"params": ""
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"path": "/_apis/graph/users?api-version=7.1-preview.1",
|
|
193
|
+
"method": "GET",
|
|
194
|
+
"host": "vssps.dev.azure.com",
|
|
195
|
+
"description": "List users in the organization (Graph API). Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
196
|
+
"params": "subjectTypes (aad, msa, etc.), continuationToken"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"path": "/_apis/graph/groups?api-version=7.1-preview.1",
|
|
200
|
+
"method": "GET",
|
|
201
|
+
"host": "vssps.dev.azure.com",
|
|
202
|
+
"description": "List groups in the organization. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
203
|
+
"params": "subjectTypes, continuationToken"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"path": "/_apis/graph/memberships/{subjectDescriptor}?api-version=7.1-preview.1",
|
|
207
|
+
"method": "GET",
|
|
208
|
+
"host": "vssps.dev.azure.com",
|
|
209
|
+
"description": "List group memberships for a user or group. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
210
|
+
"params": "direction (up = groups user belongs to, down = members of group)"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"path": "/_apis/graph/memberships/{subjectDescriptor}/{containerDescriptor}?api-version=7.1-preview.1",
|
|
214
|
+
"method": "PUT",
|
|
215
|
+
"host": "vssps.dev.azure.com",
|
|
216
|
+
"description": "Add a user to a group. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
217
|
+
"params": ""
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"path": "/_apis/graph/memberships/{subjectDescriptor}/{containerDescriptor}?api-version=7.1-preview.1",
|
|
221
|
+
"method": "DELETE",
|
|
222
|
+
"host": "vssps.dev.azure.com",
|
|
223
|
+
"description": "Remove a user from a group. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
224
|
+
"params": ""
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"path": "/_apis/projects/{projectId}/teams",
|
|
228
|
+
"method": "GET",
|
|
229
|
+
"description": "List teams in a project",
|
|
230
|
+
"params": "$top, $skip"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"path": "/_apis/projects/{projectId}/teams/{teamId}",
|
|
234
|
+
"method": "GET",
|
|
235
|
+
"description": "Get a specific team by ID",
|
|
236
|
+
"params": ""
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"path": "/_apis/projects/{projectId}/teams",
|
|
240
|
+
"method": "POST",
|
|
241
|
+
"description": "Create a new team in a project",
|
|
242
|
+
"params": "name, description"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"path": "/_apis/projects/{projectId}/teams/{teamId}/members",
|
|
246
|
+
"method": "GET",
|
|
247
|
+
"description": "List members of a team",
|
|
248
|
+
"params": "$top, $skip"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"path": "/{project}/{team}/_apis/work/teamsettings/iterations",
|
|
252
|
+
"method": "GET",
|
|
253
|
+
"description": "List iterations (sprints) for a team",
|
|
254
|
+
"params": "$timeframe (current, past, future)"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"path": "/{project}/{team}/_apis/work/teamsettings/iterations",
|
|
258
|
+
"method": "POST",
|
|
259
|
+
"description": "Add an iteration to a team's sprint schedule",
|
|
260
|
+
"params": "id (iteration node ID)"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"path": "/{project}/{team}/_apis/work/teamsettings/iterations/{iterationId}",
|
|
264
|
+
"method": "DELETE",
|
|
265
|
+
"description": "Remove an iteration from a team's sprint schedule",
|
|
266
|
+
"params": ""
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"path": "/{project}/_apis/wit/classificationnodes/iterations",
|
|
270
|
+
"method": "GET",
|
|
271
|
+
"description": "List iteration path tree (project-level iteration nodes)",
|
|
272
|
+
"params": "$depth"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"path": "/{project}/_apis/wit/classificationnodes/iterations",
|
|
276
|
+
"method": "POST",
|
|
277
|
+
"description": "Create a new iteration node (sprint)",
|
|
278
|
+
"params": "name, attributes: { startDate, finishDate }"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"path": "/{project}/_apis/wit/classificationnodes/areas",
|
|
282
|
+
"method": "GET",
|
|
283
|
+
"description": "List area path tree (project-level area nodes)",
|
|
284
|
+
"params": "$depth"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"path": "/{project}/_apis/wit/classificationnodes/areas",
|
|
288
|
+
"method": "POST",
|
|
289
|
+
"description": "Create a new area path node",
|
|
290
|
+
"params": "name"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"path": "/{project}/_apis/wit/classificationnodes/{structureGroup}/{path}",
|
|
294
|
+
"method": "DELETE",
|
|
295
|
+
"description": "Delete a classification node (area or iteration). structureGroup is 'areas' or 'iterations'.",
|
|
296
|
+
"params": "$reclassifyId (move items to this node before deleting)"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"path": "/_apis/hooks/subscriptions",
|
|
300
|
+
"method": "GET",
|
|
301
|
+
"description": "List service hook subscriptions (webhooks for events)",
|
|
302
|
+
"params": ""
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"path": "/_apis/hooks/subscriptions",
|
|
306
|
+
"method": "POST",
|
|
307
|
+
"description": "Create a service hook subscription (webhook)",
|
|
308
|
+
"params": "publisherId, eventType, consumerId, consumerActionId, publisherInputs, consumerInputs"
|
|
121
309
|
}
|
|
122
310
|
]
|
|
@@ -235,7 +235,7 @@ exports.baseToolDefinitions = [
|
|
|
235
235
|
},
|
|
236
236
|
handler: (a) => {
|
|
237
237
|
try {
|
|
238
|
-
const result = (0, child_process_1.spawnSync)("claude", ["-p", "--dangerously-skip-permissions", "--add-dir", "."], {
|
|
238
|
+
const result = (0, child_process_1.spawnSync)("claude", ["-p", "--no-session-persistence", "--dangerously-skip-permissions", "--add-dir", "."], {
|
|
239
239
|
input: a.prompt,
|
|
240
240
|
encoding: "utf-8",
|
|
241
241
|
timeout: 60000,
|
|
@@ -82,7 +82,7 @@ exports.teamsToolDefinitions = [
|
|
|
82
82
|
type: "function",
|
|
83
83
|
function: {
|
|
84
84
|
name: "ado_query",
|
|
85
|
-
description: "GET or POST (for WIQL read queries) any Azure DevOps API endpoint. Use ado_docs first to look up the correct path.",
|
|
85
|
+
description: "GET or POST (for WIQL read queries) any Azure DevOps API endpoint. Use ado_docs first to look up the correct path and host.",
|
|
86
86
|
parameters: {
|
|
87
87
|
type: "object",
|
|
88
88
|
properties: {
|
|
@@ -90,6 +90,7 @@ exports.teamsToolDefinitions = [
|
|
|
90
90
|
path: { type: "string", description: "ADO API path after /{org}, e.g. /_apis/wit/wiql" },
|
|
91
91
|
method: { type: "string", enum: ["GET", "POST"], description: "HTTP method (defaults to GET)" },
|
|
92
92
|
body: { type: "string", description: "JSON request body (optional, used with POST for WIQL)" },
|
|
93
|
+
host: { type: "string", description: "API host override for non-standard APIs (e.g. 'vsapm.dev.azure.com' for entitlements, 'vssps.dev.azure.com' for users). Omit for standard dev.azure.com." },
|
|
93
94
|
},
|
|
94
95
|
required: ["organization", "path"],
|
|
95
96
|
},
|
|
@@ -100,7 +101,7 @@ exports.teamsToolDefinitions = [
|
|
|
100
101
|
return "AUTH_REQUIRED:ado -- I need access to Azure DevOps. Please sign in when prompted.";
|
|
101
102
|
}
|
|
102
103
|
const method = args.method || "GET";
|
|
103
|
-
const result = await (0, ado_client_1.adoRequest)(ctx.adoToken, method, args.organization, args.path, args.body);
|
|
104
|
+
const result = await (0, ado_client_1.adoRequest)(ctx.adoToken, method, args.organization, args.path, args.body, args.host);
|
|
104
105
|
checkAndRecord403(result, "ado", args.organization, method, ctx);
|
|
105
106
|
return result;
|
|
106
107
|
},
|
|
@@ -111,7 +112,7 @@ exports.teamsToolDefinitions = [
|
|
|
111
112
|
type: "function",
|
|
112
113
|
function: {
|
|
113
114
|
name: "ado_mutate",
|
|
114
|
-
description: "POST/PATCH/DELETE any Azure DevOps API endpoint for actual mutations. Use ado_docs first to look up the correct path.",
|
|
115
|
+
description: "POST/PATCH/DELETE any Azure DevOps API endpoint for actual mutations. Use ado_docs first to look up the correct path and host.",
|
|
115
116
|
parameters: {
|
|
116
117
|
type: "object",
|
|
117
118
|
properties: {
|
|
@@ -119,6 +120,7 @@ exports.teamsToolDefinitions = [
|
|
|
119
120
|
organization: { type: "string", description: "Azure DevOps organization name" },
|
|
120
121
|
path: { type: "string", description: "ADO API path after /{org}" },
|
|
121
122
|
body: { type: "string", description: "JSON request body (optional)" },
|
|
123
|
+
host: { type: "string", description: "API host override for non-standard APIs (e.g. 'vsapm.dev.azure.com' for entitlements, 'vssps.dev.azure.com' for users). Omit for standard dev.azure.com." },
|
|
122
124
|
},
|
|
123
125
|
required: ["method", "organization", "path"],
|
|
124
126
|
},
|
|
@@ -133,7 +135,7 @@ exports.teamsToolDefinitions = [
|
|
|
133
135
|
}
|
|
134
136
|
/* v8 ignore next -- fallback unreachable: method is validated against MUTATE_METHODS above @preserve */
|
|
135
137
|
const action = METHOD_TO_ACTION[args.method] || args.method;
|
|
136
|
-
const result = await (0, ado_client_1.adoRequest)(ctx.adoToken, args.method, args.organization, args.path, args.body);
|
|
138
|
+
const result = await (0, ado_client_1.adoRequest)(ctx.adoToken, args.method, args.organization, args.path, args.body, args.host);
|
|
137
139
|
checkAndRecord403(result, "ado", args.organization, action, ctx);
|
|
138
140
|
return result;
|
|
139
141
|
},
|
|
@@ -201,6 +203,53 @@ exports.teamsToolDefinitions = [
|
|
|
201
203
|
},
|
|
202
204
|
integration: "ado",
|
|
203
205
|
},
|
|
206
|
+
// -- Proactive messaging --
|
|
207
|
+
{
|
|
208
|
+
tool: {
|
|
209
|
+
type: "function",
|
|
210
|
+
function: {
|
|
211
|
+
name: "teams_send_message",
|
|
212
|
+
description: "send a proactive 1:1 Teams message to a user. requires their AAD object ID (use graph_query /users to find it). the message appears as coming from the bot.",
|
|
213
|
+
parameters: {
|
|
214
|
+
type: "object",
|
|
215
|
+
properties: {
|
|
216
|
+
user_id: { type: "string", description: "AAD object ID of the user to message" },
|
|
217
|
+
user_name: { type: "string", description: "display name of the user (for logging)" },
|
|
218
|
+
message: { type: "string", description: "message text to send" },
|
|
219
|
+
tenant_id: { type: "string", description: "tenant ID (optional, defaults to current conversation tenant)" },
|
|
220
|
+
},
|
|
221
|
+
required: ["user_id", "message"],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
/* v8 ignore start -- proactive messaging requires live Teams SDK conversation client @preserve */
|
|
226
|
+
handler: async (args, ctx) => {
|
|
227
|
+
if (!ctx?.botApi) {
|
|
228
|
+
return "proactive messaging is not available -- no bot API context (this tool only works in the Teams channel)";
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const tenantId = args.tenant_id || ctx.tenantId;
|
|
232
|
+
// Cast to the SDK's ConversationClient shape (kept as `unknown` in ToolContext to avoid type coupling)
|
|
233
|
+
const conversations = ctx.botApi.conversations;
|
|
234
|
+
const conversation = await conversations.create({
|
|
235
|
+
bot: { id: ctx.botApi.id },
|
|
236
|
+
members: [{ id: args.user_id, role: "user", name: args.user_name || args.user_id }],
|
|
237
|
+
tenantId,
|
|
238
|
+
isGroup: false,
|
|
239
|
+
});
|
|
240
|
+
await conversations.activities(conversation.id).create({
|
|
241
|
+
type: "message",
|
|
242
|
+
text: args.message,
|
|
243
|
+
});
|
|
244
|
+
return `message sent to ${args.user_name || args.user_id}`;
|
|
245
|
+
}
|
|
246
|
+
catch (e) {
|
|
247
|
+
return `failed to send proactive message: ${e instanceof Error ? e.message : String(e)}`;
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
/* v8 ignore stop */
|
|
251
|
+
confirmationRequired: true,
|
|
252
|
+
},
|
|
204
253
|
// -- Documentation tools --
|
|
205
254
|
{
|
|
206
255
|
tool: {
|
|
@@ -268,6 +317,8 @@ function searchEndpoints(entries, query) {
|
|
|
268
317
|
` ${e.description}`,
|
|
269
318
|
` Params: ${e.params || "none"}`,
|
|
270
319
|
];
|
|
320
|
+
if (e.host)
|
|
321
|
+
lines.push(` Host: ${e.host}`);
|
|
271
322
|
if (e.scopes)
|
|
272
323
|
lines.push(` Scopes: ${e.scopes}`);
|
|
273
324
|
return lines.join("\n");
|
|
@@ -304,5 +355,7 @@ function summarizeTeamsArgs(name, args) {
|
|
|
304
355
|
return summarizeKeyValues(["query"]);
|
|
305
356
|
if (name === "ado_docs")
|
|
306
357
|
return summarizeKeyValues(["query"]);
|
|
358
|
+
if (name === "teams_send_message")
|
|
359
|
+
return summarizeKeyValues(["user_name", "user_id"]);
|
|
307
360
|
return undefined;
|
|
308
361
|
}
|
package/dist/repertoire/tools.js
CHANGED
|
@@ -19,9 +19,29 @@ Object.defineProperty(exports, "teamsTools", { enumerable: true, get: function (
|
|
|
19
19
|
// All tool definitions in a single registry
|
|
20
20
|
const allDefinitions = [...tools_base_1.baseToolDefinitions, ...tools_teams_1.teamsToolDefinitions, ...ado_semantic_1.adoSemanticToolDefinitions, ...tools_github_1.githubToolDefinitions];
|
|
21
21
|
const REMOTE_BLOCKED_LOCAL_TOOLS = new Set(["shell", "read_file", "write_file", "git_commit", "gh_cli"]);
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
function isRemoteChannel(capabilities) {
|
|
23
|
+
return capabilities?.channel === "teams" || capabilities?.channel === "bluebubbles";
|
|
24
|
+
}
|
|
25
|
+
function isSharedRemoteContext(friend) {
|
|
26
|
+
const externalIds = friend.externalIds ?? [];
|
|
27
|
+
return externalIds.some((externalId) => externalId.externalId.startsWith("group:") || externalId.provider === "teams-conversation");
|
|
28
|
+
}
|
|
29
|
+
function isTrustedRemoteContext(context) {
|
|
30
|
+
if (!context?.friend || !isRemoteChannel(context.channel))
|
|
31
|
+
return false;
|
|
32
|
+
const trustLevel = context.friend.trustLevel ?? "stranger";
|
|
33
|
+
return trustLevel !== "stranger" && !isSharedRemoteContext(context.friend);
|
|
34
|
+
}
|
|
35
|
+
function shouldBlockLocalTools(capabilities, context) {
|
|
36
|
+
if (!isRemoteChannel(capabilities))
|
|
37
|
+
return false;
|
|
38
|
+
return !isTrustedRemoteContext(context);
|
|
39
|
+
}
|
|
40
|
+
function blockedLocalToolMessage() {
|
|
41
|
+
return "I can't do that from here because I'm talking to multiple people in a shared remote channel, and local shell/file/git/gh operations could let conversations interfere with each other. Ask me for a remote-safe alternative (Graph/ADO/web), or run that operation from CLI.";
|
|
42
|
+
}
|
|
43
|
+
function baseToolsForCapabilities(capabilities, context) {
|
|
44
|
+
if (!shouldBlockLocalTools(capabilities, context))
|
|
25
45
|
return tools_base_1.tools;
|
|
26
46
|
return tools_base_1.tools.filter((tool) => !REMOTE_BLOCKED_LOCAL_TOOLS.has(tool.function.name));
|
|
27
47
|
}
|
|
@@ -39,13 +59,15 @@ function applyPreference(tool, pref) {
|
|
|
39
59
|
// Base tools (no integration) are always included.
|
|
40
60
|
// Teams/integration tools are included only if their integration is in availableIntegrations.
|
|
41
61
|
// When toolPreferences is provided, matching preferences are appended to tool descriptions.
|
|
42
|
-
function getToolsForChannel(capabilities, toolPreferences) {
|
|
43
|
-
const baseTools = baseToolsForCapabilities(capabilities);
|
|
62
|
+
function getToolsForChannel(capabilities, toolPreferences, context) {
|
|
63
|
+
const baseTools = baseToolsForCapabilities(capabilities, context);
|
|
44
64
|
if (!capabilities || capabilities.availableIntegrations.length === 0) {
|
|
45
65
|
return baseTools;
|
|
46
66
|
}
|
|
47
67
|
const available = new Set(capabilities.availableIntegrations);
|
|
48
|
-
const
|
|
68
|
+
const channelDefs = [...tools_teams_1.teamsToolDefinitions, ...ado_semantic_1.adoSemanticToolDefinitions, ...tools_github_1.githubToolDefinitions];
|
|
69
|
+
// Include tools whose integration is available, plus channel tools with no integration gate (e.g. teams_send_message)
|
|
70
|
+
const integrationDefs = channelDefs.filter((d) => d.integration ? available.has(d.integration) : capabilities.channel === "teams");
|
|
49
71
|
if (!toolPreferences || Object.keys(toolPreferences).length === 0) {
|
|
50
72
|
return [...baseTools, ...integrationDefs.map((d) => d.tool)];
|
|
51
73
|
}
|
|
@@ -87,9 +109,8 @@ async function execTool(name, args, ctx) {
|
|
|
87
109
|
});
|
|
88
110
|
return `unknown: ${name}`;
|
|
89
111
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const message = "I can't do that from here because I'm talking to multiple people in a shared remote channel, and local shell/file/git/gh operations could let conversations interfere with each other. Ask me for a remote-safe alternative (Graph/ADO/web), or run that operation from CLI.";
|
|
112
|
+
if (shouldBlockLocalTools(ctx?.context?.channel, ctx?.context) && REMOTE_BLOCKED_LOCAL_TOOLS.has(name)) {
|
|
113
|
+
const message = blockedLocalToolMessage();
|
|
93
114
|
(0, runtime_1.emitNervesEvent)({
|
|
94
115
|
level: "warn",
|
|
95
116
|
event: "tool.error",
|
|
@@ -176,6 +197,8 @@ function summarizeArgs(name, args) {
|
|
|
176
197
|
return summarizeKeyValues(args, ["runner", "workdir", "taskRef"]);
|
|
177
198
|
if (name === "coding_status")
|
|
178
199
|
return summarizeKeyValues(args, ["sessionId"]);
|
|
200
|
+
if (name === "coding_tail")
|
|
201
|
+
return summarizeKeyValues(args, ["sessionId"]);
|
|
179
202
|
if (name === "coding_send_input")
|
|
180
203
|
return summarizeKeyValues(args, ["sessionId", "input"]);
|
|
181
204
|
if (name === "coding_kill")
|
|
@@ -195,5 +218,13 @@ function summarizeArgs(name, args) {
|
|
|
195
218
|
}
|
|
196
219
|
if (name === "ado_backlog_list")
|
|
197
220
|
return summarizeKeyValues(args, ["organization", "project"]);
|
|
221
|
+
if (name === "ado_batch_update")
|
|
222
|
+
return summarizeKeyValues(args, ["organization", "project"]);
|
|
223
|
+
if (name === "ado_create_epic" || name === "ado_create_issue")
|
|
224
|
+
return summarizeKeyValues(args, ["organization", "project", "title"]);
|
|
225
|
+
if (name === "ado_move_items")
|
|
226
|
+
return summarizeKeyValues(args, ["organization", "project", "workItemIds"]);
|
|
227
|
+
if (name === "ado_restructure_backlog")
|
|
228
|
+
return summarizeKeyValues(args, ["organization", "project"]);
|
|
198
229
|
return summarizeUnknownArgs(args);
|
|
199
230
|
}
|