@songsid/agend 2.1.11-beta.1 → 2.1.11-beta.3

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.
Files changed (77) hide show
  1. package/dist/backend/kiro-engine-ledger.d.ts +36 -0
  2. package/dist/backend/kiro-engine-ledger.js +113 -0
  3. package/dist/backend/kiro-engine-ledger.js.map +1 -0
  4. package/dist/backend/kiro-kas-store.d.ts +11 -0
  5. package/dist/backend/kiro-kas-store.js +67 -0
  6. package/dist/backend/kiro-kas-store.js.map +1 -0
  7. package/dist/backend/kiro-v3-identity.d.ts +28 -0
  8. package/dist/backend/kiro-v3-identity.js +213 -0
  9. package/dist/backend/kiro-v3-identity.js.map +1 -0
  10. package/dist/backend/kiro.d.ts +7 -0
  11. package/dist/backend/kiro.js +34 -1
  12. package/dist/backend/kiro.js.map +1 -1
  13. package/dist/backend/opencode-permission.d.ts +49 -0
  14. package/dist/backend/opencode-permission.js +181 -0
  15. package/dist/backend/opencode-permission.js.map +1 -0
  16. package/dist/backend/opencode.d.ts +6 -0
  17. package/dist/backend/opencode.js +46 -2
  18. package/dist/backend/opencode.js.map +1 -1
  19. package/dist/backend/types.d.ts +15 -0
  20. package/dist/backend/types.js.map +1 -1
  21. package/dist/channel/adapters/discord.js +26 -25
  22. package/dist/channel/adapters/discord.js.map +1 -1
  23. package/dist/channel/ipc-bridge.d.ts +1 -0
  24. package/dist/channel/ipc-bridge.js +1 -1
  25. package/dist/channel/ipc-bridge.js.map +1 -1
  26. package/dist/channel/mcp-tools.js +2 -0
  27. package/dist/channel/mcp-tools.js.map +1 -1
  28. package/dist/cli.js +27 -6
  29. package/dist/cli.js.map +1 -1
  30. package/dist/command-table.d.ts +99 -0
  31. package/dist/command-table.js +125 -0
  32. package/dist/command-table.js.map +1 -0
  33. package/dist/daemon.d.ts +36 -0
  34. package/dist/daemon.js +165 -2
  35. package/dist/daemon.js.map +1 -1
  36. package/dist/delivery-outbox.d.ts +16 -0
  37. package/dist/delivery-outbox.js +55 -6
  38. package/dist/delivery-outbox.js.map +1 -1
  39. package/dist/e2e-isolation.d.ts +122 -0
  40. package/dist/e2e-isolation.js +386 -0
  41. package/dist/e2e-isolation.js.map +1 -0
  42. package/dist/fleet-manager.d.ts +24 -0
  43. package/dist/fleet-manager.js +301 -489
  44. package/dist/fleet-manager.js.map +1 -1
  45. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +56 -0
  46. package/dist/instance-lifecycle.d.ts +2 -0
  47. package/dist/instance-lifecycle.js +14 -0
  48. package/dist/instance-lifecycle.js.map +1 -1
  49. package/dist/kiro-engine-status.d.ts +71 -0
  50. package/dist/kiro-engine-status.js +68 -0
  51. package/dist/kiro-engine-status.js.map +1 -0
  52. package/dist/locale.js +16 -4
  53. package/dist/locale.js.map +1 -1
  54. package/dist/login-flows.js +10 -0
  55. package/dist/login-flows.js.map +1 -1
  56. package/dist/outbound-handlers.js +16 -1
  57. package/dist/outbound-handlers.js.map +1 -1
  58. package/dist/outbound-schemas.d.ts +3 -0
  59. package/dist/outbound-schemas.js +3 -0
  60. package/dist/outbound-schemas.js.map +1 -1
  61. package/dist/paths.d.ts +19 -0
  62. package/dist/paths.js +18 -5
  63. package/dist/paths.js.map +1 -1
  64. package/dist/tool-permissions.js +4 -2
  65. package/dist/tool-permissions.js.map +1 -1
  66. package/dist/topic-commands.js +3 -2
  67. package/dist/topic-commands.js.map +1 -1
  68. package/dist/ui/web-terminal/terminal-input.js +69 -0
  69. package/dist/ui/web-terminal/terminal.css +6 -0
  70. package/dist/ui/web-terminal/terminal.html +9 -1
  71. package/dist/ui/web-terminal/terminal.js +31 -2
  72. package/dist/update-check.d.ts +40 -2
  73. package/dist/update-check.js +101 -3
  74. package/dist/update-check.js.map +1 -1
  75. package/dist/web-terminal-http.js +1 -0
  76. package/dist/web-terminal-http.js.map +1 -1
  77. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ export type KiroUi = "legacy" | "tui" | "v3";
