@borgee/agents-host 0.2.35 → 0.2.44
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/agents-host.d.ts +2 -0
- package/dist/agents-host.js +111 -44
- package/dist/chat/sdk-chat-control-plane.js +3 -0
- package/dist/context/injection.d.ts +5 -3
- package/dist/context/injection.js +45 -29
- package/dist/context/prompt.js +23 -35
- package/dist/context/skill-manual.d.ts +13 -0
- package/dist/context/skill-manual.js +18 -0
- package/dist/context/turn-preparation.js +13 -4
- package/dist/gateway/localhost-gateway.js +16 -8
- package/dist/hosted-turn-content.d.ts +15 -0
- package/dist/hosted-turn-content.js +50 -0
- package/dist/managed-daemon.d.ts +3 -2
- package/dist/managed-daemon.js +77 -29
- package/dist/providers/claude/adapter.d.ts +3 -1
- package/dist/providers/claude/adapter.js +10 -0
- package/dist/providers/claude/cli-client.d.ts +11 -2
- package/dist/providers/claude/cli-client.js +98 -27
- package/dist/providers/codex/adapter.d.ts +3 -1
- package/dist/providers/codex/adapter.js +10 -0
- package/dist/providers/codex/cli-client.d.ts +10 -2
- package/dist/providers/codex/cli-client.js +85 -21
- package/dist/providers/codex/project-doc.js +13 -29
- package/dist/providers/copilot/adapter.d.ts +3 -1
- package/dist/providers/copilot/adapter.js +10 -0
- package/dist/providers/copilot/cli-client.d.ts +9 -1
- package/dist/providers/copilot/cli-client.js +71 -8
- package/dist/providers/create-provider.d.ts +1 -1
- package/dist/providers/create-provider.js +16 -5
- package/dist/providers/provider-adapter.d.ts +35 -0
- package/dist/providers/provider-adapter.js +44 -1
- package/dist/state-paths.d.ts +9 -1
- package/dist/state-paths.js +22 -3
- package/dist/types.d.ts +33 -2
- package/package.json +1 -1
- package/skills/borgee-agent/SKILL.md +119 -38
- package/skills/borgee-agent/references/errors.md +38 -0
- package/skills/borgee-agent/references/task-properties.md +30 -0
- package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
- package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
- package/skills/borgee-agent/borgee-agent.mjs +0 -562
- package/skills/borgee-agent/borgee-agent.py +0 -469
|
@@ -1,469 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import re
|
|
7
|
-
import sys
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from urllib.error import HTTPError
|
|
10
|
-
from urllib.parse import quote, urlencode
|
|
11
|
-
from urllib.request import Request, urlopen
|
|
12
|
-
|
|
13
|
-
TASK_THREAD_COLLECTION_COMMAND_ERROR = (
|
|
14
|
-
"Task assignment threads only support --get-task, --update-task, --set-property "
|
|
15
|
-
"and --delete-property. Create/list tasks belong to the parent channel."
|
|
16
|
-
)
|
|
17
|
-
TASK_THREAD_MISSING_TASK_ID_ERROR = (
|
|
18
|
-
"Task assignment thread could not resolve the current task from persisted context "
|
|
19
|
-
"or local fallback. Pass --task-id explicitly."
|
|
20
|
-
)
|
|
21
|
-
# Python ints are arbitrary precision, so nothing can be rounded on the way into the query
|
|
22
|
-
# string; the bound is here only so both packaged CLIs accept and reject exactly the same
|
|
23
|
-
# inputs. It is the Node CLI that needs it: there an out-of-range magnitude re-stringifies as
|
|
24
|
-
# `1e+21`, which the gateway's `Number.parseInt(value, 10)` reads back as `1`.
|
|
25
|
-
INTEGER_ARGUMENT_PATTERN = re.compile(r"[+-]?[0-9]+")
|
|
26
|
-
MAX_SAFE_INTEGER = 2**53 - 1
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def parse_int(flag: str, value: str | None) -> int:
|
|
30
|
-
if value is None:
|
|
31
|
-
raise ValueError(f"Missing value after {flag}")
|
|
32
|
-
if not INTEGER_ARGUMENT_PATTERN.fullmatch(value):
|
|
33
|
-
raise ValueError(f"Invalid integer for {flag}: {value}")
|
|
34
|
-
parsed = int(value)
|
|
35
|
-
if abs(parsed) > MAX_SAFE_INTEGER:
|
|
36
|
-
raise ValueError(f"Integer out of range for {flag}: {value}")
|
|
37
|
-
return parsed
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def parse_args(argv: list[str]) -> dict[str, object]:
|
|
41
|
-
context_path: Path | None = None
|
|
42
|
-
auth_path: Path | None = None
|
|
43
|
-
turn_execution_id: str | None = None
|
|
44
|
-
action = "print-bootstrap"
|
|
45
|
-
limit: int | None = None
|
|
46
|
-
before: int | None = None
|
|
47
|
-
after: int | None = None
|
|
48
|
-
body: str | None = None
|
|
49
|
-
reply_to_id: str | None = None
|
|
50
|
-
mention_target_id: str | None = None
|
|
51
|
-
mentions: list[str] = []
|
|
52
|
-
title: str | None = None
|
|
53
|
-
description: str | None = None
|
|
54
|
-
assignee_id: str | None = None
|
|
55
|
-
task_id: str | None = None
|
|
56
|
-
status: str | None = None
|
|
57
|
-
property_key: str | None = None
|
|
58
|
-
property_value: str | None = None
|
|
59
|
-
index = 0
|
|
60
|
-
|
|
61
|
-
def set_action(next_action: str) -> None:
|
|
62
|
-
nonlocal action
|
|
63
|
-
if action != "print-bootstrap":
|
|
64
|
-
raise ValueError("Only one action flag may be used per invocation")
|
|
65
|
-
action = next_action
|
|
66
|
-
|
|
67
|
-
while index < len(argv):
|
|
68
|
-
arg = argv[index]
|
|
69
|
-
if arg == "--context":
|
|
70
|
-
index += 1
|
|
71
|
-
if index >= len(argv):
|
|
72
|
-
raise ValueError("Missing path after --context")
|
|
73
|
-
context_path = Path(argv[index]).resolve()
|
|
74
|
-
elif arg == "--auth-path":
|
|
75
|
-
index += 1
|
|
76
|
-
if index >= len(argv):
|
|
77
|
-
raise ValueError("Missing path after --auth-path")
|
|
78
|
-
auth_path = Path(argv[index]).resolve()
|
|
79
|
-
elif arg == "--turn-execution-id":
|
|
80
|
-
index += 1
|
|
81
|
-
if index >= len(argv):
|
|
82
|
-
raise ValueError("Missing value after --turn-execution-id")
|
|
83
|
-
turn_execution_id = argv[index].strip()
|
|
84
|
-
elif arg == "--print-bootstrap":
|
|
85
|
-
action = "print-bootstrap"
|
|
86
|
-
elif arg == "--health":
|
|
87
|
-
set_action("health")
|
|
88
|
-
elif arg == "--read-bootstrap":
|
|
89
|
-
set_action("read-bootstrap")
|
|
90
|
-
elif arg == "--get-me":
|
|
91
|
-
set_action("get-me")
|
|
92
|
-
elif arg == "--read-history":
|
|
93
|
-
set_action("read-history")
|
|
94
|
-
elif arg == "--read-task-history":
|
|
95
|
-
set_action("read-task-history")
|
|
96
|
-
elif arg == "--read-draft":
|
|
97
|
-
set_action("read-draft")
|
|
98
|
-
elif arg == "--list-users":
|
|
99
|
-
set_action("list-users")
|
|
100
|
-
elif arg == "--send-message":
|
|
101
|
-
set_action("send-message")
|
|
102
|
-
elif arg == "--send-mention":
|
|
103
|
-
set_action("send-mention")
|
|
104
|
-
index += 1
|
|
105
|
-
if index >= len(argv):
|
|
106
|
-
raise ValueError("Missing value after --send-mention")
|
|
107
|
-
mention_target_id = argv[index].strip()
|
|
108
|
-
if not mention_target_id:
|
|
109
|
-
raise ValueError("Missing value after --send-mention")
|
|
110
|
-
elif arg == "--create-task":
|
|
111
|
-
set_action("create-task")
|
|
112
|
-
elif arg == "--list-tasks":
|
|
113
|
-
set_action("list-tasks")
|
|
114
|
-
elif arg == "--get-task":
|
|
115
|
-
set_action("get-task")
|
|
116
|
-
elif arg == "--update-task":
|
|
117
|
-
set_action("update-task")
|
|
118
|
-
elif arg == "--limit":
|
|
119
|
-
index += 1
|
|
120
|
-
limit = parse_int(arg, argv[index] if index < len(argv) else None)
|
|
121
|
-
elif arg == "--before":
|
|
122
|
-
index += 1
|
|
123
|
-
before = parse_int(arg, argv[index] if index < len(argv) else None)
|
|
124
|
-
elif arg == "--after":
|
|
125
|
-
index += 1
|
|
126
|
-
after = parse_int(arg, argv[index] if index < len(argv) else None)
|
|
127
|
-
elif arg == "--body":
|
|
128
|
-
index += 1
|
|
129
|
-
if index >= len(argv):
|
|
130
|
-
raise ValueError("Missing value after --body")
|
|
131
|
-
body = argv[index]
|
|
132
|
-
elif arg == "--reply-to":
|
|
133
|
-
index += 1
|
|
134
|
-
if index >= len(argv):
|
|
135
|
-
raise ValueError("Missing value after --reply-to")
|
|
136
|
-
reply_to_id = argv[index]
|
|
137
|
-
elif arg == "--mention":
|
|
138
|
-
index += 1
|
|
139
|
-
if index >= len(argv):
|
|
140
|
-
raise ValueError("Missing value after --mention")
|
|
141
|
-
mention = argv[index].strip()
|
|
142
|
-
if not mention:
|
|
143
|
-
raise ValueError("Missing value after --mention")
|
|
144
|
-
mentions.append(mention)
|
|
145
|
-
elif arg == "--title":
|
|
146
|
-
index += 1
|
|
147
|
-
title = argv[index] if index < len(argv) else None
|
|
148
|
-
elif arg == "--description":
|
|
149
|
-
index += 1
|
|
150
|
-
description = argv[index] if index < len(argv) else None
|
|
151
|
-
elif arg == "--assignee-id":
|
|
152
|
-
index += 1
|
|
153
|
-
assignee_id = argv[index] if index < len(argv) else None
|
|
154
|
-
elif arg == "--task-id":
|
|
155
|
-
index += 1
|
|
156
|
-
task_id = argv[index] if index < len(argv) else None
|
|
157
|
-
elif arg == "--status":
|
|
158
|
-
index += 1
|
|
159
|
-
status = argv[index] if index < len(argv) else None
|
|
160
|
-
elif arg == "--set-property":
|
|
161
|
-
set_action("set-property")
|
|
162
|
-
index += 1
|
|
163
|
-
# key=value in one token so the pair cannot be split across flags
|
|
164
|
-
# and land half-applied.
|
|
165
|
-
pair = argv[index] if index < len(argv) else None
|
|
166
|
-
if pair is None or "=" not in pair:
|
|
167
|
-
raise ValueError("--set-property requires <key>=<value>")
|
|
168
|
-
property_key, property_value = pair.split("=", 1)
|
|
169
|
-
if not property_key.strip():
|
|
170
|
-
raise ValueError("--set-property requires a non-empty key")
|
|
171
|
-
elif arg == "--delete-property":
|
|
172
|
-
set_action("delete-property")
|
|
173
|
-
index += 1
|
|
174
|
-
property_key = argv[index] if index < len(argv) else None
|
|
175
|
-
if property_key is None or not property_key.strip():
|
|
176
|
-
raise ValueError("--delete-property requires a key")
|
|
177
|
-
else:
|
|
178
|
-
raise ValueError(f"Unknown argument: {arg}")
|
|
179
|
-
index += 1
|
|
180
|
-
|
|
181
|
-
if context_path is None:
|
|
182
|
-
raise ValueError("Missing required --context <path> argument")
|
|
183
|
-
return {
|
|
184
|
-
"context_path": context_path,
|
|
185
|
-
"auth_path": auth_path,
|
|
186
|
-
"turn_execution_id": turn_execution_id,
|
|
187
|
-
"action": action,
|
|
188
|
-
"limit": limit,
|
|
189
|
-
"before": before,
|
|
190
|
-
"after": after,
|
|
191
|
-
"body": body,
|
|
192
|
-
"reply_to_id": reply_to_id,
|
|
193
|
-
"mention_target_id": mention_target_id,
|
|
194
|
-
"mentions": mentions,
|
|
195
|
-
"title": title,
|
|
196
|
-
"description": description,
|
|
197
|
-
"assignee_id": assignee_id,
|
|
198
|
-
"task_id": task_id,
|
|
199
|
-
"status": status,
|
|
200
|
-
"property_key": property_key.strip() if isinstance(property_key, str) else None,
|
|
201
|
-
"property_value": property_value,
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def ensure_visible_mention(body: str, participant_id: str) -> str:
|
|
206
|
-
token = f"<@{participant_id}>"
|
|
207
|
-
if token in body:
|
|
208
|
-
return body
|
|
209
|
-
separator = "" if body.endswith((" ", "\n", "\t")) else " "
|
|
210
|
-
return f"{body}{separator}{token}"
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
def ensure_visible_mentions(body: str, participant_ids: list[str]) -> str:
|
|
214
|
-
next_body = body
|
|
215
|
-
for participant_id in participant_ids:
|
|
216
|
-
next_body = ensure_visible_mention(next_body, participant_id)
|
|
217
|
-
return next_body
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
def explicit_task_id(options: dict[str, object]) -> str | None:
|
|
221
|
-
task_id = options.get("task_id")
|
|
222
|
-
trimmed = task_id.strip() if isinstance(task_id, str) else ""
|
|
223
|
-
return trimmed if trimmed else None
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def missing_task_id_error(action: str) -> ValueError:
|
|
227
|
-
return ValueError(f"Missing required --task-id <value> for --{action}")
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def require_explicit_task_id(action: str, options: dict[str, object]) -> str:
|
|
231
|
-
task_id = explicit_task_id(options)
|
|
232
|
-
if task_id is None:
|
|
233
|
-
raise missing_task_id_error(action)
|
|
234
|
-
return task_id
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def history_query_suffix(options: dict[str, object]) -> str:
|
|
238
|
-
query: dict[str, str] = {}
|
|
239
|
-
for key in ("limit", "before", "after"):
|
|
240
|
-
if options.get(key) is not None:
|
|
241
|
-
query[key] = str(options[key])
|
|
242
|
-
return f"?{urlencode(query)}" if query else ""
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def resolve_task_id(payload: dict[str, object], action: str, options: dict[str, object]) -> str | None:
|
|
246
|
-
task_id = explicit_task_id(options)
|
|
247
|
-
if task_id is not None:
|
|
248
|
-
return task_id
|
|
249
|
-
task_assignment_context = payload.get("taskAssignmentContext")
|
|
250
|
-
if isinstance(task_assignment_context, dict) and task_assignment_context.get("active") is True:
|
|
251
|
-
current_task_id = task_assignment_context.get("currentTaskId")
|
|
252
|
-
if isinstance(current_task_id, str) and current_task_id.strip():
|
|
253
|
-
return current_task_id.strip()
|
|
254
|
-
return None
|
|
255
|
-
raise missing_task_id_error(action)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def resolve_gateway_request(
|
|
259
|
-
payload: dict[str, object],
|
|
260
|
-
action: str,
|
|
261
|
-
options: dict[str, object],
|
|
262
|
-
) -> tuple[str, str, object | None, bool]:
|
|
263
|
-
gateway = payload.get("localhostGateway")
|
|
264
|
-
if not isinstance(gateway, dict):
|
|
265
|
-
raise ValueError("Missing localhostGateway bootstrap metadata in the context payload")
|
|
266
|
-
base_url = gateway.get("baseUrl")
|
|
267
|
-
if not isinstance(base_url, str):
|
|
268
|
-
raise ValueError("Missing localhostGateway bootstrap metadata in the context payload")
|
|
269
|
-
task_assignment_context = payload.get("taskAssignmentContext")
|
|
270
|
-
if action in ("create-task", "list-tasks") and isinstance(task_assignment_context, dict) and task_assignment_context.get("active") is True:
|
|
271
|
-
raise ValueError(TASK_THREAD_COLLECTION_COMMAND_ERROR)
|
|
272
|
-
channel_id = quote(str(payload.get("channelId", "")), safe="")
|
|
273
|
-
if action == "health":
|
|
274
|
-
return f"{base_url}/health", "GET", None, False
|
|
275
|
-
if action == "read-bootstrap":
|
|
276
|
-
return f"{base_url}/v1/channels/{channel_id}/bootstrap", "GET", None, False
|
|
277
|
-
if action == "get-me":
|
|
278
|
-
return f"{base_url}/v1/channels/{channel_id}/me", "GET", None, False
|
|
279
|
-
if action == "read-history":
|
|
280
|
-
return f"{base_url}/v1/channels/{channel_id}/history{history_query_suffix(options)}", "GET", None, False
|
|
281
|
-
if action == "read-task-history":
|
|
282
|
-
task_id = quote(require_explicit_task_id(action, options), safe="")
|
|
283
|
-
return f"{base_url}/v1/tasks/{task_id}/history{history_query_suffix(options)}", "GET", None, False
|
|
284
|
-
if action == "read-draft":
|
|
285
|
-
turn_execution_id = options.get("turn_execution_id")
|
|
286
|
-
if not isinstance(turn_execution_id, str) or not turn_execution_id.strip():
|
|
287
|
-
raise ValueError("Missing required --turn-execution-id value for --read-draft")
|
|
288
|
-
suffix = urlencode({"turnExecutionId": turn_execution_id.strip()})
|
|
289
|
-
return f"{base_url}/v1/channels/{channel_id}/draft?{suffix}", "GET", None, False
|
|
290
|
-
if action == "list-users":
|
|
291
|
-
return f"{base_url}/v1/channels/{channel_id}/users", "GET", None, False
|
|
292
|
-
if action in ("send-message", "send-mention"):
|
|
293
|
-
return f"{base_url}/v1/channels/{channel_id}/messages", "POST", None, False
|
|
294
|
-
if action == "create-task":
|
|
295
|
-
title = options.get("title")
|
|
296
|
-
if not isinstance(title, str) or title == "":
|
|
297
|
-
raise ValueError("Missing required --title <value> for --create-task")
|
|
298
|
-
request_body: dict[str, object] = {"title": title}
|
|
299
|
-
if options.get("description") is not None:
|
|
300
|
-
request_body["description"] = options["description"]
|
|
301
|
-
if options.get("assignee_id") is not None:
|
|
302
|
-
request_body["assigneeId"] = options["assignee_id"]
|
|
303
|
-
return f"{base_url}/v1/channels/{channel_id}/tasks", "POST", request_body, False
|
|
304
|
-
if action == "list-tasks":
|
|
305
|
-
return f"{base_url}/v1/channels/{channel_id}/tasks", "GET", None, False
|
|
306
|
-
if action == "get-task":
|
|
307
|
-
task_id = resolve_task_id(payload, action, options)
|
|
308
|
-
if isinstance(task_assignment_context, dict) and task_assignment_context.get("active") is True and explicit_task_id(options) is None:
|
|
309
|
-
return f"{base_url}/v1/channels/{channel_id}/current-task", "GET", None, True
|
|
310
|
-
return f"{base_url}/v1/tasks/{quote(str(task_id), safe='')}", "GET", None, False
|
|
311
|
-
if action == "update-task":
|
|
312
|
-
task_id = resolve_task_id(payload, action, options)
|
|
313
|
-
request_body: dict[str, object] = {}
|
|
314
|
-
if options.get("status") is not None:
|
|
315
|
-
request_body["status"] = options["status"]
|
|
316
|
-
if options.get("assignee_id") is not None:
|
|
317
|
-
request_body["assigneeId"] = options["assignee_id"]
|
|
318
|
-
if options.get("title") is not None:
|
|
319
|
-
request_body["title"] = options["title"]
|
|
320
|
-
if options.get("description") is not None:
|
|
321
|
-
request_body["description"] = options["description"]
|
|
322
|
-
if not request_body:
|
|
323
|
-
raise ValueError("At least one of --status, --assignee-id, --title, or --description is required for --update-task")
|
|
324
|
-
if isinstance(task_assignment_context, dict) and task_assignment_context.get("active") is True and explicit_task_id(options) is None:
|
|
325
|
-
return f"{base_url}/v1/channels/{channel_id}/current-task", "PATCH", request_body, True
|
|
326
|
-
return f"{base_url}/v1/tasks/{quote(str(task_id), safe='')}", "PATCH", request_body, False
|
|
327
|
-
if action in ("set-property", "delete-property"):
|
|
328
|
-
task_id = resolve_task_id(payload, action, options)
|
|
329
|
-
method = "PUT" if action == "set-property" else "DELETE"
|
|
330
|
-
request_body = {"value": options.get("property_value")} if action == "set-property" else None
|
|
331
|
-
encoded_key = quote(str(options.get("property_key") or ""), safe="")
|
|
332
|
-
if isinstance(task_assignment_context, dict) and task_assignment_context.get("active") is True and explicit_task_id(options) is None:
|
|
333
|
-
return f"{base_url}/v1/channels/{channel_id}/current-task/properties/{encoded_key}", method, request_body, True
|
|
334
|
-
return f"{base_url}/v1/tasks/{quote(str(task_id), safe='')}/properties/{encoded_key}", method, request_body, False
|
|
335
|
-
raise ValueError(f"Unsupported gateway action: {action}")
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
def read_gateway_auth(auth_path: Path | None, expected_channel_id: object) -> dict[str, str | None]:
|
|
339
|
-
if auth_path is None:
|
|
340
|
-
raise ValueError("Missing required --auth-path <path> argument for gateway access")
|
|
341
|
-
auth_payload = json.loads(auth_path.read_text(encoding="utf-8"))
|
|
342
|
-
token = None
|
|
343
|
-
localhost_gateway = auth_payload.get("localhostGateway")
|
|
344
|
-
if isinstance(localhost_gateway, dict):
|
|
345
|
-
raw_token = localhost_gateway.get("token")
|
|
346
|
-
if isinstance(raw_token, str):
|
|
347
|
-
token = raw_token
|
|
348
|
-
if auth_payload.get("channelId") != expected_channel_id or not token:
|
|
349
|
-
raise ValueError("Missing localhost gateway auth payload for this channel")
|
|
350
|
-
return {"token": token}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
def call_gateway(payload: dict[str, object], action: str, options: dict[str, object]) -> object:
|
|
354
|
-
url, method, request_body, uses_current_thread_fallback = resolve_gateway_request(payload, action, options)
|
|
355
|
-
auth = read_gateway_auth(
|
|
356
|
-
options.get("auth_path") if isinstance(options.get("auth_path"), Path) else None,
|
|
357
|
-
payload.get("channelId"),
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
data = None
|
|
361
|
-
headers = {"Authorization": f"Bearer {auth['token']}"}
|
|
362
|
-
if action in ("send-message", "send-mention"):
|
|
363
|
-
body = options.get("body")
|
|
364
|
-
if not isinstance(body, str) or not body.strip():
|
|
365
|
-
raise ValueError(f"Missing required --body value for --{action}")
|
|
366
|
-
turn_execution_id = options.get("turn_execution_id")
|
|
367
|
-
if not isinstance(turn_execution_id, str) or not turn_execution_id.strip():
|
|
368
|
-
raise ValueError(f"Missing required --turn-execution-id value for --{action}")
|
|
369
|
-
request_mentions = options.get("mentions")
|
|
370
|
-
if action == "send-mention":
|
|
371
|
-
mention_target_id = options.get("mention_target_id")
|
|
372
|
-
if not isinstance(mention_target_id, str) or not mention_target_id.strip():
|
|
373
|
-
raise ValueError("Missing required target user id after --send-mention")
|
|
374
|
-
extra_mentions = options.get("mentions")
|
|
375
|
-
if isinstance(extra_mentions, list) and len(extra_mentions) > 0:
|
|
376
|
-
raise ValueError("Do not combine --send-mention with additional --mention flags")
|
|
377
|
-
request_mentions = [mention_target_id.strip()]
|
|
378
|
-
if isinstance(request_mentions, list) and len(request_mentions) > 0:
|
|
379
|
-
body = ensure_visible_mentions(body, request_mentions)
|
|
380
|
-
headers["Content-Type"] = "application/json; charset=utf-8"
|
|
381
|
-
request_body = {
|
|
382
|
-
"body": body,
|
|
383
|
-
"turnExecutionId": turn_execution_id,
|
|
384
|
-
}
|
|
385
|
-
reply_to_id = options.get("reply_to_id")
|
|
386
|
-
if isinstance(reply_to_id, str) and reply_to_id:
|
|
387
|
-
request_body["replyToId"] = reply_to_id
|
|
388
|
-
data = json.dumps(request_body).encode("utf-8")
|
|
389
|
-
elif request_body is not None:
|
|
390
|
-
headers["Content-Type"] = "application/json; charset=utf-8"
|
|
391
|
-
data = json.dumps(request_body).encode("utf-8")
|
|
392
|
-
|
|
393
|
-
request = Request(url, headers=headers, method=method, data=data)
|
|
394
|
-
try:
|
|
395
|
-
with urlopen(request) as response:
|
|
396
|
-
response_body = response.read().decode("utf-8")
|
|
397
|
-
return json.loads(response_body) if response_body else None
|
|
398
|
-
except HTTPError as exc:
|
|
399
|
-
error_body = exc.read().decode("utf-8")
|
|
400
|
-
error_payload = json.loads(error_body) if error_body else None
|
|
401
|
-
if uses_current_thread_fallback and isinstance(error_payload, dict) and error_payload.get("error") == "not_found":
|
|
402
|
-
raise RuntimeError(TASK_THREAD_MISSING_TASK_ID_ERROR) from exc
|
|
403
|
-
reported_body = json.dumps(error_payload, separators=(",", ":"), ensure_ascii=False)
|
|
404
|
-
raise RuntimeError(f"Gateway request failed with {exc.code}: {reported_body}") from exc
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
def redact_bootstrap(payload: dict[str, object], context_path: Path) -> dict[str, object]:
|
|
408
|
-
localhost_gateway = payload.get("localhostGateway")
|
|
409
|
-
base_url = None
|
|
410
|
-
collaboration = None
|
|
411
|
-
if isinstance(localhost_gateway, dict):
|
|
412
|
-
raw_base_url = localhost_gateway.get("baseUrl")
|
|
413
|
-
if isinstance(raw_base_url, str):
|
|
414
|
-
base_url = raw_base_url
|
|
415
|
-
raw_collaboration = localhost_gateway.get("collaboration")
|
|
416
|
-
if isinstance(raw_collaboration, dict):
|
|
417
|
-
collaboration = raw_collaboration
|
|
418
|
-
return {
|
|
419
|
-
"kind": "borgee-agent-skill-bootstrap",
|
|
420
|
-
"contextPath": str(context_path),
|
|
421
|
-
"channelId": payload.get("channelId"),
|
|
422
|
-
"skillRuntime": payload.get("skillRuntime"),
|
|
423
|
-
"taskAssignmentContext": payload.get("taskAssignmentContext"),
|
|
424
|
-
"localhostGateway": (
|
|
425
|
-
{
|
|
426
|
-
"baseUrl": base_url,
|
|
427
|
-
**({"collaboration": collaboration} if collaboration is not None else {}),
|
|
428
|
-
}
|
|
429
|
-
if base_url
|
|
430
|
-
else None
|
|
431
|
-
),
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
parsed = parse_args(sys.argv[1:])
|
|
436
|
-
context_path = parsed["context_path"]
|
|
437
|
-
payload = json.loads(context_path.read_text(encoding="utf-8"))
|
|
438
|
-
|
|
439
|
-
if parsed["action"] == "print-bootstrap":
|
|
440
|
-
print(json.dumps(redact_bootstrap(payload, context_path), indent=2))
|
|
441
|
-
else:
|
|
442
|
-
print(
|
|
443
|
-
json.dumps(
|
|
444
|
-
call_gateway(
|
|
445
|
-
payload,
|
|
446
|
-
str(parsed["action"]),
|
|
447
|
-
{
|
|
448
|
-
"context_path": context_path,
|
|
449
|
-
"auth_path": parsed["auth_path"],
|
|
450
|
-
"turn_execution_id": parsed["turn_execution_id"],
|
|
451
|
-
"limit": parsed["limit"],
|
|
452
|
-
"before": parsed["before"],
|
|
453
|
-
"after": parsed["after"],
|
|
454
|
-
"body": parsed["body"],
|
|
455
|
-
"reply_to_id": parsed["reply_to_id"],
|
|
456
|
-
"mention_target_id": parsed["mention_target_id"],
|
|
457
|
-
"mentions": parsed["mentions"],
|
|
458
|
-
"title": parsed["title"],
|
|
459
|
-
"description": parsed["description"],
|
|
460
|
-
"assignee_id": parsed["assignee_id"],
|
|
461
|
-
"task_id": parsed["task_id"],
|
|
462
|
-
"status": parsed["status"],
|
|
463
|
-
"property_key": parsed["property_key"],
|
|
464
|
-
"property_value": parsed["property_value"],
|
|
465
|
-
},
|
|
466
|
-
),
|
|
467
|
-
indent=2,
|
|
468
|
-
)
|
|
469
|
-
)
|