@kosuke-ai/kosuke 1.0.14 → 1.0.16

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.
@@ -0,0 +1,64 @@
1
+ export const operation = {
2
+ "method": "post",
3
+ "path": "/api/agent-integrations/file-uploads",
4
+ "command": [
5
+ "integrations",
6
+ "uploads",
7
+ "create"
8
+ ],
9
+ "summary": "Reserve the upload of one file for an integration tool that takes files, on the connection a call would run on. The answer is a file reference and a URL: PUT the file to the URL with the headers given, then pass the reference as the file argument of that tool on that connection. `kosuke integrations call --file` does all of it, and is the command to use. A failure answers 200 with isError true, as a call does. A Connector Token or a browser session is refused.",
10
+ "pathParams": [],
11
+ "query": [
12
+ {
13
+ "name": "workspace",
14
+ "flag": "workspace",
15
+ "kind": "string",
16
+ "required": false,
17
+ "description": "The workspace the call is made in. Required with an API key, which is refused with 403 when you are not a member of it. An agent credential reaches its own workspace and refuses another."
18
+ }
19
+ ],
20
+ "body": {
21
+ "fields": [
22
+ {
23
+ "name": "name",
24
+ "flag": "name",
25
+ "kind": "string",
26
+ "required": true,
27
+ "description": "The tool the file is for, as the tools list names it."
28
+ },
29
+ {
30
+ "name": "account",
31
+ "flag": "account",
32
+ "kind": "string",
33
+ "required": false,
34
+ "description": "The connection the call will run on: its short id, its alias or scope:label. Needed when several can run the tool."
35
+ },
36
+ {
37
+ "name": "filename",
38
+ "flag": "filename",
39
+ "kind": "string",
40
+ "required": true,
41
+ "description": "The file name the vendor receives, without a directory."
42
+ },
43
+ {
44
+ "name": "mimetype",
45
+ "flag": "mimetype",
46
+ "kind": "string",
47
+ "required": true,
48
+ "description": "The MIME type of the file, such as video/mp4."
49
+ },
50
+ {
51
+ "name": "md5",
52
+ "flag": "md5",
53
+ "kind": "string",
54
+ "required": true,
55
+ "description": "The MD5 digest of the file, in lowercase hex."
56
+ }
57
+ ],
58
+ "required": true
59
+ },
60
+ "multipart": null,
61
+ "attachments": false,
62
+ "anonymous": false,
63
+ "agents": "all"
64
+ };
@@ -0,0 +1,42 @@
1
+ export const operation = {
2
+ "method": "post",
3
+ "path": "/api/sensei/fyi/move",
4
+ "command": [
5
+ "sensei",
6
+ "fyi",
7
+ "move"
8
+ ],
9
+ "summary": "Move one conversation's parked FYI messages to needs-reply, so its Event Turn drafts a reply card",
10
+ "pathParams": [],
11
+ "query": [],
12
+ "body": {
13
+ "fields": [
14
+ {
15
+ "name": "card",
16
+ "flag": "card",
17
+ "kind": "string",
18
+ "required": true,
19
+ "description": "The FYI digest card the member replied to"
20
+ },
21
+ {
22
+ "name": "connection",
23
+ "flag": "connection",
24
+ "kind": "string",
25
+ "required": true,
26
+ "description": "The connection of the conversation that needs the member"
27
+ },
28
+ {
29
+ "name": "conversation",
30
+ "flag": "conversation",
31
+ "kind": "string",
32
+ "required": true,
33
+ "description": "The conversation id, as the fyi_digest event and the card name it"
34
+ }
35
+ ],
36
+ "required": true
37
+ },
38
+ "multipart": null,
39
+ "attachments": false,
40
+ "anonymous": false,
41
+ "agents": "all"
42
+ };
@@ -6,7 +6,7 @@ export const operation = {
6
6
  "profile",
7
7
  "get"
8
8
  ],
9
- "summary": "Read your account: id, email, name, onboarding state, default model, auto-commit setting and Reply Style.",
9
+ "summary": "Read your account: id, email, name, onboarding state, default model, auto-commit setting, Reply Style and FYI digest interval.",
10
10
  "pathParams": [],
