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
|
@@ -35,6 +35,18 @@ export function cleanupOrphanedWorkerMcpConfig() {
|
|
|
35
35
|
fs.rmSync(path.join(dir, entry), { recursive: true, force: true });
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* NOT-364: Dealer-owned blobs for Linear issue-source attachments. Content is
|
|
40
|
+
* addressed under `<dir>/<issueId>/`; rows in `issue_source_attachments` point
|
|
41
|
+
* here. Retained with the issue history — worktree copies are removed with
|
|
42
|
+
* normal worktree cleanup, but these blobs survive until the issue row itself
|
|
43
|
+
* is gone (and there is no hard-delete path: `closed` issues stay queryable).
|
|
44
|
+
*/
|
|
45
|
+
export function getSourceAttachmentsDir() {
|
|
46
|
+
const dir = path.join(getDataDir(), "source-attachments");
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
return dir;
|
|
49
|
+
}
|
|
38
50
|
/**
|
|
39
51
|
* Root for Dealer-managed repository clones and session worktrees (NOT-149).
|
|
40
52
|
* Defaults under AGENT_DEALER_HOME; override with AGENT_DEALER_EXECUTION_ROOT for a
|
|
@@ -9,7 +9,20 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Diagnostic evidence stays server-side: `evidence_ref` is a pointer
|
|
11
11
|
// (log path, probe id), never credentials and never a raw provider payload.
|
|
12
|
+
import { CapacityUnavailableDetail } from "@agent-dealer/shared";
|
|
12
13
|
import { getDb } from "../db/index.js";
|
|
14
|
+
/** Stored JSON → validated detail; anything malformed reads as absent. */
|
|
15
|
+
function parseUnavailableDetail(raw) {
|
|
16
|
+
if (!raw)
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
const parsed = CapacityUnavailableDetail.safeParse(JSON.parse(raw));
|
|
20
|
+
return parsed.success ? parsed.data : null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
13
26
|
function rowToSnapshot(row) {
|
|
14
27
|
return {
|
|
15
28
|
windowKey: row.window_key,
|
|
@@ -25,6 +38,7 @@ function rowToSnapshot(row) {
|
|
|
25
38
|
expiresAt: row.expires_at,
|
|
26
39
|
source: row.source,
|
|
27
40
|
unavailableReason: row.unavailable_reason,
|
|
41
|
+
unavailableDetail: parseUnavailableDetail(row.unavailable_detail),
|
|
28
42
|
criticalRole: row.critical_role,
|
|
29
43
|
};
|
|
30
44
|
}
|
|
@@ -39,13 +53,13 @@ export function recordCapacitySnapshots(runtime, windows) {
|
|
|
39
53
|
runtime, window_key, provider_bucket, duration_minutes, display_label,
|
|
40
54
|
used_value, used_unit, remaining_percent, reset_at, observed_at,
|
|
41
55
|
fresh_until, expires_at, source, unavailable_reason, evidence_ref,
|
|
42
|
-
critical_role
|
|
56
|
+
critical_role, unavailable_detail
|
|
43
57
|
)
|
|
44
58
|
VALUES (
|
|
45
59
|
@runtime, @window_key, @provider_bucket, @duration_minutes, @display_label,
|
|
46
60
|
@used_value, @used_unit, @remaining_percent, @reset_at, @observed_at,
|
|
47
61
|
@fresh_until, @expires_at, @source, @unavailable_reason, @evidence_ref,
|
|
48
|
-
@critical_role
|
|
62
|
+
@critical_role, @unavailable_detail
|
|
49
63
|
)
|
|
50
64
|
ON CONFLICT(runtime, window_key) DO UPDATE SET
|
|
51
65
|
provider_bucket = excluded.provider_bucket,
|
|
@@ -61,7 +75,8 @@ export function recordCapacitySnapshots(runtime, windows) {
|
|
|
61
75
|
source = excluded.source,
|
|
62
76
|
unavailable_reason = excluded.unavailable_reason,
|
|
63
77
|
evidence_ref = excluded.evidence_ref,
|
|
64
|
-
critical_role = excluded.critical_role
|
|
78
|
+
critical_role = excluded.critical_role,
|
|
79
|
+
unavailable_detail = excluded.unavailable_detail
|
|
65
80
|
`);
|
|
66
81
|
const run = getDb().transaction((rows) => {
|
|
67
82
|
for (const w of rows) {
|
|
@@ -82,6 +97,7 @@ export function recordCapacitySnapshots(runtime, windows) {
|
|
|
82
97
|
unavailable_reason: w.unavailableReason ?? null,
|
|
83
98
|
evidence_ref: w.evidenceRef ?? null,
|
|
84
99
|
critical_role: w.criticalRole ?? null,
|
|
100
|
+
unavailable_detail: w.unavailableDetail ? JSON.stringify(w.unavailableDetail) : null,
|
|
85
101
|
});
|
|
86
102
|
}
|
|
87
103
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// packages/server/src/repository/source-attachments.ts
|
|
2
|
+
//
|
|
3
|
+
// NOT-364: read/write side for `issue_source_attachments` — durable snapshots
|
|
4
|
+
// of a Linear issue's own file/link attachments, keyed by
|
|
5
|
+
// (Dealer issue id, Linear attachment id). Hosted files carry a Dealer-owned
|
|
6
|
+
// blob_path plus integrity fields; links carry source metadata only.
|
|
7
|
+
import { v4 as uuid } from "uuid";
|
|
8
|
+
import { getDb } from "../db/index.js";
|
|
9
|
+
function rowToRecord(row) {
|
|
10
|
+
const out = {
|
|
11
|
+
linearAttachmentId: row.linear_attachment_id,
|
|
12
|
+
kind: row.kind === "file" ? "file" : "link",
|
|
13
|
+
title: row.title,
|
|
14
|
+
url: row.url,
|
|
15
|
+
};
|
|
16
|
+
if (row.safe_file_name)
|
|
17
|
+
out.safeFileName = row.safe_file_name;
|
|
18
|
+
if (row.blob_path)
|
|
19
|
+
out.blobPath = row.blob_path;
|
|
20
|
+
if (row.content_type)
|
|
21
|
+
out.contentType = row.content_type;
|
|
22
|
+
if (row.size_bytes !== null)
|
|
23
|
+
out.sizeBytes = row.size_bytes;
|
|
24
|
+
if (row.sha256)
|
|
25
|
+
out.sha256 = row.sha256;
|
|
26
|
+
if (row.subtitle)
|
|
27
|
+
out.subtitle = row.subtitle;
|
|
28
|
+
if (row.source)
|
|
29
|
+
out.source = row.source;
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** Every source attachment for the issue, oldest first (stable prompt order). */
|
|
33
|
+
export function listSourceAttachments(issueId) {
|
|
34
|
+
const rows = getDb()
|
|
35
|
+
.prepare("SELECT * FROM issue_source_attachments WHERE issue_id = ? ORDER BY created_at ASC, rowid ASC")
|
|
36
|
+
.all(issueId);
|
|
37
|
+
return rows.map(rowToRecord);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* All-or-nothing replacement of an issue's attachment snapshot: deletes every
|
|
41
|
+
* existing row for the issue, then inserts the new set. Call inside the
|
|
42
|
+
* caller's transaction when the replacement must commit atomically with other
|
|
43
|
+
* writes (import text, reload text) — a throw leaves the prior rows untouched.
|
|
44
|
+
*/
|
|
45
|
+
export function replaceSourceAttachments(issueId, records) {
|
|
46
|
+
const db = getDb();
|
|
47
|
+
const now = new Date().toISOString();
|
|
48
|
+
db.prepare("DELETE FROM issue_source_attachments WHERE issue_id = ?").run(issueId);
|
|
49
|
+
const insert = db.prepare(`INSERT INTO issue_source_attachments
|
|
50
|
+
(id, issue_id, linear_attachment_id, kind, title, safe_file_name, blob_path,
|
|
51
|
+
content_type, size_bytes, sha256, url, subtitle, source, created_at, updated_at)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
53
|
+
for (const r of records) {
|
|
54
|
+
insert.run(uuid(), issueId, r.linearAttachmentId, r.kind, r.title, r.safeFileName ?? null, r.blobPath ?? null, r.contentType ?? null, r.sizeBytes ?? null, r.sha256 ?? null, r.url, r.subtitle ?? null, r.source ?? null, now, now);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -2,6 +2,7 @@ import { getHumanAction, listOpenHumanActions } from "../repository/human-action
|
|
|
2
2
|
import { resolveHumanActionAndAdvanceAsync } from "../coordinator/commands.js";
|
|
3
3
|
import { normalizeResolutionNote } from "../coordinator/human-resolution.js";
|
|
4
4
|
import { triggerIssueReflect, resolveReflectionInteractionAction } from "../coordinator/reflect-trigger.js";
|
|
5
|
+
import { isLinearMergeStaleAction, resolveLinearMergeStaleAction, } from "../coordinator/linear-merge-verify.js";
|
|
5
6
|
import { resolveOutboundDeliveryAction } from "../queue/approve-deliver.js";
|
|
6
7
|
export async function registerHumanActionRoutes(app) {
|
|
7
8
|
app.get("/api/human-actions", async () => listOpenHumanActions());
|
|
@@ -50,6 +51,22 @@ export async function registerHumanActionRoutes(app) {
|
|
|
50
51
|
return reply.status(deliveryResult.code).send({ error: deliveryResult.error });
|
|
51
52
|
return { runStatus: deliveryResult.runStatus, delivered: deliveryResult.delivered };
|
|
52
53
|
}
|
|
54
|
+
// NOT-362: the stale-source notice sits on an already-`done` issue with no
|
|
55
|
+
// active workflow to advance — the generic resolver would 409 on "no active
|
|
56
|
+
// workflow for this action" and the notice could never close. Resolve it
|
|
57
|
+
// directly (same shape as the reflection branch above); `recheck` re-runs
|
|
58
|
+
// the post-merge verification fire-and-forget.
|
|
59
|
+
if (action.actionType === "policy_escalation" && isLinearMergeStaleAction(action)) {
|
|
60
|
+
const staleResult = resolveLinearMergeStaleAction(id, resolvedBy, choice);
|
|
61
|
+
if (!staleResult.ok)
|
|
62
|
+
return reply.status(staleResult.code).send({ error: staleResult.error });
|
|
63
|
+
return {
|
|
64
|
+
issueStatus: staleResult.issueStatus,
|
|
65
|
+
nextWorkItemId: null,
|
|
66
|
+
instanceCompleted: false,
|
|
67
|
+
restarted: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
53
70
|
// Awaits undraft+merge when final_review:complete (NOT-102) — sync resolve alone would
|
|
54
71
|
// only park at AUTO_MERGE_INTENT and leave the PR draft.
|
|
55
72
|
const result = await resolveHumanActionAndAdvanceAsync(id, resolvedBy, choice, {
|