teamcopilot 0.0.3 → 0.1.1
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/frontend/assets/{cssMode-BBJnYJ3k.js → cssMode-DPbQk08M.js} +1 -1
- package/dist/frontend/assets/{freemarker2-D_OsaDj3.js → freemarker2-DFE4Z2hp.js} +1 -1
- package/dist/frontend/assets/{handlebars-OZHda73C.js → handlebars-Dw_PK21Y.js} +1 -1
- package/dist/frontend/assets/{html-CdGHj-sb.js → html-DxZRNRpl.js} +1 -1
- package/dist/frontend/assets/{htmlMode-DmOnHDtb.js → htmlMode-iGgmnmtJ.js} +1 -1
- package/dist/frontend/assets/{index-DNOGp3R4.js → index-CphuwuEr.js} +4 -4
- package/dist/frontend/assets/index-Ds8n3J4W.css +1 -0
- package/dist/frontend/assets/{javascript--es-Ez5N.js → javascript-Bw_u6aiq.js} +1 -1
- package/dist/frontend/assets/{jsonMode-C3d51Wej.js → jsonMode-iBiD9w9m.js} +1 -1
- package/dist/frontend/assets/{liquid-D2JYL9PO.js → liquid-nnkE2sbw.js} +1 -1
- package/dist/frontend/assets/{mdx-Ch04DugU.js → mdx-CHi7ETpD.js} +1 -1
- package/dist/frontend/assets/{python-D-iNySlZ.js → python--OTnmJv2.js} +1 -1
- package/dist/frontend/assets/{razor-Bq45_Fpy.js → razor-C6OgnQD9.js} +1 -1
- package/dist/frontend/assets/{tsMode-CC8kGhhz.js → tsMode-DzsKqqZD.js} +1 -1
- package/dist/frontend/assets/{typescript-CtgyIznM.js → typescript-BMZ0H3W7.js} +1 -1
- package/dist/frontend/assets/{xml-D5joow3e.js → xml-HTDEz_LP.js} +1 -1
- package/dist/frontend/assets/{yaml-BsRI11pd.js → yaml-CVa5ypgE.js} +1 -1
- package/dist/frontend/index.html +2 -2
- package/dist/opencode-server.js +3 -0
- package/dist/utils/approval-snapshot-common.js +29 -16
- package/dist/utils/resource-files.js +22 -21
- package/dist/utils/skill.js +39 -9
- package/dist/utils/workspace-sync.js +5 -7
- package/dist/workspace_files/.opencode/opencode.json +4 -1
- package/dist/workspace_files/.opencode/package.json +2 -1
- package/dist/workspace_files/.opencode/plugins/createSkill.ts +38 -7
- package/dist/workspace_files/.opencode/plugins/createWorkflow.ts +34 -3
- package/dist/workspace_files/.opencode/plugins/findSimilarWorkflow.ts +33 -2
- package/dist/workspace_files/.opencode/plugins/findSkill.ts +36 -5
- package/dist/workspace_files/.opencode/plugins/getSkillContent.ts +34 -3
- package/dist/workspace_files/.opencode/plugins/honeytoken-protection.ts +1 -1
- package/dist/workspace_files/.opencode/plugins/listAvailableSkills.ts +33 -2
- package/dist/workspace_files/.opencode/plugins/listAvailableWorkflows.ts +33 -2
- package/dist/workspace_files/.opencode/plugins/runWorkflow.ts +34 -3
- package/dist/workspace_files/.opencode/plugins/skill-command-guard.ts +138 -0
- package/dist/workspace_files/AGENTS.md +3 -3
- package/dist/workspace_files/package-lock.json +2 -1
- package/dist/workspace_files/package.json +3 -1
- package/package.json +1 -1
- package/dist/frontend/assets/index-CnBE4Efj.css +0 -1
|
@@ -11,8 +11,22 @@ const redact_1 = require("./redact");
|
|
|
11
11
|
function createResourceFileManager(options) {
|
|
12
12
|
const { getResourcePath, resourceLabel, editorLabel } = options;
|
|
13
13
|
const rootLabel = `${resourceLabel} root`;
|
|
14
|
-
const symlinkRootError = `${editorLabel} editor does not support symlinked ${resourceLabel} directories`;
|
|
15
14
|
const symlinkError = `${editorLabel} editor does not support symlinks`;
|
|
15
|
+
function getResolvedRootPaths(slug) {
|
|
16
|
+
const configuredRoot = getResourcePath(slug);
|
|
17
|
+
try {
|
|
18
|
+
return {
|
|
19
|
+
configuredRoot,
|
|
20
|
+
realRoot: fs_1.default.realpathSync(configuredRoot),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw {
|
|
25
|
+
status: 404,
|
|
26
|
+
message: `${rootLabel} not found`
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
16
30
|
function toEtag(buffer) {
|
|
17
31
|
return crypto_1.default.createHash("sha256").update(buffer).digest("hex");
|
|
18
32
|
}
|
|
@@ -64,21 +78,8 @@ function createResourceFileManager(options) {
|
|
|
64
78
|
}
|
|
65
79
|
return absolute;
|
|
66
80
|
}
|
|
67
|
-
function assertRootNotSymlink(slug) {
|
|
68
|
-
const resourceRoot = getResourcePath(slug);
|
|
69
|
-
if (!fs_1.default.existsSync(resourceRoot)) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const rootLstat = fs_1.default.lstatSync(resourceRoot);
|
|
73
|
-
if (rootLstat.isSymbolicLink()) {
|
|
74
|
-
throw {
|
|
75
|
-
status: 400,
|
|
76
|
-
message: symlinkRootError
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
81
|
function assertRealPathWithinRoot(slug, absolutePath) {
|
|
81
|
-
const realRoot =
|
|
82
|
+
const { realRoot } = getResolvedRootPaths(slug);
|
|
82
83
|
const realTarget = fs_1.default.realpathSync(absolutePath);
|
|
83
84
|
if (realTarget !== realRoot && !realTarget.startsWith(`${realRoot}${path_1.default.sep}`)) {
|
|
84
85
|
throw {
|
|
@@ -88,9 +89,14 @@ function createResourceFileManager(options) {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
function assertNoSymlinkInAncestors(slug, absolutePath) {
|
|
91
|
-
const
|
|
92
|
+
const { configuredRoot: rawConfiguredRoot, realRoot: rawRealRoot } = getResolvedRootPaths(slug);
|
|
93
|
+
const configuredRoot = path_1.default.resolve(rawConfiguredRoot);
|
|
94
|
+
const realRoot = path_1.default.resolve(rawRealRoot);
|
|
92
95
|
let current = path_1.default.resolve(absolutePath);
|
|
93
96
|
while (true) {
|
|
97
|
+
if (current === configuredRoot || current === realRoot) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
94
100
|
const lstat = fs_1.default.lstatSync(current);
|
|
95
101
|
if (lstat.isSymbolicLink()) {
|
|
96
102
|
throw {
|
|
@@ -98,9 +104,6 @@ function createResourceFileManager(options) {
|
|
|
98
104
|
message: symlinkError
|
|
99
105
|
};
|
|
100
106
|
}
|
|
101
|
-
if (current === root) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
107
|
const parent = path_1.default.dirname(current);
|
|
105
108
|
if (parent === current) {
|
|
106
109
|
throw {
|
|
@@ -112,12 +115,10 @@ function createResourceFileManager(options) {
|
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
function assertExistingPathIsSafe(slug, absolutePath) {
|
|
115
|
-
assertRootNotSymlink(slug);
|
|
116
118
|
assertRealPathWithinRoot(slug, absolutePath);
|
|
117
119
|
assertNoSymlinkInAncestors(slug, absolutePath);
|
|
118
120
|
}
|
|
119
121
|
function assertParentDirectoryIsSafeForCreate(slug, parentAbsolutePath) {
|
|
120
|
-
assertRootNotSymlink(slug);
|
|
121
122
|
assertRealPathWithinRoot(slug, parentAbsolutePath);
|
|
122
123
|
assertNoSymlinkInAncestors(slug, parentAbsolutePath);
|
|
123
124
|
}
|
package/dist/utils/skill.js
CHANGED
|
@@ -15,7 +15,7 @@ const client_1 = __importDefault(require("../prisma/client"));
|
|
|
15
15
|
const workspace_sync_1 = require("./workspace-sync");
|
|
16
16
|
const permission_common_1 = require("./permission-common");
|
|
17
17
|
function getSkillsRootPath() {
|
|
18
|
-
return path_1.default.join((0, workspace_sync_1.getWorkspaceDirFromEnv)(), "
|
|
18
|
+
return path_1.default.join((0, workspace_sync_1.getWorkspaceDirFromEnv)(), ".agents", "skills");
|
|
19
19
|
}
|
|
20
20
|
function getSkillPath(slug) {
|
|
21
21
|
return path_1.default.join(getSkillsRootPath(), slug);
|
|
@@ -66,9 +66,19 @@ function listSkillSlugs() {
|
|
|
66
66
|
const entries = fs_1.default.readdirSync(skillsDir, { withFileTypes: true });
|
|
67
67
|
const slugs = [];
|
|
68
68
|
for (const entry of entries) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
const skillEntryPath = path_1.default.join(skillsDir, entry.name);
|
|
70
|
+
if (!entry.isDirectory()) {
|
|
71
|
+
if (!entry.isSymbolicLink())
|
|
72
|
+
continue;
|
|
73
|
+
try {
|
|
74
|
+
if (!fs_1.default.statSync(skillEntryPath).isDirectory())
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const canonicalManifestPath = path_1.default.join(skillEntryPath, "SKILL.md");
|
|
72
82
|
if (fs_1.default.existsSync(canonicalManifestPath)) {
|
|
73
83
|
slugs.push(entry.name);
|
|
74
84
|
}
|
|
@@ -76,12 +86,32 @@ function listSkillSlugs() {
|
|
|
76
86
|
return slugs;
|
|
77
87
|
}
|
|
78
88
|
function extractFrontmatterValue(frontmatter, key) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
const lines = frontmatter.split("\n");
|
|
90
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
91
|
+
const line = lines[index];
|
|
92
|
+
const match = line.match(new RegExp(`^${key}\\s*:\\s*(.*)$`));
|
|
93
|
+
if (!match) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const rawValue = match[1]?.trim() ?? "";
|
|
97
|
+
if (rawValue === "|" || rawValue === ">") {
|
|
98
|
+
const blockLines = [];
|
|
99
|
+
for (let blockIndex = index + 1; blockIndex < lines.length; blockIndex += 1) {
|
|
100
|
+
const blockLine = lines[blockIndex] ?? "";
|
|
101
|
+
if (blockLine.length === 0) {
|
|
102
|
+
blockLines.push("");
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (!/^\s+/.test(blockLine)) {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
blockLines.push(blockLine.replace(/^\s+/, ""));
|
|
109
|
+
}
|
|
110
|
+
return blockLines.join(rawValue === ">" ? " " : "\n").trim();
|
|
111
|
+
}
|
|
112
|
+
return rawValue.replace(/^["']|["']$/g, "");
|
|
83
113
|
}
|
|
84
|
-
return
|
|
114
|
+
return null;
|
|
85
115
|
}
|
|
86
116
|
function readSkillManifest(slug) {
|
|
87
117
|
const skillManifestPath = getSkillManifestPath(slug);
|
|
@@ -49,13 +49,11 @@ function ensureWorkspaceDatabaseDirectory() {
|
|
|
49
49
|
function normalizeRelativePath(relativePath) {
|
|
50
50
|
return relativePath.split(path_1.default.sep).join("/");
|
|
51
51
|
}
|
|
52
|
-
const MANAGED_WORKSPACE_DIRECTORIES = new Set([
|
|
53
|
-
"workflows",
|
|
54
|
-
"custom-skills",
|
|
55
|
-
]);
|
|
56
52
|
function shouldSkipManagedDirectoryContent(relativePath) {
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
if (relativePath === "workflows" || relativePath.startsWith("workflows/")) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (relativePath === ".agents/skills" || relativePath.startsWith(".agents/skills/")) {
|
|
59
57
|
return true;
|
|
60
58
|
}
|
|
61
59
|
if (relativePath === ".opencode/xdg-data" || relativePath.startsWith(".opencode/xdg-data/")) {
|
|
@@ -217,7 +215,7 @@ async function initializeWorkspaceDirectory() {
|
|
|
217
215
|
fs_1.default.mkdirSync(workspaceDir, { recursive: true });
|
|
218
216
|
const workflowsDir = path_1.default.join(workspaceDir, "workflows");
|
|
219
217
|
fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
220
|
-
const skillsDir = path_1.default.join(workspaceDir, "
|
|
218
|
+
const skillsDir = path_1.default.join(workspaceDir, ".agents", "skills");
|
|
221
219
|
fs_1.default.mkdirSync(skillsDir, { recursive: true });
|
|
222
220
|
const honeytokenValue = `DO_NOT_EXPOSE:${HONEYTOKEN_UUID}\n`;
|
|
223
221
|
fs_1.default.writeFileSync(path_1.default.join(workflowsDir, HONEYTOKEN_FILE_NAME), honeytokenValue, "utf-8");
|
|
@@ -24,6 +24,14 @@ interface PermissionResponse {
|
|
|
24
24
|
approved: boolean
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
interface SessionLookupResponse {
|
|
28
|
+
error?: unknown
|
|
29
|
+
data?: {
|
|
30
|
+
id?: string
|
|
31
|
+
parentID?: string
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
async function readErrorMessageFromResponse(
|
|
28
36
|
response: Response,
|
|
29
37
|
fallbackMessage: string
|
|
@@ -191,7 +199,29 @@ function buildSkillMarkdown(
|
|
|
191
199
|
return `---\nname: ${JSON.stringify(slug)}\ndescription: ${JSON.stringify(description)}\n---\n\n${body}\n`
|
|
192
200
|
}
|
|
193
201
|
|
|
194
|
-
export const CreateSkillPlugin: Plugin = async (
|
|
202
|
+
export const CreateSkillPlugin: Plugin = async ({ client }) => {
|
|
203
|
+
async function resolveRootSessionID(sessionID: string): Promise<string> {
|
|
204
|
+
let currentSessionID = sessionID
|
|
205
|
+
|
|
206
|
+
while (true) {
|
|
207
|
+
const response = (await client.session.get({
|
|
208
|
+
path: {
|
|
209
|
+
id: currentSessionID,
|
|
210
|
+
},
|
|
211
|
+
})) as SessionLookupResponse
|
|
212
|
+
if (response.error) {
|
|
213
|
+
throw new Error(`Failed to resolve root session for ${currentSessionID}`)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const parentID = response.data?.parentID
|
|
217
|
+
if (!parentID) {
|
|
218
|
+
return currentSessionID
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
currentSessionID = parentID
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
195
225
|
return {
|
|
196
226
|
tool: {
|
|
197
227
|
createSkill: tool({
|
|
@@ -210,6 +240,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
210
240
|
},
|
|
211
241
|
async execute(args, context) {
|
|
212
242
|
const { directory, sessionID } = context
|
|
243
|
+
const authSessionID = await resolveRootSessionID(sessionID)
|
|
213
244
|
const slug = args.slug.trim()
|
|
214
245
|
const description = args.description.trim()
|
|
215
246
|
const content = args.content.trim()
|
|
@@ -238,7 +269,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
238
269
|
throw new Error("Could not determine call id from tool context.")
|
|
239
270
|
}
|
|
240
271
|
|
|
241
|
-
const skillsDir = path.join(directory, "
|
|
272
|
+
const skillsDir = path.join(directory, ".agents", "skills")
|
|
242
273
|
const skillDir = path.join(skillsDir, slug)
|
|
243
274
|
|
|
244
275
|
if (!isPathInside(skillDir, skillsDir)) {
|
|
@@ -250,7 +281,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
250
281
|
}
|
|
251
282
|
|
|
252
283
|
await requestCreationPermission(
|
|
253
|
-
|
|
284
|
+
authSessionID,
|
|
254
285
|
messageId,
|
|
255
286
|
callId
|
|
256
287
|
)
|
|
@@ -259,7 +290,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
259
290
|
method: "POST",
|
|
260
291
|
headers: {
|
|
261
292
|
"Content-Type": "application/json",
|
|
262
|
-
Authorization: `Bearer ${
|
|
293
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
263
294
|
},
|
|
264
295
|
body: JSON.stringify({
|
|
265
296
|
name: slug,
|
|
@@ -278,7 +309,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
278
309
|
`${getApiBaseUrl()}/api/skills/${encodeURIComponent(slug)}/files/content?path=${encodeURIComponent("SKILL.md")}`,
|
|
279
310
|
{
|
|
280
311
|
headers: {
|
|
281
|
-
Authorization: `Bearer ${
|
|
312
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
282
313
|
},
|
|
283
314
|
}
|
|
284
315
|
)
|
|
@@ -302,7 +333,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
302
333
|
method: "PUT",
|
|
303
334
|
headers: {
|
|
304
335
|
"Content-Type": "application/json",
|
|
305
|
-
Authorization: `Bearer ${
|
|
336
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
306
337
|
},
|
|
307
338
|
body: JSON.stringify({
|
|
308
339
|
path: "SKILL.md",
|
|
@@ -324,7 +355,7 @@ export const CreateSkillPlugin: Plugin = async (_ctx) => {
|
|
|
324
355
|
skill: {
|
|
325
356
|
slug,
|
|
326
357
|
description,
|
|
327
|
-
file_path:
|
|
358
|
+
file_path: `.agents/skills/${slug}/SKILL.md`,
|
|
328
359
|
},
|
|
329
360
|
},
|
|
330
361
|
null,
|
|
@@ -37,6 +37,14 @@ interface PermissionResponse {
|
|
|
37
37
|
approved: boolean
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
interface SessionLookupResponse {
|
|
41
|
+
error?: unknown
|
|
42
|
+
data?: {
|
|
43
|
+
id?: string
|
|
44
|
+
parentID?: string
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
// ============================================================================
|
|
41
49
|
// Constants
|
|
42
50
|
// ============================================================================
|
|
@@ -195,7 +203,29 @@ async function requestWorkflowPermission(
|
|
|
195
203
|
// Plugin
|
|
196
204
|
// ============================================================================
|
|
197
205
|
|
|
198
|
-
export const CreateWorkflowPlugin: Plugin = async (
|
|
206
|
+
export const CreateWorkflowPlugin: Plugin = async ({ client }) => {
|
|
207
|
+
async function resolveRootSessionID(sessionID: string): Promise<string> {
|
|
208
|
+
let currentSessionID = sessionID
|
|
209
|
+
|
|
210
|
+
while (true) {
|
|
211
|
+
const response = (await client.session.get({
|
|
212
|
+
path: {
|
|
213
|
+
id: currentSessionID,
|
|
214
|
+
},
|
|
215
|
+
})) as SessionLookupResponse
|
|
216
|
+
if (response.error) {
|
|
217
|
+
throw new Error(`Failed to resolve root session for ${currentSessionID}`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const parentID = response.data?.parentID
|
|
221
|
+
if (!parentID) {
|
|
222
|
+
return currentSessionID
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
currentSessionID = parentID
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
199
229
|
return {
|
|
200
230
|
tool: {
|
|
201
231
|
createWorkflow: tool({
|
|
@@ -237,6 +267,7 @@ export const CreateWorkflowPlugin: Plugin = async (_ctx) => {
|
|
|
237
267
|
},
|
|
238
268
|
async execute(args, context) {
|
|
239
269
|
const { directory, sessionID } = context
|
|
270
|
+
const authSessionID = await resolveRootSessionID(sessionID)
|
|
240
271
|
const { slug, intent_summary, inputs = {}, timeout_seconds = 300 } = args
|
|
241
272
|
const messageId = extractMessageId(context)
|
|
242
273
|
const callId = extractCallId(context)
|
|
@@ -278,7 +309,7 @@ export const CreateWorkflowPlugin: Plugin = async (_ctx) => {
|
|
|
278
309
|
|
|
279
310
|
// Request permission after basic validation and existence checks pass.
|
|
280
311
|
await requestWorkflowPermission(
|
|
281
|
-
|
|
312
|
+
authSessionID,
|
|
282
313
|
messageId,
|
|
283
314
|
callId
|
|
284
315
|
)
|
|
@@ -316,7 +347,7 @@ export const CreateWorkflowPlugin: Plugin = async (_ctx) => {
|
|
|
316
347
|
method: "POST",
|
|
317
348
|
headers: {
|
|
318
349
|
"Content-Type": "application/json",
|
|
319
|
-
Authorization: `Bearer ${
|
|
350
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
320
351
|
},
|
|
321
352
|
}
|
|
322
353
|
)
|
|
@@ -26,6 +26,14 @@ interface WorkflowSummary {
|
|
|
26
26
|
intent_summary: string
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
interface SessionLookupResponse {
|
|
30
|
+
error?: unknown
|
|
31
|
+
data?: {
|
|
32
|
+
id?: string
|
|
33
|
+
parentID?: string
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
async function readErrorMessageFromResponse(
|
|
30
38
|
response: Response,
|
|
31
39
|
fallbackMessage: string
|
|
@@ -85,7 +93,29 @@ function cosineSimilarity(vecA: number[], vecB: number[]): number {
|
|
|
85
93
|
// Plugin
|
|
86
94
|
// ============================================================================
|
|
87
95
|
|
|
88
|
-
export const FindSimilarWorkflowPlugin: Plugin = async (
|
|
96
|
+
export const FindSimilarWorkflowPlugin: Plugin = async ({ client }) => {
|
|
97
|
+
async function resolveRootSessionID(sessionID: string): Promise<string> {
|
|
98
|
+
let currentSessionID = sessionID
|
|
99
|
+
|
|
100
|
+
while (true) {
|
|
101
|
+
const response = (await client.session.get({
|
|
102
|
+
path: {
|
|
103
|
+
id: currentSessionID,
|
|
104
|
+
},
|
|
105
|
+
})) as SessionLookupResponse
|
|
106
|
+
if (response.error) {
|
|
107
|
+
throw new Error(`Failed to resolve root session for ${currentSessionID}`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const parentID = response.data?.parentID
|
|
111
|
+
if (!parentID) {
|
|
112
|
+
return currentSessionID
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
currentSessionID = parentID
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
89
119
|
return {
|
|
90
120
|
tool: {
|
|
91
121
|
findSimilarWorkflow: tool({
|
|
@@ -105,11 +135,12 @@ export const FindSimilarWorkflowPlugin: Plugin = async (_ctx) => {
|
|
|
105
135
|
},
|
|
106
136
|
async execute(args, context) {
|
|
107
137
|
const { sessionID } = context
|
|
138
|
+
const authSessionID = await resolveRootSessionID(sessionID)
|
|
108
139
|
const { description, limit = 5 } = args
|
|
109
140
|
|
|
110
141
|
const workflowsResponse = await fetch(`${getApiBaseUrl()}/api/workflows`, {
|
|
111
142
|
headers: {
|
|
112
|
-
Authorization: `Bearer ${
|
|
143
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
113
144
|
},
|
|
114
145
|
})
|
|
115
146
|
|
|
@@ -31,6 +31,14 @@ interface SkillMatch {
|
|
|
31
31
|
similarity: number
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
interface SessionLookupResponse {
|
|
35
|
+
error?: unknown
|
|
36
|
+
data?: {
|
|
37
|
+
id?: string
|
|
38
|
+
parentID?: string
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
async function readErrorMessageFromResponse(
|
|
35
43
|
response: Response,
|
|
36
44
|
fallbackMessage: string
|
|
@@ -90,14 +98,14 @@ function cosineSimilarity(vecA: number[], vecB: number[]): number {
|
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
async function readSkillMarkdown(
|
|
93
|
-
|
|
101
|
+
authSessionID: string,
|
|
94
102
|
slug: string
|
|
95
103
|
): Promise<string> {
|
|
96
104
|
const response = await fetch(
|
|
97
105
|
`${getApiBaseUrl()}/api/skills/${encodeURIComponent(slug)}/files/content?path=${encodeURIComponent("SKILL.md")}`,
|
|
98
106
|
{
|
|
99
107
|
headers: {
|
|
100
|
-
Authorization: `Bearer ${
|
|
108
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
101
109
|
},
|
|
102
110
|
}
|
|
103
111
|
)
|
|
@@ -118,7 +126,29 @@ async function readSkillMarkdown(
|
|
|
118
126
|
return payload.content ?? ""
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
export const FindSkillPlugin: Plugin = async (
|
|
129
|
+
export const FindSkillPlugin: Plugin = async ({ client, directory }) => {
|
|
130
|
+
async function resolveRootSessionID(sessionID: string): Promise<string> {
|
|
131
|
+
let currentSessionID = sessionID
|
|
132
|
+
|
|
133
|
+
while (true) {
|
|
134
|
+
const response = (await client.session.get({
|
|
135
|
+
path: {
|
|
136
|
+
id: currentSessionID,
|
|
137
|
+
},
|
|
138
|
+
})) as SessionLookupResponse
|
|
139
|
+
if (response.error) {
|
|
140
|
+
throw new Error(`Failed to resolve root session for ${currentSessionID}`)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const parentID = response.data?.parentID
|
|
144
|
+
if (!parentID) {
|
|
145
|
+
return currentSessionID
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
currentSessionID = parentID
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
122
152
|
return {
|
|
123
153
|
tool: {
|
|
124
154
|
findSkill: tool({
|
|
@@ -143,9 +173,10 @@ export const FindSkillPlugin: Plugin = async (_ctx) => {
|
|
|
143
173
|
throw new Error("description is required")
|
|
144
174
|
}
|
|
145
175
|
|
|
176
|
+
const authSessionID = await resolveRootSessionID(sessionID)
|
|
146
177
|
const skillsResponse = await fetch(`${getApiBaseUrl()}/api/skills`, {
|
|
147
178
|
headers: {
|
|
148
|
-
Authorization: `Bearer ${
|
|
179
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
149
180
|
},
|
|
150
181
|
})
|
|
151
182
|
|
|
@@ -180,7 +211,7 @@ export const FindSkillPlugin: Plugin = async (_ctx) => {
|
|
|
180
211
|
const matches: SkillMatch[] = []
|
|
181
212
|
|
|
182
213
|
for (const skill of candidateSkills) {
|
|
183
|
-
const markdown = await readSkillMarkdown(
|
|
214
|
+
const markdown = await readSkillMarkdown(authSessionID, skill.slug)
|
|
184
215
|
const searchableText = `${skill.description}\n\n${stripLeadingFrontmatter(markdown)}`
|
|
185
216
|
const skillEmbedding = await getEmbedding(searchableText)
|
|
186
217
|
const similarity = cosineSimilarity(queryEmbedding, skillEmbedding)
|
|
@@ -23,6 +23,14 @@ interface SkillDetailsResponse {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
interface SessionLookupResponse {
|
|
27
|
+
error?: unknown
|
|
28
|
+
data?: {
|
|
29
|
+
id?: string
|
|
30
|
+
parentID?: string
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
async function readErrorMessageFromResponse(
|
|
27
35
|
response: Response,
|
|
28
36
|
fallbackMessage: string
|
|
@@ -45,7 +53,29 @@ async function readErrorMessageFromResponse(
|
|
|
45
53
|
}
|
|
46
54
|
}
|
|
47
55
|
|
|
48
|
-
export const GetSkillContentPlugin: Plugin = async (
|
|
56
|
+
export const GetSkillContentPlugin: Plugin = async ({ client }) => {
|
|
57
|
+
async function resolveRootSessionID(sessionID: string): Promise<string> {
|
|
58
|
+
let currentSessionID = sessionID
|
|
59
|
+
|
|
60
|
+
while (true) {
|
|
61
|
+
const response = (await client.session.get({
|
|
62
|
+
path: {
|
|
63
|
+
id: currentSessionID,
|
|
64
|
+
},
|
|
65
|
+
})) as SessionLookupResponse
|
|
66
|
+
if (response.error) {
|
|
67
|
+
throw new Error(`Failed to resolve root session for ${currentSessionID}`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const parentID = response.data?.parentID
|
|
71
|
+
if (!parentID) {
|
|
72
|
+
return currentSessionID
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
currentSessionID = parentID
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
49
79
|
return {
|
|
50
80
|
tool: {
|
|
51
81
|
getSkillContent: tool({
|
|
@@ -59,6 +89,7 @@ export const GetSkillContentPlugin: Plugin = async (_ctx) => {
|
|
|
59
89
|
async execute(args, context) {
|
|
60
90
|
const { sessionID } = context
|
|
61
91
|
const slug = args.slug.trim()
|
|
92
|
+
const authSessionID = await resolveRootSessionID(sessionID)
|
|
62
93
|
|
|
63
94
|
if (!SLUG_REGEX.test(slug)) {
|
|
64
95
|
throw new Error(
|
|
@@ -70,7 +101,7 @@ export const GetSkillContentPlugin: Plugin = async (_ctx) => {
|
|
|
70
101
|
`${getApiBaseUrl()}/api/skills/${encodeURIComponent(slug)}`,
|
|
71
102
|
{
|
|
72
103
|
headers: {
|
|
73
|
-
Authorization: `Bearer ${
|
|
104
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
74
105
|
},
|
|
75
106
|
}
|
|
76
107
|
)
|
|
@@ -97,7 +128,7 @@ export const GetSkillContentPlugin: Plugin = async (_ctx) => {
|
|
|
97
128
|
`${getApiBaseUrl()}/api/skills/${encodeURIComponent(slug)}/files/content?path=${encodeURIComponent("SKILL.md")}`,
|
|
98
129
|
{
|
|
99
130
|
headers: {
|
|
100
|
-
Authorization: `Bearer ${
|
|
131
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
101
132
|
},
|
|
102
133
|
}
|
|
103
134
|
)
|
|
@@ -18,7 +18,7 @@ async function ensureHoneytokenFile(workspaceDirectory: string): Promise<void> {
|
|
|
18
18
|
const workflowsHoneytokenPath = path.join(workflowsDirectory, HONEYTOKEN_FILE_NAME)
|
|
19
19
|
await fs.writeFile(workflowsHoneytokenPath, honeytokenValue, "utf-8")
|
|
20
20
|
|
|
21
|
-
const skillsDirectory = path.join(workspaceDirectory, "
|
|
21
|
+
const skillsDirectory = path.join(workspaceDirectory, ".agents", "skills")
|
|
22
22
|
await fs.mkdir(skillsDirectory, { recursive: true })
|
|
23
23
|
const honeytokenPath = path.join(skillsDirectory, HONEYTOKEN_FILE_NAME)
|
|
24
24
|
await fs.writeFile(honeytokenPath, honeytokenValue, "utf-8")
|
|
@@ -18,6 +18,14 @@ interface SkillSummary {
|
|
|
18
18
|
can_edit: boolean
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
interface SessionLookupResponse {
|
|
22
|
+
error?: unknown
|
|
23
|
+
data?: {
|
|
24
|
+
id?: string
|
|
25
|
+
parentID?: string
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
async function readErrorMessageFromResponse(
|
|
22
30
|
response: Response,
|
|
23
31
|
fallbackMessage: string
|
|
@@ -40,7 +48,29 @@ async function readErrorMessageFromResponse(
|
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
export const ListAvailableSkillsPlugin: Plugin = async (
|
|
51
|
+
export const ListAvailableSkillsPlugin: Plugin = async ({ client }) => {
|
|
52
|
+
async function resolveRootSessionID(sessionID: string): Promise<string> {
|
|
53
|
+
let currentSessionID = sessionID
|
|
54
|
+
|
|
55
|
+
while (true) {
|
|
56
|
+
const response = (await client.session.get({
|
|
57
|
+
path: {
|
|
58
|
+
id: currentSessionID,
|
|
59
|
+
},
|
|
60
|
+
})) as SessionLookupResponse
|
|
61
|
+
if (response.error) {
|
|
62
|
+
throw new Error(`Failed to resolve root session for ${currentSessionID}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const parentID = response.data?.parentID
|
|
66
|
+
if (!parentID) {
|
|
67
|
+
return currentSessionID
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
currentSessionID = parentID
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
44
74
|
return {
|
|
45
75
|
tool: {
|
|
46
76
|
listAvailableSkills: tool({
|
|
@@ -49,10 +79,11 @@ export const ListAvailableSkillsPlugin: Plugin = async (_ctx) => {
|
|
|
49
79
|
args: {},
|
|
50
80
|
async execute(_args, context) {
|
|
51
81
|
const { sessionID } = context
|
|
82
|
+
const authSessionID = await resolveRootSessionID(sessionID)
|
|
52
83
|
|
|
53
84
|
const response = await fetch(`${getApiBaseUrl()}/api/skills`, {
|
|
54
85
|
headers: {
|
|
55
|
-
Authorization: `Bearer ${
|
|
86
|
+
Authorization: `Bearer ${authSessionID}`,
|
|
56
87
|
},
|
|
57
88
|
})
|
|
58
89
|
|