11
11
  "query": [],
12
12
  "body": null,
@@ -0,0 +1,153 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { createReadStream } from 'node:fs';
3
+ import { stat } from 'node:fs/promises';
4
+ import { basename, extname } from 'node:path';
5
+ import { Readable } from 'node:stream';
6
+ /**
7
+ * `kosuke integrations call --file <argument>=<path>`: a local file for a tool's file argument.
8
+ * The platform reserves the upload and answers a file reference; the file goes straight to the
9
+ * storage URL it names, never through the platform, so a large video costs the platform nothing.
10
+ */
11
+ export const FILE_UPLOAD_PATH = '/api/agent-integrations/file-uploads';
12
+ /** The storage takes one PUT of at most 5 GiB. */
13
+ export const MAX_FILE_BYTES = 5 * 1024 ** 3;
14
+ const NAME = /^[A-Za-z_][A-Za-z0-9_-]{0,63}$/;
15
+ const INDEX = /^\d{1,4}$/;
16
+ const UNSAFE = new Set(['__proto__', 'constructor', 'prototype']);
17
+ /** `videoFilePath=./clip.mp4`, or `attachment.0=./a.pdf` for a file inside a list or object. */
18
+ export function parseFileArgument(raw) {
19
+ const split = raw.indexOf('=');
20
+ if (split <= 0 || split === raw.length - 1)
21
+ return null;
22
+ const at = raw.slice(0, split).split('.');
23
+ const path = raw.slice(split + 1);
24
+ if (!NAME.test(at[0]))
25
+ return null;
26
+ if (at.some((segment) => UNSAFE.has(segment) || !(NAME.test(segment) || INDEX.test(segment)))) {
27
+ return null;
28
+ }
29
+ return { at, path };
30
+ }
31
+ /** Puts `value` at `at`, making the lists and objects on the way; false when one is in the way. */
32
+ export function setAt(args, at, value) {
33
+ let parent = args;
34
+ for (let index = 0; index < at.length; index += 1) {
35
+ const segment = at[index];
36
+ const last = index === at.length - 1;
37
+ const nextIsIndex = !last && INDEX.test(at[index + 1]);
38
+ if (Array.isArray(parent)) {
39
+ if (!INDEX.test(segment))
40
+ return false;
41
+ const position = Number(segment);
42
+ if (position > parent.length)
43
+ return false;
44
+ if (last) {
45
+ parent[position] = value;
46
+ return true;
47
+ }
48
+ parent[position] ??= nextIsIndex ? [] : {};
49
+ const child = parent[position];
50
+ if (typeof child !== 'object' || child === null)
51
+ return false;
52
+ parent = child;
53
+ continue;
54
+ }
55
+ if (INDEX.test(segment))
56
+ return false;
57
+ if (last) {
58
+ parent[segment] = value;
59
+ return true;
60
+ }
61
+ if (!Object.hasOwn(parent, segment))
62
+ parent[segment] = nextIsIndex ? [] : {};
63
+ const child = parent[segment];
64
+ if (typeof child !== 'object' || child === null)
65
+ return false;
66
+ if (nextIsIndex !== Array.isArray(child))
67
+ return false;
68
+ parent = child;
69
+ }
70
+ return false;
71
+ }
72
+ const MIME_TYPES = {
73
+ '.3gp': 'video/3gpp',
74
+ '.avi': 'video/x-msvideo',
75
+ '.flv': 'video/x-flv',
76
+ '.m4v': 'video/x-m4v',
77
+ '.mkv': 'video/x-matroska',
78
+ '.mov': 'video/quicktime',
79
+ '.mp4': 'video/mp4',
80
+ '.mpeg': 'video/mpeg',
81
+ '.mpg': 'video/mpeg',
82
+ '.webm': 'video/webm',
83
+ '.wmv': 'video/x-ms-wmv',
84
+ '.mp3': 'audio/mpeg',
85
+ '.m4a': 'audio/mp4',
86
+ '.wav': 'audio/wav',
87
+ '.ogg': 'audio/ogg',
88
+ '.srt': 'application/x-subrip',
89
+ '.vtt': 'text/vtt',
90
+ '.sbv': 'text/plain',
91
+ '.ttml': 'application/ttml+xml',
92
+ '.dfxp': 'application/ttml+xml',
93
+ '.scc': 'text/plain',
94
+ '.gif': 'image/gif',
95
+ '.jpeg': 'image/jpeg',
96
+ '.jpg': 'image/jpeg',
97
+ '.png': 'image/png',
98
+ '.svg': 'image/svg+xml',
99
+ '.webp': 'image/webp',
100
+ '.csv': 'text/csv',
101
+ '.html': 'text/html',
102
+ '.json': 'application/json',
103
+ '.md': 'text/markdown',
104
+ '.txt': 'text/plain',
105
+ '.xml': 'application/xml',
106
+ '.pdf': 'application/pdf',
107
+ '.zip': 'application/zip',
108
+ '.doc': 'application/msword',
109
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
110
+ '.xls': 'application/vnd.ms-excel',
111
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
112
+ '.ppt': 'application/vnd.ms-powerpoint',
113
+ '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
114
+ };
115
+ export function mimetypeOf(name) {
116
+ return MIME_TYPES[extname(name).toLowerCase()] ?? 'application/octet-stream';
117
+ }
118
+ /** The name the vendor receives: the base name, when the platform can take it. */
119
+ export function uploadName(path) {
120
+ const name = basename(path);
121
+ return name.length > 0 && name.length <= 255 && !/[/\\\p{Cc}]/u.test(name) ? name : null;
122
+ }
123
+ export async function fileSize(path) {
124
+ try {
125
+ const info = await stat(path);
126
+ return info.isFile() ? info.size : null;
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ export async function md5Of(path) {
133
+ const hash = createHash('md5');
134
+ for await (const chunk of createReadStream(path))
135
+ hash.update(chunk);
136
+ return hash.digest('hex');
137
+ }
138
+ /**
139
+ * One PUT of the whole file. The length is stated because the storage refuses a chunked upload,
140
+ * and the file is streamed so a large video is never held in memory.
141
+ */
142
+ export async function putFile(fetch, upload, path, size, mimetype, signal) {
143
+ const response = await fetch(upload.url, {
144
+ method: 'PUT',
145
+ headers: { ...upload.headers, 'content-type': mimetype, 'content-length': String(size) },
146
+ body: Readable.toWeb(createReadStream(path)),
147
+ duplex: 'half',
148
+ redirect: 'error',
149
+ signal,
150
+ });
151
+ await response.body?.cancel().catch(() => undefined);
152
+ return response.status;
153
+ }
package/openapi.json CHANGED
@@ -232,6 +232,107 @@
232
232
  }
233
233
  }
