agent-dealer 1.2.13 → 1.2.15
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/bundle/server/dist/adapters/git-worktree.js +15 -0
- package/bundle/server/dist/adapters/git-worktree.test.js +30 -0
- package/bundle/server/dist/adapters/linear-inbox.js +97 -0
- package/bundle/server/dist/adapters/linear-inbox.test.js +299 -1
- package/bundle/server/dist/adapters/linear-sync.js +33 -10
- package/bundle/server/dist/capacity/claude-events.js +25 -5
- package/bundle/server/dist/capacity/claude-local-cache.js +159 -13
- package/bundle/server/dist/capacity/claude-local-cache.test.js +191 -3
- package/bundle/server/dist/coordinator/auto-merge.js +10 -0
- package/bundle/server/dist/coordinator/branch-progress.js +5 -4
- package/bundle/server/dist/coordinator/commands.js +37 -3
- package/bundle/server/dist/coordinator/delete-issue.js +473 -0
- package/bundle/server/dist/coordinator/developer-effect.js +33 -6
- package/bundle/server/dist/coordinator/linear-merge-verify.js +382 -0
- package/bundle/server/dist/coordinator/linear-merge-verify.test.js +376 -0
- package/bundle/server/dist/coordinator/prompts.js +9 -1
- package/bundle/server/dist/coordinator/prompts.test.js +93 -0
- package/bundle/server/dist/coordinator/source-attachments.js +496 -0
- package/bundle/server/dist/coordinator/source-attachments.test.js +396 -0
- package/bundle/server/dist/coordinator/source-reload.js +178 -0
- package/bundle/server/dist/db/index.js +4 -0
- package/bundle/server/dist/db/schema.sql +32 -0
- package/bundle/server/dist/docs-linear-attachments.test.js +36 -0
- package/bundle/server/dist/paths.js +12 -0
- package/bundle/server/dist/repository/runtime-capacity.js +19 -3
- package/bundle/server/dist/repository/source-attachments.js +56 -0
- package/bundle/server/dist/routes/human-actions.js +17 -0
- package/bundle/server/dist/routes/issues-delete.test.js +666 -0
- package/bundle/server/dist/routes/issues-execution-contract.test.js +296 -0
- package/bundle/server/dist/routes/issues-source-attachments.test.js +447 -0
- package/bundle/server/dist/routes/issues.js +109 -1
- package/bundle/server/dist/routes/runtime-capacity.test.js +35 -0
- package/bundle/server/package.json +2 -2
- package/bundle/server/static-ui/assets/index-DVyqblie.js +64 -0
- package/bundle/server/static-ui/index.html +1 -1
- package/bundle/shared/dist/index.d.ts +108 -2
- package/bundle/shared/dist/index.js +12 -1
- package/bundle/shared/dist/issues.d.ts +43 -2
- package/bundle/shared/dist/issues.js +10 -0
- package/bundle/shared/dist/linear-intake.d.ts +85 -0
- package/bundle/shared/dist/linear-intake.js +56 -0
- package/bundle/shared/dist/linear-intake.test.js +55 -1
- package/bundle/shared/dist/runtime-capacity.d.ts +147 -0
- package/bundle/shared/dist/runtime-capacity.js +15 -0
- package/bundle/shared/dist/workflow.d.ts +4 -4
- package/bundle/shared/dist/workflow.js +7 -0
- package/bundle/shared/package.json +1 -1
- package/package.json +1 -1
- package/bundle/server/static-ui/assets/index-BzLjMBI9.js +0 -64
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
9
9
|
<meta name="theme-color" content="#0a0e14" />
|
|
10
10
|
<link rel="preload" href="/fonts/Monaco.ttf" as="font" type="font/ttf" crossorigin />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-DVyqblie.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-COYJxD1y.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
@@ -593,16 +593,16 @@ export declare const Artifact: z.ZodObject<{
|
|
|
593
593
|
createdAt: string;
|
|
594
594
|
kind: "slack_draft" | "email_draft" | "service_draft" | "task_snapshot" | "draft_plan" | "approved_plan" | "plan_triage" | "plan_answers" | "acceptance_criteria" | "transcript" | "stream_trace" | "usage" | "agent_session" | "execution_result" | "diff" | "pr" | "document" | "research_brief" | "deliverable" | "feedback" | "playbook_patch" | "playbook_use_receipt" | "deck_feedback_signal" | "reflect_status" | "linear_sync" | "send_receipt" | "result_qa";
|
|
595
595
|
runId: string;
|
|
596
|
-
contentJson: string | null;
|
|
597
596
|
blobPath: string | null;
|
|
597
|
+
contentJson: string | null;
|
|
598
598
|
author: "human" | "system" | "agent";
|
|
599
599
|
}, {
|
|
600
600
|
id: string;
|
|
601
601
|
createdAt: string;
|
|
602
602
|
kind: "slack_draft" | "email_draft" | "service_draft" | "task_snapshot" | "draft_plan" | "approved_plan" | "plan_triage" | "plan_answers" | "acceptance_criteria" | "transcript" | "stream_trace" | "usage" | "agent_session" | "execution_result" | "diff" | "pr" | "document" | "research_brief" | "deliverable" | "feedback" | "playbook_patch" | "playbook_use_receipt" | "deck_feedback_signal" | "reflect_status" | "linear_sync" | "send_receipt" | "result_qa";
|
|
603
603
|
runId: string;
|
|
604
|
-
contentJson: string | null;
|
|
605
604
|
blobPath: string | null;
|
|
605
|
+
contentJson: string | null;
|
|
606
606
|
author: "human" | "system" | "agent";
|
|
607
607
|
}>;
|
|
608
608
|
export type Artifact = z.infer<typeof Artifact>;
|
|
@@ -994,6 +994,12 @@ export declare const LinearCandidate: z.ZodObject<{
|
|
|
994
994
|
state: z.ZodOptional<z.ZodString>;
|
|
995
995
|
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
996
996
|
teamId: z.ZodOptional<z.ZodString>;
|
|
997
|
+
/**
|
|
998
|
+
* NOT-362: Linear's own suggested branch name for the issue. Intake persists
|
|
999
|
+
* it as the Dealer issue's branch so Linear's GitHub integration can link
|
|
1000
|
+
* the PR on repositories it covers.
|
|
1001
|
+
*/
|
|
1002
|
+
branchName: z.ZodOptional<z.ZodString>;
|
|
997
1003
|
/**
|
|
998
1004
|
* NOT-242: server-resolved repository hint read from explicit `repo:` labels.
|
|
999
1005
|
* Raw labels stay on the candidate; this is the deterministic read of them.
|
|
@@ -1017,6 +1023,29 @@ export declare const LinearCandidate: z.ZodObject<{
|
|
|
1017
1023
|
labels?: string[] | undefined;
|
|
1018
1024
|
error?: string | undefined;
|
|
1019
1025
|
}>>;
|
|
1026
|
+
/**
|
|
1027
|
+
* NOT-364: the issue's own file/link attachments (never comment uploads).
|
|
1028
|
+
* Hosted files are snapshotted at import/reload; links stay metadata only.
|
|
1029
|
+
*/
|
|
1030
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1031
|
+
id: z.ZodString;
|
|
1032
|
+
title: z.ZodString;
|
|
1033
|
+
url: z.ZodString;
|
|
1034
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
1035
|
+
source: z.ZodOptional<z.ZodString>;
|
|
1036
|
+
}, "strip", z.ZodTypeAny, {
|
|
1037
|
+
id: string;
|
|
1038
|
+
title: string;
|
|
1039
|
+
url: string;
|
|
1040
|
+
subtitle?: string | undefined;
|
|
1041
|
+
source?: string | undefined;
|
|
1042
|
+
}, {
|
|
1043
|
+
id: string;
|
|
1044
|
+
title: string;
|
|
1045
|
+
url: string;
|
|
1046
|
+
subtitle?: string | undefined;
|
|
1047
|
+
source?: string | undefined;
|
|
1048
|
+
}>, "many">>;
|
|
1020
1049
|
}, "strip", z.ZodTypeAny, {
|
|
1021
1050
|
id: string;
|
|
1022
1051
|
title: string;
|
|
@@ -1026,6 +1055,7 @@ export declare const LinearCandidate: z.ZodObject<{
|
|
|
1026
1055
|
labels?: string[] | undefined;
|
|
1027
1056
|
state?: string | undefined;
|
|
1028
1057
|
teamId?: string | undefined;
|
|
1058
|
+
branchName?: string | undefined;
|
|
1029
1059
|
repoResolution?: {
|
|
1030
1060
|
status: "resolved" | "unresolved" | "conflict" | "invalid";
|
|
1031
1061
|
repository?: string | undefined;
|
|
@@ -1033,6 +1063,13 @@ export declare const LinearCandidate: z.ZodObject<{
|
|
|
1033
1063
|
labels?: string[] | undefined;
|
|
1034
1064
|
error?: string | undefined;
|
|
1035
1065
|
} | undefined;
|
|
1066
|
+
attachments?: {
|
|
1067
|
+
id: string;
|
|
1068
|
+
title: string;
|
|
1069
|
+
url: string;
|
|
1070
|
+
subtitle?: string | undefined;
|
|
1071
|
+
source?: string | undefined;
|
|
1072
|
+
}[] | undefined;
|
|
1036
1073
|
}, {
|
|
1037
1074
|
id: string;
|
|
1038
1075
|
title: string;
|
|
@@ -1042,6 +1079,7 @@ export declare const LinearCandidate: z.ZodObject<{
|
|
|
1042
1079
|
labels?: string[] | undefined;
|
|
1043
1080
|
state?: string | undefined;
|
|
1044
1081
|
teamId?: string | undefined;
|
|
1082
|
+
branchName?: string | undefined;
|
|
1045
1083
|
repoResolution?: {
|
|
1046
1084
|
status: "resolved" | "unresolved" | "conflict" | "invalid";
|
|
1047
1085
|
repository?: string | undefined;
|
|
@@ -1049,6 +1087,13 @@ export declare const LinearCandidate: z.ZodObject<{
|
|
|
1049
1087
|
labels?: string[] | undefined;
|
|
1050
1088
|
error?: string | undefined;
|
|
1051
1089
|
} | undefined;
|
|
1090
|
+
attachments?: {
|
|
1091
|
+
id: string;
|
|
1092
|
+
title: string;
|
|
1093
|
+
url: string;
|
|
1094
|
+
subtitle?: string | undefined;
|
|
1095
|
+
source?: string | undefined;
|
|
1096
|
+
}[] | undefined;
|
|
1052
1097
|
}>;
|
|
1053
1098
|
export type LinearCandidate = z.infer<typeof LinearCandidate>;
|
|
1054
1099
|
export declare const LinearIntakeConfig: z.ZodObject<{
|
|
@@ -1274,6 +1319,12 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1274
1319
|
state: z.ZodOptional<z.ZodString>;
|
|
1275
1320
|
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1276
1321
|
teamId: z.ZodOptional<z.ZodString>;
|
|
1322
|
+
/**
|
|
1323
|
+
* NOT-362: Linear's own suggested branch name for the issue. Intake persists
|
|
1324
|
+
* it as the Dealer issue's branch so Linear's GitHub integration can link
|
|
1325
|
+
* the PR on repositories it covers.
|
|
1326
|
+
*/
|
|
1327
|
+
branchName: z.ZodOptional<z.ZodString>;
|
|
1277
1328
|
/**
|
|
1278
1329
|
* NOT-242: server-resolved repository hint read from explicit `repo:` labels.
|
|
1279
1330
|
* Raw labels stay on the candidate; this is the deterministic read of them.
|
|
@@ -1297,6 +1348,29 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1297
1348
|
labels?: string[] | undefined;
|
|
1298
1349
|
error?: string | undefined;
|
|
1299
1350
|
}>>;
|
|
1351
|
+
/**
|
|
1352
|
+
* NOT-364: the issue's own file/link attachments (never comment uploads).
|
|
1353
|
+
* Hosted files are snapshotted at import/reload; links stay metadata only.
|
|
1354
|
+
*/
|
|
1355
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1356
|
+
id: z.ZodString;
|
|
1357
|
+
title: z.ZodString;
|
|
1358
|
+
url: z.ZodString;
|
|
1359
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
1360
|
+
source: z.ZodOptional<z.ZodString>;
|
|
1361
|
+
}, "strip", z.ZodTypeAny, {
|
|
1362
|
+
id: string;
|
|
1363
|
+
title: string;
|
|
1364
|
+
url: string;
|
|
1365
|
+
subtitle?: string | undefined;
|
|
1366
|
+
source?: string | undefined;
|
|
1367
|
+
}, {
|
|
1368
|
+
id: string;
|
|
1369
|
+
title: string;
|
|
1370
|
+
url: string;
|
|
1371
|
+
subtitle?: string | undefined;
|
|
1372
|
+
source?: string | undefined;
|
|
1373
|
+
}>, "many">>;
|
|
1300
1374
|
}, "strip", z.ZodTypeAny, {
|
|
1301
1375
|
id: string;
|
|
1302
1376
|
title: string;
|
|
@@ -1306,6 +1380,7 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1306
1380
|
labels?: string[] | undefined;
|
|
1307
1381
|
state?: string | undefined;
|
|
1308
1382
|
teamId?: string | undefined;
|
|
1383
|
+
branchName?: string | undefined;
|
|
1309
1384
|
repoResolution?: {
|
|
1310
1385
|
status: "resolved" | "unresolved" | "conflict" | "invalid";
|
|
1311
1386
|
repository?: string | undefined;
|
|
@@ -1313,6 +1388,13 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1313
1388
|
labels?: string[] | undefined;
|
|
1314
1389
|
error?: string | undefined;
|
|
1315
1390
|
} | undefined;
|
|
1391
|
+
attachments?: {
|
|
1392
|
+
id: string;
|
|
1393
|
+
title: string;
|
|
1394
|
+
url: string;
|
|
1395
|
+
subtitle?: string | undefined;
|
|
1396
|
+
source?: string | undefined;
|
|
1397
|
+
}[] | undefined;
|
|
1316
1398
|
}, {
|
|
1317
1399
|
id: string;
|
|
1318
1400
|
title: string;
|
|
@@ -1322,6 +1404,7 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1322
1404
|
labels?: string[] | undefined;
|
|
1323
1405
|
state?: string | undefined;
|
|
1324
1406
|
teamId?: string | undefined;
|
|
1407
|
+
branchName?: string | undefined;
|
|
1325
1408
|
repoResolution?: {
|
|
1326
1409
|
status: "resolved" | "unresolved" | "conflict" | "invalid";
|
|
1327
1410
|
repository?: string | undefined;
|
|
@@ -1329,6 +1412,13 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1329
1412
|
labels?: string[] | undefined;
|
|
1330
1413
|
error?: string | undefined;
|
|
1331
1414
|
} | undefined;
|
|
1415
|
+
attachments?: {
|
|
1416
|
+
id: string;
|
|
1417
|
+
title: string;
|
|
1418
|
+
url: string;
|
|
1419
|
+
subtitle?: string | undefined;
|
|
1420
|
+
source?: string | undefined;
|
|
1421
|
+
}[] | undefined;
|
|
1332
1422
|
}>, "many">;
|
|
1333
1423
|
hasMore: z.ZodBoolean;
|
|
1334
1424
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -1341,6 +1431,7 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1341
1431
|
labels?: string[] | undefined;
|
|
1342
1432
|
state?: string | undefined;
|
|
1343
1433
|
teamId?: string | undefined;
|
|
1434
|
+
branchName?: string | undefined;
|
|
1344
1435
|
repoResolution?: {
|
|
1345
1436
|
status: "resolved" | "unresolved" | "conflict" | "invalid";
|
|
1346
1437
|
repository?: string | undefined;
|
|
@@ -1348,6 +1439,13 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1348
1439
|
labels?: string[] | undefined;
|
|
1349
1440
|
error?: string | undefined;
|
|
1350
1441
|
} | undefined;
|
|
1442
|
+
attachments?: {
|
|
1443
|
+
id: string;
|
|
1444
|
+
title: string;
|
|
1445
|
+
url: string;
|
|
1446
|
+
subtitle?: string | undefined;
|
|
1447
|
+
source?: string | undefined;
|
|
1448
|
+
}[] | undefined;
|
|
1351
1449
|
}[];
|
|
1352
1450
|
hasMore: boolean;
|
|
1353
1451
|
}, {
|
|
@@ -1360,6 +1458,7 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1360
1458
|
labels?: string[] | undefined;
|
|
1361
1459
|
state?: string | undefined;
|
|
1362
1460
|
teamId?: string | undefined;
|
|
1461
|
+
branchName?: string | undefined;
|
|
1363
1462
|
repoResolution?: {
|
|
1364
1463
|
status: "resolved" | "unresolved" | "conflict" | "invalid";
|
|
1365
1464
|
repository?: string | undefined;
|
|
@@ -1367,6 +1466,13 @@ export declare const LinearCandidatesPage: z.ZodObject<{
|
|
|
1367
1466
|
labels?: string[] | undefined;
|
|
1368
1467
|
error?: string | undefined;
|
|
1369
1468
|
} | undefined;
|
|
1469
|
+
attachments?: {
|
|
1470
|
+
id: string;
|
|
1471
|
+
title: string;
|
|
1472
|
+
url: string;
|
|
1473
|
+
subtitle?: string | undefined;
|
|
1474
|
+
source?: string | undefined;
|
|
1475
|
+
}[] | undefined;
|
|
1370
1476
|
}[];
|
|
1371
1477
|
hasMore: boolean;
|
|
1372
1478
|
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { LinearRepoResolution } from "./linear-intake.js";
|
|
2
|
+
import { LinearAttachment, LinearRepoResolution } from "./linear-intake.js";
|
|
3
3
|
import { Runtime } from "./runtime.js";
|
|
4
4
|
import { AgentWithHealth } from "./agents.js";
|
|
5
5
|
import { PhaseBudget, RunBudget } from "./budget.js";
|
|
@@ -267,11 +267,22 @@ export const LinearCandidate = z.object({
|
|
|
267
267
|
state: z.string().optional(),
|
|
268
268
|
labels: z.array(z.string()).optional(),
|
|
269
269
|
teamId: z.string().optional(),
|
|
270
|
+
/**
|
|
271
|
+
* NOT-362: Linear's own suggested branch name for the issue. Intake persists
|
|
272
|
+
* it as the Dealer issue's branch so Linear's GitHub integration can link
|
|
273
|
+
* the PR on repositories it covers.
|
|
274
|
+
*/
|
|
275
|
+
branchName: z.string().optional(),
|
|
270
276
|
/**
|
|
271
277
|
* NOT-242: server-resolved repository hint read from explicit `repo:` labels.
|
|
272
278
|
* Raw labels stay on the candidate; this is the deterministic read of them.
|
|
273
279
|
*/
|
|
274
280
|
repoResolution: LinearRepoResolution.optional(),
|
|
281
|
+
/**
|
|
282
|
+
* NOT-364: the issue's own file/link attachments (never comment uploads).
|
|
283
|
+
* Hosted files are snapshotted at import/reload; links stay metadata only.
|
|
284
|
+
*/
|
|
285
|
+
attachments: z.array(LinearAttachment).optional(),
|
|
275
286
|
});
|
|
276
287
|
// NOT-71: defaultAgentId and routingRules (label -> agent `autoAgent` routing) are gone
|
|
277
288
|
// with intake/agent-routing.ts and the promote endpoint that consumed them. The persisted
|
|
@@ -209,6 +209,33 @@ export declare const CreateIssueInput: z.ZodObject<{
|
|
|
209
209
|
* `false` creates a draft that sits outside the queue until someone adds or starts it.
|
|
210
210
|
*/
|
|
211
211
|
enqueue: z.ZodDefault<z.ZodBoolean>;
|
|
212
|
+
/**
|
|
213
|
+
* NOT-364: Linear issue attachment metadata captured by the From-Linear form
|
|
214
|
+
* at lookup time. The server snapshots every Linear-hosted file before the
|
|
215
|
+
* issue/queue rows are written (atomic: any download/limit failure refuses
|
|
216
|
+
* the whole create); external links are stored as metadata only. Ignored
|
|
217
|
+
* for non-Linear sources. Never a stored issue field — rows live in
|
|
218
|
+
* `issue_source_attachments`.
|
|
219
|
+
*/
|
|
220
|
+
linearAttachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
221
|
+
id: z.ZodString;
|
|
222
|
+
title: z.ZodString;
|
|
223
|
+
url: z.ZodString;
|
|
224
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
225
|
+
source: z.ZodOptional<z.ZodString>;
|
|
226
|
+
}, "strip", z.ZodTypeAny, {
|
|
227
|
+
id: string;
|
|
228
|
+
title: string;
|
|
229
|
+
url: string;
|
|
230
|
+
subtitle?: string | undefined;
|
|
231
|
+
source?: string | undefined;
|
|
232
|
+
}, {
|
|
233
|
+
id: string;
|
|
234
|
+
title: string;
|
|
235
|
+
url: string;
|
|
236
|
+
subtitle?: string | undefined;
|
|
237
|
+
source?: string | undefined;
|
|
238
|
+
}>, "many">>;
|
|
212
239
|
}, "strip", z.ZodTypeAny, {
|
|
213
240
|
repo: string;
|
|
214
241
|
title: string;
|
|
@@ -225,6 +252,13 @@ export declare const CreateIssueInput: z.ZodObject<{
|
|
|
225
252
|
externalId?: string | undefined;
|
|
226
253
|
externalLabel?: string | undefined;
|
|
227
254
|
externalUrl?: string | undefined;
|
|
255
|
+
linearAttachments?: {
|
|
256
|
+
id: string;
|
|
257
|
+
title: string;
|
|
258
|
+
url: string;
|
|
259
|
+
subtitle?: string | undefined;
|
|
260
|
+
source?: string | undefined;
|
|
261
|
+
}[] | undefined;
|
|
228
262
|
}, {
|
|
229
263
|
repo: string;
|
|
230
264
|
title: string;
|
|
@@ -241,6 +275,13 @@ export declare const CreateIssueInput: z.ZodObject<{
|
|
|
241
275
|
maxInfraAttempts?: number | undefined;
|
|
242
276
|
autoMerge?: boolean | undefined;
|
|
243
277
|
enqueue?: boolean | undefined;
|
|
278
|
+
linearAttachments?: {
|
|
279
|
+
id: string;
|
|
280
|
+
title: string;
|
|
281
|
+
url: string;
|
|
282
|
+
subtitle?: string | undefined;
|
|
283
|
+
source?: string | undefined;
|
|
284
|
+
}[] | undefined;
|
|
244
285
|
}>;
|
|
245
286
|
/** Wire/API body shape — defaults applied by `CreateIssueInput.parse` / repository. */
|
|
246
287
|
export type CreateIssueInput = z.input<typeof CreateIssueInput>;
|
|
@@ -528,18 +569,18 @@ export declare const IssueArtifact: z.ZodObject<{
|
|
|
528
569
|
issueId: string | null;
|
|
529
570
|
createdAt: string;
|
|
530
571
|
kind: string;
|
|
572
|
+
blobPath: string | null;
|
|
531
573
|
workerSessionId: string | null;
|
|
532
574
|
contentJson: string | null;
|
|
533
|
-
blobPath: string | null;
|
|
534
575
|
author: "human" | "system" | "agent";
|
|
535
576
|
}, {
|
|
536
577
|
id: string;
|
|
537
578
|
issueId: string | null;
|
|
538
579
|
createdAt: string;
|
|
539
580
|
kind: string;
|
|
581
|
+
blobPath: string | null;
|
|
540
582
|
workerSessionId: string | null;
|
|
541
583
|
contentJson: string | null;
|
|
542
|
-
blobPath: string | null;
|
|
543
584
|
author: "human" | "system" | "agent";
|
|
544
585
|
}>;
|
|
545
586
|
export type IssueArtifact = z.infer<typeof IssueArtifact>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { GitHubRepoInput } from "./github-repo.js";
|
|
3
|
+
import { LinearAttachment } from "./linear-intake.js";
|
|
3
4
|
import { ExecutionContractV1 } from "./execution-contract.js";
|
|
4
5
|
export const IssueStatus = z.enum([
|
|
5
6
|
"ready",
|
|
@@ -98,6 +99,15 @@ export const CreateIssueInput = z.object({
|
|
|
98
99
|
* `false` creates a draft that sits outside the queue until someone adds or starts it.
|
|
99
100
|
*/
|
|
100
101
|
enqueue: z.boolean().default(true),
|
|
102
|
+
/**
|
|
103
|
+
* NOT-364: Linear issue attachment metadata captured by the From-Linear form
|
|
104
|
+
* at lookup time. The server snapshots every Linear-hosted file before the
|
|
105
|
+
* issue/queue rows are written (atomic: any download/limit failure refuses
|
|
106
|
+
* the whole create); external links are stored as metadata only. Ignored
|
|
107
|
+
* for non-Linear sources. Never a stored issue field — rows live in
|
|
108
|
+
* `issue_source_attachments`.
|
|
109
|
+
*/
|
|
110
|
+
linearAttachments: z.array(LinearAttachment).optional(),
|
|
101
111
|
});
|
|
102
112
|
/** Pre-start (or parked-on-`needs_human`) edits only — the route enforces no active
|
|
103
113
|
* workflow instance; status/owner/intent/branch/SHA/PR stay coordinator-owned. */
|
|
@@ -133,3 +133,88 @@ export declare function resolveLinearRepoWithMappings(labels: readonly string[]
|
|
|
133
133
|
* identity and a manually entered one cannot drift.
|
|
134
134
|
*/
|
|
135
135
|
export declare function resolveLinearRepoLabels(labels: readonly string[] | undefined): LinearRepoResolution;
|
|
136
|
+
/**
|
|
137
|
+
* NOT-364: a Linear issue attachment as returned by the direct GraphQL query.
|
|
138
|
+
* `subtitle` and `source` are Linear's own optional metadata (present on link
|
|
139
|
+
* attachments that point at external documents, often absent on uploaded
|
|
140
|
+
* files). Files that exist only inside Linear *comments* never appear here —
|
|
141
|
+
* the query reads the issue's own `attachments` connection only.
|
|
142
|
+
*/
|
|
143
|
+
export declare const LinearAttachment: z.ZodObject<{
|
|
144
|
+
/** Stable Linear attachment id — the reconciliation key across reloads. */
|
|
145
|
+
id: z.ZodString;
|
|
146
|
+
/** Display name (`repro.tar.gz`) or link title. */
|
|
147
|
+
title: z.ZodString;
|
|
148
|
+
/** Download URL for hosted files (short-lived) or the external target for links. */
|
|
149
|
+
url: z.ZodString;
|
|
150
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
151
|
+
/** Linear's source metadata, coerced to a string by the adapter when needed. */
|
|
152
|
+
source: z.ZodOptional<z.ZodString>;
|
|
153
|
+
}, "strip", z.ZodTypeAny, {
|
|
154
|
+
id: string;
|
|
155
|
+
title: string;
|
|
156
|
+
url: string;
|
|
157
|
+
subtitle?: string | undefined;
|
|
158
|
+
source?: string | undefined;
|
|
159
|
+
}, {
|
|
160
|
+
id: string;
|
|
161
|
+
title: string;
|
|
162
|
+
url: string;
|
|
163
|
+
subtitle?: string | undefined;
|
|
164
|
+
source?: string | undefined;
|
|
165
|
+
}>;
|
|
166
|
+
export type LinearAttachment = z.infer<typeof LinearAttachment>;
|
|
167
|
+
/**
|
|
168
|
+
* NOT-364: Linear-hosted file uploads live under Linear's own upload hosts;
|
|
169
|
+
* everything else is an ordinary external link. Hosted files are snapshotted
|
|
170
|
+
* into Dealer-owned storage at import/reload so a queued Builder never needs
|
|
171
|
+
* the (expiring) Linear URL; links are metadata only and never downloaded.
|
|
172
|
+
*/
|
|
173
|
+
export declare function isLinearHostedAttachmentUrl(rawUrl: string): boolean;
|
|
174
|
+
/**
|
|
175
|
+
* NOT-364: a durable Dealer-side record of one Linear issue attachment.
|
|
176
|
+
* `kind: "file"` rows carry a Dealer-owned `blobPath` plus integrity fields;
|
|
177
|
+
* `kind: "link"` rows carry source metadata only and must never be presented
|
|
178
|
+
* as offline-available files.
|
|
179
|
+
*/
|
|
180
|
+
export declare const SourceAttachmentRecord: z.ZodObject<{
|
|
181
|
+
linearAttachmentId: z.ZodString;
|
|
182
|
+
kind: z.ZodEnum<["file", "link"]>;
|
|
183
|
+
title: z.ZodString;
|
|
184
|
+
/** Worktree-local safe basename for files — absent on links. */
|
|
185
|
+
safeFileName: z.ZodOptional<z.ZodString>;
|
|
186
|
+
/** Dealer-owned blob path for downloaded files — absent on links. */
|
|
187
|
+
blobPath: z.ZodOptional<z.ZodString>;
|
|
188
|
+
contentType: z.ZodOptional<z.ZodString>;
|
|
189
|
+
sizeBytes: z.ZodOptional<z.ZodNumber>;
|
|
190
|
+
sha256: z.ZodOptional<z.ZodString>;
|
|
191
|
+
/** The Linear (possibly expiring) URL for files, the external target for links. */
|
|
192
|
+
url: z.ZodString;
|
|
193
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
194
|
+
source: z.ZodOptional<z.ZodString>;
|
|
195
|
+
}, "strip", z.ZodTypeAny, {
|
|
196
|
+
kind: "link" | "file";
|
|
197
|
+
title: string;
|
|
198
|
+
url: string;
|
|
199
|
+
linearAttachmentId: string;
|
|
200
|
+
subtitle?: string | undefined;
|
|
201
|
+
source?: string | undefined;
|
|
202
|
+
safeFileName?: string | undefined;
|
|
203
|
+
blobPath?: string | undefined;
|
|
204
|
+
contentType?: string | undefined;
|
|
205
|
+
sizeBytes?: number | undefined;
|
|
206
|
+
sha256?: string | undefined;
|
|
207
|
+
}, {
|
|
208
|
+
kind: "link" | "file";
|
|
209
|
+
title: string;
|
|
210
|
+
url: string;
|
|
211
|
+
linearAttachmentId: string;
|
|
212
|
+
subtitle?: string | undefined;
|
|
213
|
+
source?: string | undefined;
|
|
214
|
+
safeFileName?: string | undefined;
|
|
215
|
+
blobPath?: string | undefined;
|
|
216
|
+
contentType?: string | undefined;
|
|
217
|
+
sizeBytes?: number | undefined;
|
|
218
|
+
sha256?: string | undefined;
|
|
219
|
+
}>;
|
|
220
|
+
export type SourceAttachmentRecord = z.infer<typeof SourceAttachmentRecord>;
|
|
@@ -178,3 +178,59 @@ export function resolveLinearRepoLabels(labels) {
|
|
|
178
178
|
return { status: "invalid", labels: [raw], error: err.message };
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* NOT-364: a Linear issue attachment as returned by the direct GraphQL query.
|
|
183
|
+
* `subtitle` and `source` are Linear's own optional metadata (present on link
|
|
184
|
+
* attachments that point at external documents, often absent on uploaded
|
|
185
|
+
* files). Files that exist only inside Linear *comments* never appear here —
|
|
186
|
+
* the query reads the issue's own `attachments` connection only.
|
|
187
|
+
*/
|
|
188
|
+
export const LinearAttachment = z.object({
|
|
189
|
+
/** Stable Linear attachment id — the reconciliation key across reloads. */
|
|
190
|
+
id: z.string(),
|
|
191
|
+
/** Display name (`repro.tar.gz`) or link title. */
|
|
192
|
+
title: z.string(),
|
|
193
|
+
/** Download URL for hosted files (short-lived) or the external target for links. */
|
|
194
|
+
url: z.string(),
|
|
195
|
+
subtitle: z.string().optional(),
|
|
196
|
+
/** Linear's source metadata, coerced to a string by the adapter when needed. */
|
|
197
|
+
source: z.string().optional(),
|
|
198
|
+
});
|
|
199
|
+
/**
|
|
200
|
+
* NOT-364: Linear-hosted file uploads live under Linear's own upload hosts;
|
|
201
|
+
* everything else is an ordinary external link. Hosted files are snapshotted
|
|
202
|
+
* into Dealer-owned storage at import/reload so a queued Builder never needs
|
|
203
|
+
* the (expiring) Linear URL; links are metadata only and never downloaded.
|
|
204
|
+
*/
|
|
205
|
+
export function isLinearHostedAttachmentUrl(rawUrl) {
|
|
206
|
+
let host;
|
|
207
|
+
try {
|
|
208
|
+
host = new URL(rawUrl).hostname.toLowerCase();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
return host === "uploads.linear.app" || host.endsWith(".uploads.linear.app") || host === "linear.app" || host.endsWith(".linear.app");
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* NOT-364: a durable Dealer-side record of one Linear issue attachment.
|
|
217
|
+
* `kind: "file"` rows carry a Dealer-owned `blobPath` plus integrity fields;
|
|
218
|
+
* `kind: "link"` rows carry source metadata only and must never be presented
|
|
219
|
+
* as offline-available files.
|
|
220
|
+
*/
|
|
221
|
+
export const SourceAttachmentRecord = z.object({
|
|
222
|
+
linearAttachmentId: z.string(),
|
|
223
|
+
kind: z.enum(["file", "link"]),
|
|
224
|
+
title: z.string(),
|
|
225
|
+
/** Worktree-local safe basename for files — absent on links. */
|
|
226
|
+
safeFileName: z.string().optional(),
|
|
227
|
+
/** Dealer-owned blob path for downloaded files — absent on links. */
|
|
228
|
+
blobPath: z.string().optional(),
|
|
229
|
+
contentType: z.string().optional(),
|
|
230
|
+
sizeBytes: z.number().int().nonnegative().optional(),
|
|
231
|
+
sha256: z.string().optional(),
|
|
232
|
+
/** The Linear (possibly expiring) URL for files, the external target for links. */
|
|
233
|
+
url: z.string(),
|
|
234
|
+
subtitle: z.string().optional(),
|
|
235
|
+
source: z.string().optional(),
|
|
236
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
4
|
import { LinearCandidate, LinearCandidatesPage, LinearIntakeConfigPatch, LinearIntakeConfigView, LinearIntakeMetadata, } from "./index.js";
|
|
5
|
-
import { LinearRepoResolution, extractRepoLabels, normalizeLinearIntakePickerPatch, normalizeMappingLabel, normalizeRepositoryMappings, resolveLinearRepoLabels, resolveLinearRepoWithMappings, } from "./linear-intake.js";
|
|
5
|
+
import { LinearAttachment, LinearRepoResolution, SourceAttachmentRecord, extractRepoLabels, isLinearHostedAttachmentUrl, normalizeLinearIntakePickerPatch, normalizeMappingLabel, normalizeRepositoryMappings, resolveLinearRepoLabels, resolveLinearRepoWithMappings, } from "./linear-intake.js";
|
|
6
6
|
test("one valid repo: label resolves to the canonical identity", () => {
|
|
7
7
|
const r = resolveLinearRepoLabels(["agent-dealer", "repo:github.com/not-so-fat/agent-dealer"]);
|
|
8
8
|
assert.equal(r.status, "resolved");
|
|
@@ -187,6 +187,60 @@ test("LinearIntakeConfigView and Patch reject deleted routing fields and empty s
|
|
|
187
187
|
const patch = LinearIntakeConfigPatch.parse({ assigneeMe: true, teamId: "t-1" });
|
|
188
188
|
assert.deepEqual(patch, { assigneeMe: true, teamId: "t-1" });
|
|
189
189
|
});
|
|
190
|
+
// NOT-364: Linear-hosted files vs external links, and the durable record shape.
|
|
191
|
+
test("isLinearHostedAttachmentUrl distinguishes Linear uploads from external links", () => {
|
|
192
|
+
assert.equal(isLinearHostedAttachmentUrl("https://uploads.linear.app/abc/repro.tar.gz"), true);
|
|
193
|
+
assert.equal(isLinearHostedAttachmentUrl("https://files.uploads.linear.app/x.zip"), true);
|
|
194
|
+
assert.equal(isLinearHostedAttachmentUrl("https://linear.app/issue/attachment/x"), true);
|
|
195
|
+
assert.equal(isLinearHostedAttachmentUrl("https://docs.google.com/document/d/x"), false);
|
|
196
|
+
assert.equal(isLinearHostedAttachmentUrl("https://github.com/acme/app/archive/refs/heads/main.zip"), false);
|
|
197
|
+
assert.equal(isLinearHostedAttachmentUrl("https://evil-linear.app.evil.com/x.tar.gz"), false);
|
|
198
|
+
assert.equal(isLinearHostedAttachmentUrl("not a url"), false);
|
|
199
|
+
});
|
|
200
|
+
test("LinearAttachment and SourceAttachmentRecord parse the attachment contract", () => {
|
|
201
|
+
const file = LinearAttachment.parse({
|
|
202
|
+
id: "att-1",
|
|
203
|
+
title: "repro.tar.gz",
|
|
204
|
+
url: "https://uploads.linear.app/abc/repro.tar.gz",
|
|
205
|
+
});
|
|
206
|
+
assert.equal(file.subtitle, undefined);
|
|
207
|
+
const link = LinearAttachment.parse({
|
|
208
|
+
id: "att-2",
|
|
209
|
+
title: "Design doc",
|
|
210
|
+
url: "https://docs.example.com/x",
|
|
211
|
+
subtitle: "Spec",
|
|
212
|
+
source: "google-docs",
|
|
213
|
+
});
|
|
214
|
+
assert.equal(link.subtitle, "Spec");
|
|
215
|
+
const record = SourceAttachmentRecord.parse({
|
|
216
|
+
linearAttachmentId: "att-1",
|
|
217
|
+
kind: "file",
|
|
218
|
+
title: "repro.tar.gz",
|
|
219
|
+
safeFileName: "repro.tar.gz",
|
|
220
|
+
blobPath: "/data/source-attachments/issue-1/repro.tar.gz",
|
|
221
|
+
contentType: "application/gzip",
|
|
222
|
+
sizeBytes: 12,
|
|
223
|
+
sha256: "abc",
|
|
224
|
+
url: "https://uploads.linear.app/abc/repro.tar.gz",
|
|
225
|
+
});
|
|
226
|
+
assert.equal(record.kind, "file");
|
|
227
|
+
// Attachments stay optional so older candidates still parse.
|
|
228
|
+
const bare = LinearCandidate.parse({
|
|
229
|
+
id: "uuid-1",
|
|
230
|
+
identifier: "NOT-1",
|
|
231
|
+
title: "t",
|
|
232
|
+
url: "https://linear.app/x/issue/NOT-1/t",
|
|
233
|
+
});
|
|
234
|
+
assert.equal(bare.attachments, undefined);
|
|
235
|
+
const withAttachments = LinearCandidate.parse({
|
|
236
|
+
id: "uuid-1",
|
|
237
|
+
identifier: "NOT-1",
|
|
238
|
+
title: "t",
|
|
239
|
+
url: "https://linear.app/x/issue/NOT-1/t",
|
|
240
|
+
attachments: [file, link],
|
|
241
|
+
});
|
|
242
|
+
assert.equal(withAttachments.attachments?.length, 2);
|
|
243
|
+
});
|
|
190
244
|
test("LinearCandidatesPage and LinearIntakeMetadata parse the picker contracts", () => {
|
|
191
245
|
const page = LinearCandidatesPage.parse({
|
|
192
246
|
candidates: [],
|