2
+ export interface KiroLaunchRecord {
3
+ at: string;
4
+ kiroVersion: string | null;
5
+ agendVersion: string;
6
+ ui: KiroUi;
7
+ flags: string[];
8
+ }
9
+ export interface KiroLedgerEntry {
10
+ workingDirectory: string;
11
+ /** The credential profile the instance launched under; null for the shared store. */
12
+ credentialProfile: string | null;
13
+ firstSeen: string;
14
+ lastLaunch: KiroLaunchRecord;
15
+ /** One row per change of kiro version, AgEnD version, UI or engine flags; newest last. */
16
+ history: KiroLaunchRecord[];
17
+ }
18
+ export type KiroLedger = Record<string, KiroLedgerEntry>;
19
+ export declare function kiroLedgerPath(agendHome?: string): string;
20
+ export declare function agendVersion(): string;
21
+ export declare function readKiroLedger(path?: string): KiroLedger;
22
+ /** Write via a temp file and rename; the temp file is removed whatever happens. Throws on failure. */
23
+ export declare function writeFileAtomic(path: string, data: string): void;
24
+ /** Read, change one entry, write back atomically. Returns false when the ledger could not be written. */
25
+ export declare function updateKiroLedger(instance: string, change: (entry: KiroLedgerEntry | undefined) => KiroLedgerEntry | undefined, path?: string): boolean;
26
+ export interface KiroLaunchFacts {
27
+ instance: string;
28
+ workingDirectory: string;
29
+ credentialProfile: string | null;
30
+ kiroVersion: string | null;
31
+ ui: KiroUi;
32
+ /** The UI/engine flags AgEnD pinned for this launch. */
33
+ flags: string[];
34
+ now?: Date;
35
+ }
36
+ export declare function recordKiroLaunch(facts: KiroLaunchFacts, path?: string): boolean;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Which kiro engine, kiro-cli version and AgEnD version each kiro instance
3
+ * has been launched with — the baseline the V1 → V3 migration works from.
4
+ *
5
+ * Kept in `<AGEND_HOME>/kiro-engine-ledger.json`, not the instance directory:
6
+ * `replace_instance` deletes that directory, and the migration needs to know
7
+ * what an instance ran on before it was replaced.
8
+ *
9
+ * It is a record, not a source of truth: writing it is best-effort, and a
10
+ * launch never fails because it could not be read or written. Which V3
11
+ * session an instance owns is NOT kept here (see kiro-v3-identity.ts), since
12
+ * that has to be durable and exclusive and this does not.
13
+ */
14
+ import { createRequire } from "node:module";
15
+ import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { randomBytes } from "node:crypto";
18
+ import { getAgendHome } from "../paths.js";
19
+ const HISTORY_LIMIT = 20;
20
+ export function kiroLedgerPath(agendHome = getAgendHome()) {
21
+ return join(agendHome, "kiro-engine-ledger.json");
22
+ }
23
+ let agendVersionCache;
24
+ export function agendVersion() {
25
+ if (agendVersionCache === undefined) {
26
+ try {
27
+ agendVersionCache = String(createRequire(import.meta.url)("../../package.json").version ?? "unknown");
28
+ }
29
+ catch {
30
+ agendVersionCache = "unknown";
31
+ }
32
+ }
33
+ return agendVersionCache;
34
+ }
35
+ const isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
36
+ const UIS = ["legacy", "tui", "v3"];
37
+ function isLaunchRecord(v) {
38
+ return isObject(v) && typeof v.at === "string"
39
+ && (v.kiroVersion === null || typeof v.kiroVersion === "string")
40
+ && typeof v.agendVersion === "string" && UIS.includes(v.ui)
41
+ && Array.isArray(v.flags) && v.flags.every(f => typeof f === "string");
42
+ }
43
+ /** An entry the code can read without guessing, down to every launch record; anything else is dropped. */
44
+ function isLedgerEntry(v) {
45
+ return isObject(v) && typeof v.workingDirectory === "string" && typeof v.firstSeen === "string"
46
+ && (v.credentialProfile === null || typeof v.credentialProfile === "string")
47
+ && isLaunchRecord(v.lastLaunch) && Array.isArray(v.history) && v.history.every(isLaunchRecord);
48
+ }
49
+ export function readKiroLedger(path = kiroLedgerPath()) {
50
+ try {
51
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
52
+ if (isObject(parsed))
53
+ return Object.fromEntries(Object.entries(parsed).filter(([, e]) => isLedgerEntry(e)));
54
+ }
55
+ catch { /* missing or damaged: start over */ }
56
+ return {};
57
+ }
58
+ /** Write via a temp file and rename; the temp file is removed whatever happens. Throws on failure. */
59
+ export function writeFileAtomic(path, data) {
60
+ const tmp = `${path}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
61
+ try {
62
+ writeFileSync(tmp, data, { mode: 0o600, flag: "wx" });
63
+ renameSync(tmp, path);
64
+ }
65
+ finally {
66
+ rmSync(tmp, { force: true });
67
+ }
68
+ }
69
+ function writeKiroLedger(ledger, path) {
70
+ writeFileAtomic(path, JSON.stringify(ledger, null, 2) + "\n");
71
+ }
72
+ /** Read, change one entry, write back atomically. Returns false when the ledger could not be written. */
73
+ export function updateKiroLedger(instance, change, path = kiroLedgerPath()) {
74
+ try {
75
+ const ledger = readKiroLedger(path);
76
+ const next = change(ledger[instance]);
77
+ if (next === undefined)
78
+ return true;
79
+ ledger[instance] = next;
80
+ writeKiroLedger(ledger, path);
81
+ return true;
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ function sameLaunch(a, b) {
88
+ return a.kiroVersion === b.kiroVersion && a.agendVersion === b.agendVersion && a.ui === b.ui
89
+ && a.flags.length === b.flags.length && a.flags.every((f, i) => f === b.flags[i]);
90
+ }
91
+ export function recordKiroLaunch(facts, path = kiroLedgerPath()) {
92
+ const record = {
93
+ at: (facts.now ?? new Date()).toISOString(),
94
+ kiroVersion: facts.kiroVersion,
95
+ agendVersion: agendVersion(),
96
+ ui: facts.ui,
97
+ flags: [...facts.flags],
98
+ };
99
+ return updateKiroLedger(facts.instance, entry => {
100
+ const history = entry?.history ?? [];
101
+ const last = history[history.length - 1];
102
+ const nextHistory = last && sameLaunch(last, record) ? history : [...history, record].slice(-HISTORY_LIMIT);
103
+ return {
104
+ ...entry,
105
+ workingDirectory: facts.workingDirectory,
106
+ credentialProfile: facts.credentialProfile,
107
+ firstSeen: entry?.firstSeen ?? record.at,
108
+ lastLaunch: record,
109
+ history: nextHistory,
110
+ };
111
+ }, path);
112
+ }
113
+ //# sourceMappingURL=kiro-engine-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kiro-engine-ledger.js","sourceRoot":"","sources":["../../src/backend/kiro-engine-ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface KasSession {
2
+ id: string;
3
+ /** Epoch ms from session.json, or null when absent or not a time. */
4
+ createdAt: number | null;
5
+ lastModifiedAt: number | null;
6
+ }
7
+ export declare function kiroHome(env?: NodeJS.ProcessEnv): string;
8
+ /** The bucket kiro files a working directory's V3 sessions under. */
9
+ export declare function kasBucket(workingDirectory: string): string;
10
+ /** This working directory's V3 sessions, most recently active first. Unreadable entries are skipped. */
11
+ export declare function listKasSessions(workingDirectory: string, env?: NodeJS.ProcessEnv): KasSession[];
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Kiro's V3 (KAS) session store, read-only.
3
+ *
4
+ * V3 keeps a conversation as a directory, not a sqlite row:
5
+ * <KIRO_HOME or ~/.kiro>/sessions/<bucket>/<session id>/{session.json, messages.jsonl}
6
+ * where <bucket> is the first 16 hex of SHA-256 of the working directory
7
+ * (checked against real buckets kiro-cli 2.27.1 wrote). Session ids are
8
+ * `sess_<uuid>` for sessions V3 started and `cli_<classic id>_<suffix>` for ones
9
+ * converted from a classic conversation.
10
+ *
11
+ * Unlike the classic store this is not under XDG_DATA_HOME, so a credential
12
+ * profile does not give an instance its own copy of it.
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ import { readdirSync, readFileSync, realpathSync } from "node:fs";
16
+ import { homedir } from "node:os";
17
+ import { join } from "node:path";
18
+ /** A timestamp kiro wrote, as an instant; anything that is not one is null (never "" or NaN). */
19
+ function instant(v) {
20
+ if (typeof v !== "string")
21
+ return null;
22
+ const ms = Date.parse(v);
23
+ return Number.isFinite(ms) ? ms : null;
24
+ }
25
+ export function kiroHome(env = process.env) {
26
+ return env.KIRO_HOME || join(homedir(), ".kiro");
27
+ }
28
+ /** The bucket kiro files a working directory's V3 sessions under. */
29
+ export function kasBucket(workingDirectory) {
30
+ let cwd = workingDirectory;
31
+ // kiro hashes its process cwd, which the kernel reports with symlinks resolved.
32
+ try {
33
+ cwd = realpathSync(workingDirectory);
34
+ }
35
+ catch { /* not there (yet): hash it as given */ }
36
+ return createHash("sha256").update(cwd).digest("hex").slice(0, 16);
37
+ }
38
+ /** This working directory's V3 sessions, most recently active first. Unreadable entries are skipped. */
39
+ export function listKasSessions(workingDirectory, env = process.env) {
40
+ const dir = join(kiroHome(env), "sessions", kasBucket(workingDirectory));
41
+ let names;
42
+ try {
43
+ names = readdirSync(dir);
44
+ }
45
+ catch {
46
+ return [];
47
+ }
48
+ const sessions = [];
49
+ for (const name of names) {
50
+ if (name.startsWith("."))
51
+ continue;
52
+ try {
53
+ const meta = JSON.parse(readFileSync(join(dir, name, "session.json"), "utf8"));
54
+ // The directory name is the id `--resume-id` takes (what ensure-session returns).
55
+ sessions.push({
56
+ id: name,
57
+ createdAt: instant(meta.createdAt),
58
+ lastModifiedAt: instant(meta.lastModifiedAt),
59
+ });
60
+ }
61
+ catch { /* not a session directory, or one being written */ }
62
+ }
63
+ // Compared as instants (offsets differ between writers); one with no time sorts last.
64
+ const latest = (s) => s.lastModifiedAt ?? s.createdAt ?? -Infinity;
65
+ return sessions.sort((a, b) => latest(b) - latest(a));
66
+ }
67
+ //# sourceMappingURL=kiro-kas-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kiro-kas-store.js","sourceRoot":"","sources":["../../src/backend/kiro-kas-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC,iGAAiG;AACjG,SAAS,OAAO,CAAC,CAAU;IACzB,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC3D,OAAO,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,gBAAwB;IAChD,IAAI,GAAG,GAAG,gBAAgB,CAAC;IAC3B,gFAAgF;IAChF,IAAI,CAAC;QAAC,GAAG,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,uCAAuC,CAAC,CAAC;IAC/F,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,eAAe,CAAC,gBAAwB,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC5F,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACzE,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QAAC,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;IACtD,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAA4B,CAAC;YAC1G,kFAAkF;YAClF,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,IAAI;gBACR,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;gBAClC,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC;aAC7C,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC,CAAC,mDAAmD,CAAC,CAAC;IACjE,CAAC;IACD,sFAAsF;IACtF,MAAM,MAAM,GAAG,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC;IAC/E,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,28 @@
1
+ /** The fresh start could not be recorded: launching anyway would let the next launch resume what this one gave up. */
2
+ export declare class KiroV3IdentityError extends Error {
3
+ constructor(detail: string);
4
+ }
5
+ export interface KiroV3ResumeOptions {
6
+ /** Start fresh: give up the session this instance owned. */
7
+ skipResume?: boolean;
8
+ agendHome?: string;
9
+ env?: NodeJS.ProcessEnv;
10
+ now?: Date;
11
+ }
12
+ export declare function resolveKiroV3Resume(instance: string, workingDirectory: string, credentialProfile: string | null, opts?: KiroV3ResumeOptions): string | null;
13
+ export type KiroV3Identity = {
14
+ kind: "none";
15
+ } | {
16
+ kind: "unreadable";
17
+ } | {
18
+ kind: "owned";
19
+ id: string;
20
+ claimHeld: boolean;
21
+ credentialProfile: string | null;
22
+ } | {
23
+ kind: "fresh";
24
+ since: string;
25
+ credentialProfile: string | null;
26
+ };
27
+ /** What an instance's V3 identity says right now, read-only (for kiro_engine_status). */
28
+ export declare function readKiroV3Identity(instance: string, agendHome?: string): KiroV3Identity;
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Which V3 session a V3 kiro instance owns, and so resumes.
3
+ *
4
+ * Kiro's V3 `--resume` takes the newest conversation in the working directory
5
+ * from ANY engine, and converts a classic one into a new V3 copy on every
6
+ * launch (#1141 kiro migration study). A V3 instance therefore resumes by id —
7
+ * and the id must be its own: never a sibling's running conversation, never
8
+ * one it gave up, never a guess. Two stores, both under `<AGEND_HOME>/kiro-v3`:
9
+ *
10
+ * claims/<session id> created exclusively ("wx") holding the owner's name
11
+ * and a newline, the newline written last: a claim
12
+ * without it is still being written (or was cut
13
+ * short) and belongs to nobody who can use it.
14
+ * Whoever created it owns the session; nobody else
15
+ * can take it over, and only the owner removes it.
16
+ * instances/<name>.json the instance's state, written only by its own
17
+ * launch: the session it owns, or a fresh-start mark
18
+ * (`id: null`, when, and which sessions already existed).
19
+ *
20
+ * A session is taken up only with evidence that this instance's own fresh
21
+ * launch made it: created (a valid instant) after the mark, absent from the
22
+ * mark's list, owned by nobody, and the only such session — with no other
23
+ * instance in the same directory waiting to take up its own. Anything less
24
+ * certain starts fresh again — and a fresh start is only made once it is
25
+ * on record: one that cannot be recorded refuses the launch, since the next
26
+ * launch would otherwise go back to the session this one gave up. Two V3
27
+ * instances in one working directory
28
+ * cannot tell their sessions apart and each keeps starting fresh: a known
29
+ * limit, chosen over handing one of them the other's conversation.
30
+ */
31
+ import { mkdirSync, openSync, closeSync, readdirSync, readFileSync, rmSync, writeSync } from "node:fs";
32
+ import { join } from "node:path";
33
+ import { getAgendHome } from "../paths.js";
34
+ import { writeFileAtomic } from "./kiro-engine-ledger.js";
35
+ import { kasBucket, listKasSessions } from "./kiro-kas-store.js";
36
+ /** Session ids and instance names become file names: only plain ones. */
37
+ const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,199}$/;
38
+ /** Never a valid instance name (it fails SAFE_NAME), so never mistaken for one. */
39
+ const INCOMPLETE = "\0incomplete";
40
+ function isState(v) {
41
+ if (!v || typeof v !== "object" || Array.isArray(v))
42
+ return false;
43
+ const s = v;
44
+ return typeof s.bucket === "string"
45
+ && (s.credentialProfile === null || typeof s.credentialProfile === "string")
46
+ && (s.id === null || (typeof s.id === "string" && SAFE_NAME.test(s.id)))
47
+ // A real instant: Date accepts only ±8.64e15 ms, and an out-of-range mark is no evidence of anything.
48
+ && typeof s.since === "number" && Number.isFinite(new Date(s.since).getTime())
49
+ && Array.isArray(s.known) && s.known.every(k => typeof k === "string");
50
+ }
51
+ /** The fresh start could not be recorded: launching anyway would let the next launch resume what this one gave up. */
52
+ export class KiroV3IdentityError extends Error {
53
+ constructor(detail) {
54
+ super(`AgEnD could not record this V3 instance's fresh start (${detail}); not launching, so the conversation it gave up is not resumed later`);
55
+ this.name = "KiroV3IdentityError";
56
+ }
57
+ }
58
+ export function resolveKiroV3Resume(instance, workingDirectory, credentialProfile, opts = {}) {
59
+ if (!SAFE_NAME.test(instance))
60
+ return null;
61
+ const root = join(opts.agendHome ?? getAgendHome(), "kiro-v3");
62
+ const claims = join(root, "claims");
63
+ const instances = join(root, "instances");
64
+ const statePath = join(instances, `${instance}.json`);
65
+ const bucket = kasBucket(workingDirectory);
66
+ const sessions = listKasSessions(workingDirectory, opts.env).filter(s => SAFE_NAME.test(s.id));
67
+ const readState = (path) => {
68
+ try {
69
+ const v = JSON.parse(readFileSync(path, "utf8"));
70
+ return isState(v) ? v : null;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ };
76
+ /** null: unclaimed. INCOMPLETE: a claim being written, or cut short — someone's, and not usable. */
77
+ const owner = (id) => {
78
+ let text;
79
+ try {
80
+ text = readFileSync(join(claims, id), "utf8");
81
+ }
82
+ catch (err) {
83
+ return err.code === "ENOENT" ? null : INCOMPLETE;
84
+ }
85
+ return text.endsWith("\n") ? text.slice(0, -1) : INCOMPLETE;
86
+ };
87
+ /** Claims an unclaimed session. True only once the whole owner line is on disk. */
88
+ const claim = (id) => {
89
+ const path = join(claims, id);
90
+ let fd;
91
+ try {
92
+ mkdirSync(claims, { recursive: true, mode: 0o700 });
93
+ fd = openSync(path, "wx", 0o600);
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ let complete = false;
99
+ try {
100
+ const line = Buffer.from(`${instance}\n`);
101
+ let offset = 0;
102
+ while (offset < line.length) {
103
+ const n = writeSync(fd, line, offset, line.length - offset);
104
+ if (n <= 0)
105
+ break;
106
+ offset += n;
107
+ }
108
+ complete = offset === line.length;
109
+ }
110
+ catch { /* the claim stays incomplete */ }
111
+ finally {
112
+ closeSync(fd);
113
+ }
114
+ // Ours (created exclusively), so ours to take back when it could not be finished.
115
+ if (!complete)
116
+ rmSync(path, { force: true });
117
+ return complete;
118
+ };
119
+ // Only the owner removes a claim, and no one can create it while it exists, so read-then-remove is safe.
120
+ const release = (id) => { if (owner(id) === instance)
121
+ rmSync(join(claims, id), { force: true }); };
122
+ const writeState = (state) => {
123
+ try {
124
+ mkdirSync(instances, { recursive: true, mode: 0o700 });
125
+ writeFileAtomic(statePath, JSON.stringify(state) + "\n");
126
+ return true;
127
+ }
128
+ catch {
129
+ return false;
130
+ }
131
+ };
132
+ const state = readState(statePath);
133
+ const startFresh = () => {
134
+ // Recorded first: until it is, the old state still says which session this instance holds.
135
+ if (!writeState({ bucket, credentialProfile, id: null, since: (opts.now ?? new Date()).getTime(), known: sessions.map(s => s.id) })) {
136
+ throw new KiroV3IdentityError(`cannot write ${statePath}`);
137
+ }
138
+ try {
139
+ if (state?.id)
140
+ release(state.id);
141
+ }
142
+ catch { /* the claim stays; it only keeps others off a session nobody resumes */ }
143
+ return null;
144
+ };
145
+ if (opts.skipResume || !state || state.bucket !== bucket || state.credentialProfile !== credentialProfile)
146
+ return startFresh();
147
+ if (state.id) {
148
+ const id = state.id;
149
+ // Resumed only while the claim is really held: a state alone never re-creates one.
150
+ return sessions.some(s => s.id === id) && owner(id) === instance ? id : startFresh();
151
+ }
152
+ // A fresh start is on record: take up the session that launch made, if — and only if — that is certain.
153
+ const known = new Set(state.known);
154
+ const candidates = sessions.filter(s => !known.has(s.id) && s.createdAt !== null && s.createdAt > state.since
155
+ && (owner(s.id) ?? instance) === instance);
156
+ const siblingWaiting = (() => {
157
+ let names;
158
+ // Siblings that cannot be listed are siblings that cannot be ruled out.
159
+ try {
160
+ names = readdirSync(instances);
161
+ }
162
+ catch {
163
+ return true;
164
+ }
165
+ return names.some(n => {
166
+ if (n === `${instance}.json` || !n.endsWith(".json"))
167
+ return false;
168
+ const other = readState(join(instances, n));
169
+ // An unreadable state in this directory could be a sibling waiting: not certain, so it counts.
170
+ return other === null || (other.bucket === bucket && other.id === null);
171
+ });
172
+ })();
173
+ if (candidates.length !== 1 || siblingWaiting)
174
+ return startFresh();
175
+ const pick = candidates[0].id;
176
+ // Already ours when an earlier launch claimed it but could not record that in its state.
177
+ if (owner(pick) !== instance && !claim(pick))
178
+ return startFresh();
179
+ // The claim is the ownership; if the state cannot record it, the next launch finds the claim again.
180
+ writeState({ ...state, id: pick });
181
+ return pick;
182
+ }
183
+ /** What an instance's V3 identity says right now, read-only (for kiro_engine_status). */
184
+ export function readKiroV3Identity(instance, agendHome = getAgendHome()) {
185
+ if (!SAFE_NAME.test(instance))
186
+ return { kind: "none" };
187
+ const root = join(agendHome, "kiro-v3");
188
+ let raw;
189
+ try {
190
+ raw = readFileSync(join(root, "instances", `${instance}.json`), "utf8");
191
+ }
192
+ catch (err) {
193
+ return err.code === "ENOENT" ? { kind: "none" } : { kind: "unreadable" };
194
+ }
195
+ let state;
196
+ try {
197
+ state = JSON.parse(raw);
198
+ }
199
+ catch {
200
+ return { kind: "unreadable" };
201
+ }
202
+ if (!isState(state))
203
+ return { kind: "unreadable" };
204
+ if (state.id === null)
205
+ return { kind: "fresh", since: new Date(state.since).toISOString(), credentialProfile: state.credentialProfile };
206
+ let claimHeld = false;
207
+ try {
208
+ claimHeld = readFileSync(join(root, "claims", state.id), "utf8") === `${instance}\n`;
209
+ }
210
+ catch { /* not held */ }
211
+ return { kind: "owned", id: state.id, claimHeld, credentialProfile: state.credentialProfile };
212
+ }
213
+ //# sourceMappingURL=kiro-v3-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kiro-v3-identity.js","sourceRoot":"","sources":["../../src/backend/kiro-v3-identity.ts"],"names":[],"mappings":"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"}
@@ -89,6 +89,13 @@ export declare function planKiroLaunch(ui: "legacy" | "tui" | "v3", compat: Kiro
89
89
  export declare function getCachedKiroCliCompatibility(binaryPath: string, run?: KiroProbeRunner): KiroCliCompatibility;
90
90
  /** Test-only reset for the process-level compatibility and warning memo. */
91
91
  export declare function resetKiroCompatibilityCacheForTests(): void;
92
+ /** The kiro-cli compatibility AgEnD last probed for a launch: read by kiro_engine_status, which never probes itself. */
93
+ export interface KiroCompatibilitySnapshot {
94
+ binaryPath: string;
95
+ compatibility: KiroCliCompatibility;
96
+ at: string;
97
+ }
98
+ export declare function lastKiroCompatibilitySnapshot(): KiroCompatibilitySnapshot | null;
92
99
  /** Startup budget for a `--resume` launch (60% first output, 40% ready). */
93
100
  export declare const KIRO_RESUME_STARTUP_BUDGET_MS = 60000;
94
101
  export declare class KiroBackend implements CliBackend {
@@ -8,6 +8,8 @@ import { UnsupportedCliError, resolveBinary, shellQuote, validateEffort, validat
8
8
  import { PIE_CLASS } from "../tui-glyphs.js";
9
9
  import { KIRO_EXPIRED_LOGIN_SCREEN } from "../login-flows.js";
10
10
  import { t } from "../locale.js";
11
+ import { recordKiroLaunch } from "./kiro-engine-ledger.js";
12
+ import { resolveKiroV3Resume } from "./kiro-v3-identity.js";
11
13
  // Kiro CLI feature gates. These are deliberately separate: the flags shipped
12
14
  // in different releases, so one broad "old Kiro" check would still crash some
13
15
  // supported versions with an unknown argument. Every row below was read from
@@ -485,6 +487,13 @@ export function resetKiroCompatibilityCacheForTests() {
485
487
  warnedUnsupportedEffortCacheKeys.clear();
486
488
  warnedVersionGateCacheKeys.clear();
487
489
  }
490
+ let lastKiroCompatibility = null;
491
+ export function lastKiroCompatibilitySnapshot() {
492
+ return lastKiroCompatibility;
493
+ }
494
+ function publishKiroCompatibility(binaryPath, compatibility) {
495
+ lastKiroCompatibility = { binaryPath, compatibility, at: new Date().toISOString() };
496
+ }
488
497
  /** Startup budget for a `--resume` launch (60% first output, 40% ready). */
489
498
  export const KIRO_RESUME_STARTUP_BUDGET_MS = 60_000;
490
499
  export class KiroBackend {
@@ -524,6 +533,7 @@ export class KiroBackend {
524
533
  const cached = cachedKiroCliCompatibility(this.binaryPath);
525
534
  this.compatibility = cached.compatibility;
526
535
  this.compatibilityCacheKey = cached.cacheKey;
536
+ publishKiroCompatibility(this.binaryPath, this.compatibility);
527
537
  }
528
538
  }
529
539
  requiresDeliveryEnterRetry() {
@@ -604,6 +614,8 @@ export class KiroBackend {
604
614
  this.compatibility = current.compatibility;
605
615
  this.compatibilityCacheKey = current.cacheKey;
606
616
  }
617
+ // What this launch is judged by, for kiro_engine_status to read without probing.
618
+ publishKiroCompatibility(this.binaryPath, this.compatibility);
607
619
  if (this.compatibility.source === "unknown") {
608
620
  // Neither --version nor chat --help answered: a missing or wedged binary,
609
621
  // or a loaded host timing out. Launching blind could start kiro on its
@@ -617,6 +629,15 @@ export class KiroBackend {
617
629
  for (const flag of plan.flags)
618
630
  cmd += ` ${flag}`;
619
631
  this.noteVersionGate();
632
+ const credentialProfile = resolveCredentialProfile(config.backendOptions);
633
+ recordKiroLaunch({
634
+ instance: config.instanceName,
635
+ workingDirectory: config.workingDirectory,
636
+ credentialProfile,
637
+ kiroVersion: this.compatibility.version ?? null,
638
+ ui,
639
+ flags: plan.flags,
640
+ });
620
641
  // Record what is actually being launched for the delivery gate (see
621
642
  // dropsEnterWhileBusy): the legacy prompt row exists only under
622
643
  // --legacy-ui; a binary from before 1.27 paints its own classic screen.
@@ -636,8 +657,20 @@ export class KiroBackend {
636
657
  // store, which is the same thing every brand-new instance does on its first
637
658
  // launch. Switching profiles also skips resume outright (crash-state
638
659
  // resumeDisabled), so this never fires against a store that just changed.
639
- if (!config.skipResume)
660
+ //
661
+ // V3 is the exception, and resumes only the session it owns, by id: its
662
+ // `--resume` takes the newest conversation in the directory from ANY
663
+ // engine and converts a classic one into a new V3 copy — on every launch.
664
+ // A skipped resume is recorded there too, so the session given up is not
665
+ // taken back on the next launch. See kiro-v3-identity.ts.
666
+ if (ui === "v3") {
667
+ const id = resolveKiroV3Resume(config.instanceName, config.workingDirectory, credentialProfile, { skipResume: config.skipResume });
668
+ if (id)
669
+ cmd += ` --resume-id ${shellQuote(id)}`;
670
+ }
671
+ else if (!config.skipResume) {
640
672
  cmd += " --resume";
673
+ }
641
674
  if (config.model) {
642
675
  const model = validateModel(config.model);
643
676
  warnIfModelMismatch("kiro-cli", model);