234
234
  },
235
+ "/api/agent-integrations/file-uploads": {
236
+ "post": {
237
+ "operationId": "integrationsUploadsCreate",
238
+ "summary": "Reserve the upload of one file for an integration tool that takes files, on the connection a call would run on. The answer is a file reference and a URL: PUT the file to the URL with the headers given, then pass the reference as the file argument of that tool on that connection. `kosuke integrations call --file` does all of it, and is the command to use. A failure answers 200 with isError true, as a call does. A Connector Token or a browser session is refused.",
239
+ "tags": ["integrations"],
240
+ "x-kosuke-command": ["integrations", "uploads", "create"],
241
+ "x-kosuke-sandbox-token": true,
242
+ "x-kosuke-cron-token": true,
243
+ "x-kosuke-sensei-token": true,
244
+ "parameters": [
245
+ {
246
+ "name": "workspace",
247
+ "in": "query",
248
+ "required": false,
249
+ "description": "The workspace the call is made in. Required with an API key, which is refused with 403 when you are not a member of it. An agent credential reaches its own workspace and refuses another.",
250
+ "schema": {
251
+ "description": "The workspace the call is made in. Required with an API key, which is refused with 403 when you are not a member of it. An agent credential reaches its own workspace and refuses another.",
252
+ "type": "string",
253
+ "minLength": 1,
254
+ "maxLength": 100
255
+ }
256
+ }
257
+ ],
258
+ "requestBody": {
259
+ "required": true,
260
+ "content": {
261
+ "application/json": {
262
+ "schema": {
263
+ "type": "object",
264
+ "properties": {
265
+ "name": {
266
+ "type": "string",
267
+ "minLength": 1,
268
+ "maxLength": 80,
269
+ "description": "The tool the file is for, as the tools list names it."
270
+ },
271
+ "account": {
272
+ "description": "The connection the call will run on: its short id, its alias or scope:label. Needed when several can run the tool.",
273
+ "type": "string",
274
+ "minLength": 1,
275
+ "maxLength": 100
276
+ },
277
+ "filename": {
278
+ "type": "string",
279
+ "minLength": 1,
280
+ "maxLength": 255,
281
+ "pattern": "^[^/\\\\\\p{Cc}]+$",
282
+ "description": "The file name the vendor receives, without a directory."
283
+ },
284
+ "mimetype": {
285
+ "type": "string",
286
+ "maxLength": 255,
287
+ "pattern": "^[a-z0-9][a-z0-9!#$&^_.+-]*\\/[a-z0-9][a-z0-9!#$&^_.+-]*$",
288
+ "description": "The MIME type of the file, such as video/mp4."
289
+ },
290
+ "md5": {
291
+ "type": "string",
292
+ "pattern": "^[a-f0-9]{32}$",
293
+ "description": "The MD5 digest of the file, in lowercase hex."
294
+ }
295
+ },
296
+ "required": ["name", "filename", "mimetype", "md5"],
297
+ "additionalProperties": false
298
+ }
299
+ }
300
+ }
301
+ },
302
+ "responses": {
303
+ "2XX": {
304
+ "description": "Success envelope",
305
+ "content": {
306
+ "application/json": {
307
+ "schema": {
308
+ "$ref": "#/components/schemas/ApiSuccess"
309
+ }
310
+ }
311
+ }
312
+ },
313
+ "4XX": {
314
+ "description": "Error envelope",
315
+ "content": {
316
+ "application/json": {
317
+ "schema": {
318
+ "$ref": "#/components/schemas/ApiError"
319
+ }
320
+ }
321
+ }
322
+ },
323
+ "5XX": {
324
+ "description": "Error envelope",
325
+ "content": {
326
+ "application/json": {
327
+ "schema": {
328
+ "$ref": "#/components/schemas/ApiError"
329
+ }
330
+ }
331
+ }
332
+ }
333
+ }
334
+ }
335
+ },
235
336
  "/api/agent-integrations/tools": {
236
337
  "get": {
237
338
  "operationId": "integrationsToolsList",
@@ -20848,6 +20949,72 @@
20848
20949
  }
20849
20950
  },
20850
20951
  "required": ["type", "title", "items"]
20952
+ },
20953
+ {
20954
+ "type": "object",
20955
+ "properties": {
20956
+ "type": {
20957
+ "type": "string",
20958
+ "const": "messages"
20959
+ },
20960
+ "caption": {
20961
+ "type": "string",
20962
+ "minLength": 1,
20963
+ "maxLength": 120
20964
+ },
20965
+ "rows": {
20966
+ "minItems": 1,
20967
+ "maxItems": 12,
20968
+ "type": "array",
20969
+ "items": {
20970
+ "type": "object",
20971
+ "properties": {
20972
+ "network": {
20973
+ "type": "string",
20974
+ "enum": ["gmail", "slack", "telegram", "whatsapp"]
20975
+ },
20976
+ "sender": {
20977
+ "type": "string",
20978
+ "minLength": 1,
20979
+ "maxLength": 60
20980
+ },
20981
+ "subject": {
20982
+ "type": "string",
20983
+ "minLength": 1,
20984
+ "maxLength": 200
20985
+ },
20986
+ "count": {
20987
+ "type": "integer",
20988
+ "exclusiveMinimum": 0,
20989
+ "maximum": 9007199254740991
20990
+ },
20991
+ "at": {
20992
+ "type": "string",
20993
+ "minLength": 1,
20994
+ "maxLength": 20
20995
+ }
20996
+ },
20997
+ "required": ["network", "sender", "subject", "count", "at"]
20998
+ }
20999
+ },
21000
+ "older": {
21001
+ "type": "object",
21002
+ "properties": {
21003
+ "conversations": {
21004
+ "type": "integer",
21005
+ "exclusiveMinimum": 0,
21006
+ "maximum": 9007199254740991
21007
+ },
21008
+ "messages": {
21009
+ "type": "integer",
21010
+ "exclusiveMinimum": 0,
21011
+ "maximum": 9007199254740991
21012
+ }
21013
+ },
21014
+ "required": ["conversations", "messages"]
21015
+ }
21016
+ },
21017
+ "required": ["type", "rows"]
20851
21018
  }
20852
21019
  ]
20853
21020
  }
@@ -20987,6 +21154,9 @@
20987
21154
  "messages": {
20988
21155
  "not": {}
20989
21156
  },
21157
+ "messagesDigest": {
21158
+ "not": {}
21159
+ },
20990
21160
  "projectId": {
20991
21161
  "not": {}
20992
21162
  },
@@ -21058,6 +21228,9 @@
21058
21228
  },
21059
21229
  "messages": {
21060
21230
  "not": {}
21231
+ },
21232
+ "messagesDigest": {
21233
+ "not": {}
21061
21234
  }
21062
21235
  },
21063
21236
  "required": ["projectId"]
@@ -21081,6 +21254,9 @@
21081
21254
  "messages": {
21082
21255
  "not": {}
21083
21256
  },
21257
+ "messagesDigest": {
21258
+ "not": {}
21259
+ },
21084
21260
  "projectId": {
21085
21261
  "not": {}
21086
21262
  },
@@ -21119,6 +21295,9 @@
21119
21295
  "messages": {
21120
21296
  "not": {}
21121
21297
  },
21298
+ "messagesDigest": {
21299
+ "not": {}
21300
+ },
21122
21301
  "projectId": {
21123
21302
  "not": {}
21124
21303
  },
@@ -21164,6 +21343,9 @@
21164
21343
  "required": ["connection", "conversation"],
21165
21344
  "additionalProperties": false
21166
21345
  },
21346
+ "messagesDigest": {
21347
+ "not": {}
21348
+ },
21167
21349
  "memory": {
21168
21350
  "not": {}
21169
21351
  },
@@ -21189,6 +21371,47 @@
21189
21371
  "required": ["messages"],
21190
21372
  "additionalProperties": false
21191
21373
  },
21374
+ {
21375
+ "type": "object",
21376
+ "properties": {
21377
+ "nudge": {
21378
+ "not": {}
21379
+ },
21380
+ "job": {
21381
+ "not": {}
21382
+ },
21383
+ "messages": {
21384
+ "not": {}
21385
+ },
21386
+ "messagesDigest": {
21387
+ "type": "boolean",
21388
+ "const": true
21389
+ },
21390
+ "memory": {
21391
+ "not": {}
21392
+ },
21393
+ "integration": {
21394
+ "not": {}
21395
+ },
21396
+ "projectId": {
21397
+ "not": {}
21398
+ },
21399
+ "sessionId": {
21400
+ "not": {}
21401
+ },
21402
+ "pullRequest": {
21403
+ "not": {}
21404
+ },
21405
+ "cronId": {
21406
+ "not": {}
21407
+ },
21408
+ "deployJobId": {
21409
+ "not": {}
21410
+ }
21411
+ },
21412
+ "required": ["messagesDigest"],
21413
+ "additionalProperties": false
21414
+ },
21192
21415
  {
21193
21416
  "type": "object",
21194
21417
  "properties": {
@@ -21221,6 +21444,9 @@
21221
21444
  "messages": {
21222
21445
  "not": {}
21223
21446
  },
21447
+ "messagesDigest": {
21448
+ "not": {}
21449
+ },
21224
21450
  "projectId": {
21225
21451
  "not": {}
21226
21452
  },
@@ -21290,7 +21516,7 @@
21290
21516
  },
21291
21517
  "rows": {
21292
21518
  "minItems": 1,
21293
- "maxItems": 12,
21519
+ "maxItems": 50,
21294
21520
  "type": "array",
21295
21521
  "items": {
21296
21522
  "type": "object",
@@ -22204,6 +22430,81 @@
22204
22430
  }
22205
22431
  }
22206
22432
  },
22433
+ "/api/sensei/fyi/move": {
22434
+ "post": {
22435
+ "operationId": "senseiFyiMove",
22436
+ "summary": "Move one conversation's parked FYI messages to needs-reply, so its Event Turn drafts a reply card",
22437
+ "tags": ["sensei"],
22438
+ "x-kosuke-command": ["sensei", "fyi", "move"],
22439
+ "x-kosuke-sandbox-token": true,
22440
+ "x-kosuke-cron-token": true,
22441
+ "x-kosuke-sensei-token": true,
22442
+ "requestBody": {
22443
+ "required": true,
22444
+ "content": {
22445
+ "application/json": {
22446
+ "schema": {
22447
+ "type": "object",
22448
+ "properties": {
22449
+ "card": {
22450
+ "type": "string",
22451
+ "format": "uuid",
22452
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
22453
+ "description": "The FYI digest card the member replied to"
22454
+ },
22455
+ "connection": {
22456
+ "type": "string",
22457
+ "format": "uuid",
22458
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
22459
+ "description": "The connection of the conversation that needs the member"
22460
+ },
22461
+ "conversation": {
22462
+ "type": "string",
22463
+ "minLength": 1,
22464
+ "maxLength": 512,
22465
+ "description": "The conversation id, as the fyi_digest event and the card name it"
22466
+ }
22467
+ },
22468
+ "required": ["card", "connection", "conversation"],
22469
+ "additionalProperties": false
22470
+ }
22471
+ }
22472
+ }
22473
+ },
22474
+ "responses": {
22475
+ "2XX": {
22476
+ "description": "Success envelope",
22477
+ "content": {
22478
+ "application/json": {
22479
+ "schema": {
22480
+ "$ref": "#/components/schemas/ApiSuccess"
22481
+ }
22482
+ }
22483
+ }
22484
+ },
22485
+ "4XX": {
22486
+ "description": "Error envelope",
22487
+ "content": {
22488
+ "application/json": {
22489
+ "schema": {
22490
+ "$ref": "#/components/schemas/ApiError"
22491
+ }
22492
+ }
22493
+ }
22494
+ },
22495
+ "5XX": {
22496
+ "description": "Error envelope",
22497
+ "content": {
22498
+ "application/json": {
22499
+ "schema": {
22500
+ "$ref": "#/components/schemas/ApiError"
22501
+ }
22502
+ }
22503
+ }
22504
+ }
22505
+ }
22506
+ }
22507
+ },
22207
22508
  "/api/sensei/memory": {
22208
22509
  "get": {
22209
22510
  "operationId": "senseiMemoryGet",
@@ -26316,7 +26617,7 @@
26316
26617
  "/api/user/profile": {
26317
26618
  "get": {
26318
26619
  "operationId": "userProfileGet",
26319
- "summary": "Read your account: id, email, name, onboarding state, default model, auto-commit setting and Reply Style.",
26620
+ "summary": "Read your account: id, email, name, onboarding state, default model, auto-commit setting, Reply Style and FYI digest interval.",
26320
26621
  "tags": ["user"],
26321
26622
  "x-kosuke-command": ["user", "profile", "get"],
26322
26623
  "x-kosuke-sandbox-token": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kosuke-ai/kosuke",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Kosuke platform CLI. The client customers install to drive app.kosuke.ai from their own machine.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,11 +40,10 @@
40
40
  "vitest": "^4.1.12"
41
41
  },
42
42
  "kosukeRelease": {
43
- "sourceCommit": "76f5440713c26ece8b7a10148e2756fcda195399",
44
- "sourceIdentity": "6d76d9b36ed84006ecffb3cbf97a91c26c66ebf3596e530a3aa342cbb7421ed0",
43
+ "sourceCommit": "ab85cf946cdc768b0b0a051ea9a25426f03f56d2",
44
+ "sourceIdentity": "54461d5b39b8b5ac97df0fa3bf085e169b29ed7454ee8b09efbc26b40b8085ce",
45
45
  "fragments": [
46
- "devices-list-occupied-mmw6bi.json",
47
- "task-requested-branch.json"
46
+ "sensei-fyi-move.json"
48
47
  ]
49
48
  }
50
49
  }