hanoman 0.9.40 → 0.9.41

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 (26) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/cli.js +3 -0
  3. package/dist/server.js +413 -273
  4. package/package.json +1 -1
  5. package/prisma/migrations/20261108090000_session_agent_session_id/migration.sql +2 -0
  6. package/prisma/schema.prisma +3 -0
  7. package/web/assets/{DalangHanomanScreen-WQ2B2G0F.js → DalangHanomanScreen-BBByhG99.js} +1 -1
  8. package/web/assets/{DocPreviewModal-DkuSjeL9.js → DocPreviewModal-CQY4THbu.js} +1 -1
  9. package/web/assets/{DocsWorkspace-BJ71xmQ_.js → DocsWorkspace-C6pT1E7m.js} +1 -1
  10. package/web/assets/{IdeScreen-Bc4gR6md.js → IdeScreen-BLFtTk_L.js} +1 -1
  11. package/web/assets/{LeadScreen-BmRdOMoJ.js → LeadScreen-jkt1heEV.js} +1 -1
  12. package/web/assets/{ReviewScreen-C83FuzRB.js → ReviewScreen-wUPVe5KU.js} +1 -1
  13. package/web/assets/{SchedulerScreen-BKnqWV7j.js → SchedulerScreen-DWz8dSHR.js} +1 -1
  14. package/web/assets/SettingsScreen-CK252KAu.js +62 -0
  15. package/web/assets/{TeamScreen-Dgrvomt0.js → TeamScreen-PNeLa4KQ.js} +1 -1
  16. package/web/assets/{TerminalScreen-D2KSQ13m.js → TerminalScreen-D_nXSBk6.js} +1 -1
  17. package/web/assets/{TriageScreen-DrPERvDs.js → TriageScreen-Bntfin0b.js} +1 -1
  18. package/web/assets/{VpsScreen-7sDyg5MI.js → VpsScreen-DugFaqzS.js} +1 -1
  19. package/web/assets/fields-ADsZ83ci.js +1 -0
  20. package/web/assets/{index-NoTUDPfb.js → index-BIwgmLXG.js} +3 -3
  21. package/web/assets/{live-B4ZwQ4kB.js → live-BIDo2oKi.js} +1 -1
  22. package/web/assets/{sched-lead-shared-CZ0_L34F.js → sched-lead-shared-BGQiQkHX.js} +1 -1
  23. package/web/assets/{settings-rebase-DwTIHvhF.js → settings-rebase-CF06reBv.js} +1 -1
  24. package/web/index.html +1 -1
  25. package/web/assets/SettingsScreen-FrgI5pzO.js +0 -62
  26. package/web/assets/fields-BNhNDKOP.js +0 -1
package/dist/server.js CHANGED
@@ -5798,6 +5798,9 @@ var init_entities = __esm({
5798
5798
  // SPEC-184
5799
5799
  notifyDecisionTelegram: external_exports.boolean().default(true),
5800
5800
  // ADR-0226 · dorong "menunggu keputusan" ke Telegram
5801
+ // ADR-0235 · saklar tunggal auto-resume SEMUA sesi (backlog + bebas) yang terputus paksa (reboot/crash).
5802
+ // Default MATI — mengamandemen ADR-0169 (dulu selalu nyala untuk backlog).
5803
+ autoResumeSessions: external_exports.boolean().default(false),
5801
5804
  agentAccessEnabled: external_exports.boolean().default(false),
5802
5805
  // SPEC-257 · master switch akses AI agent
5803
5806
  scheduler: zScheduler.default(SCHEDULER_DEFAULTS),
@@ -21749,10 +21752,29 @@ var init_session_id = __esm({
21749
21752
  }
21750
21753
  });
21751
21754
 
21755
+ // src/services/claude-conversation.ts
21756
+ import { existsSync as existsSync8, readdirSync as readdirSync8 } from "node:fs";
21757
+ import { homedir as homedir4 } from "node:os";
21758
+ import { join as join13 } from "node:path";
21759
+ function claudeConversationExists(uuid, configDir) {
21760
+ if (!/^[0-9a-f-]{36}$/i.test(uuid)) return false;
21761
+ const projects = join13(configDir ?? process.env.CLAUDE_CONFIG_DIR ?? join13(homedir4(), ".claude"), "projects");
21762
+ try {
21763
+ return readdirSync8(projects).some((slug2) => existsSync8(join13(projects, slug2, `${uuid}.jsonl`)));
21764
+ } catch {
21765
+ return false;
21766
+ }
21767
+ }
21768
+ var init_claude_conversation = __esm({
21769
+ "src/services/claude-conversation.ts"() {
21770
+ "use strict";
21771
+ }
21772
+ });
21773
+
21752
21774
  // src/services/antigravity-env-bootstrap.ts
21753
21775
  import * as fs from "node:fs";
21754
21776
  import { createHash as createHash6 } from "node:crypto";
21755
- import { join as join13, dirname as dirname8, basename as basename5 } from "node:path";
21777
+ import { join as join14, dirname as dirname8, basename as basename5 } from "node:path";
21756
21778
  import { tmpdir as tmpdir3 } from "node:os";
21757
21779
  function consume(receipt, io, hashOf2, parent, paths3) {
21758
21780
  if (!receipt || typeof receipt.directory !== "string" || paths3.dirname(receipt.directory) !== parent || !paths3.basename(receipt.directory).startsWith("hanoman-ag-env-")) return;
@@ -21768,12 +21790,12 @@ function consume(receipt, io, hashOf2, parent, paths3) {
21768
21790
  }
21769
21791
  function cleanupAntigravityEnvBootstrap(receipt) {
21770
21792
  const parent = receipt && typeof receipt === "object" && "parent" in receipt && typeof receipt.parent === "string" ? receipt.parent : tmpdir3();
21771
- consume(receipt, fs, hashOf, parent, { join: join13, dirname: dirname8, basename: basename5 });
21793
+ consume(receipt, fs, hashOf, parent, { join: join14, dirname: dirname8, basename: basename5 });
21772
21794
  }
21773
21795
  function createAntigravityEnvBootstrap(command, env) {
21774
- const directory = fs.mkdtempSync(join13(tmpdir3(), "hanoman-ag-env-"));
21796
+ const directory = fs.mkdtempSync(join14(tmpdir3(), "hanoman-ag-env-"));
21775
21797
  fs.chmodSync(directory, 448);
21776
- const path = join13(directory, "launch.sh"), metadata = join13(directory, "receipt.json");
21798
+ const path = join14(directory, "launch.sh"), metadata = join14(directory, "receipt.json");
21777
21799
  const cleaner = `const fs=require('node:fs'),{createHash}=require('node:crypto'),{join,dirname,basename}=require('node:path'); (${consume.toString()})(JSON.parse(fs.readFileSync(process.argv[1],'utf8')),fs,b=>createHash('sha256').update(b).digest('hex'),${JSON.stringify(dirname8(directory))},require('node:path'));`;
21778
21800
  const exports = ["GEMINI_API_KEY", "GOOGLE_GEMINI_BASE_URL"].map((key) => env[key] === void 0 ? `unset ${key}` : `export ${key}=${quote(env[key])}`);
21779
21801
  const content = `unset GEMINI_API_KEY GOOGLE_GEMINI_BASE_URL CLAUDE_CODE_OAUTH_TOKEN ANTHROPIC_API_KEY
@@ -21802,9 +21824,9 @@ var init_antigravity_env_bootstrap = __esm({
21802
21824
  });
21803
21825
 
21804
21826
  // src/services/secret-box.ts
21805
- import { chmodSync as chmodSync6, existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
21827
+ import { chmodSync as chmodSync6, existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
21806
21828
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
21807
- import { dirname as dirname9, join as join14 } from "node:path";
21829
+ import { dirname as dirname9, join as join15 } from "node:path";
21808
21830
  function isEncrypted(value) {
21809
21831
  return value.startsWith(ENC_PREFIX);
21810
21832
  }
@@ -21836,7 +21858,7 @@ function fromEnv(raw) {
21836
21858
  return null;
21837
21859
  }
21838
21860
  function secretKeyPath() {
21839
- return join14(resolveHome(), "secret.key");
21861
+ return join15(resolveHome(), "secret.key");
21840
21862
  }
21841
21863
  function secretKey() {
21842
21864
  if (cached) return cached;
@@ -21848,7 +21870,7 @@ function secretKey() {
21848
21870
  return key2;
21849
21871
  }
21850
21872
  const path = secretKeyPath();
21851
- if (existsSync8(path)) {
21873
+ if (existsSync9(path)) {
21852
21874
  const key2 = Buffer.from(readFileSync10(path, "utf8").trim(), "base64url");
21853
21875
  if (key2.length !== KEY_BYTES) throw new Error(`kunci di ${path} rusak \u2014 panjangnya bukan 32 byte`);
21854
21876
  cached = key2;
@@ -21972,12 +21994,12 @@ var init_antigravity_runtime = __esm({
21972
21994
  });
21973
21995
 
21974
21996
  // src/runner/deps.ts
21975
- import { existsSync as existsSync9 } from "node:fs";
21976
- import { dirname as dirname10, join as join15 } from "node:path";
21997
+ import { existsSync as existsSync10 } from "node:fs";
21998
+ import { dirname as dirname10, join as join16 } from "node:path";
21977
21999
  function repoRootFrom(startDir) {
21978
22000
  let dir2 = startDir;
21979
22001
  for (let i = 0; i < 8; i++) {
21980
- if (existsSync9(join15(dir2, "pnpm-workspace.yaml"))) return dir2;
22002
+ if (existsSync10(join16(dir2, "pnpm-workspace.yaml"))) return dir2;
21981
22003
  const parent = dirname10(dir2);
21982
22004
  if (parent === dir2) break;
21983
22005
  dir2 = parent;
@@ -21994,19 +22016,19 @@ var init_deps = __esm({
21994
22016
 
21995
22017
  // src/services/bundled-skills.ts
21996
22018
  import { createHash as createHash7 } from "node:crypto";
21997
- import { chmodSync as chmodSync7, cpSync as cpSync3, existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync11, readdirSync as readdirSync8, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync3, writeFileSync as writeFileSync11 } from "node:fs";
21998
- import { join as join16, relative as relative5, sep as sep6 } from "node:path";
22019
+ import { chmodSync as chmodSync7, cpSync as cpSync3, existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync11, readdirSync as readdirSync9, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync3, writeFileSync as writeFileSync11 } from "node:fs";
22020
+ import { join as join17, relative as relative5, sep as sep6 } from "node:path";
21999
22021
  import { fileURLToPath as fileURLToPath3 } from "node:url";
22000
22022
  function bundledSkillsDir() {
22001
22023
  const packed = packagedDir();
22002
- return existsSync10(packed) ? packed : join16(repoRoot(moduleDir()), "server", "skills");
22024
+ return existsSync11(packed) ? packed : join17(repoRoot(moduleDir()), "server", "skills");
22003
22025
  }
22004
22026
  function filesUnder(dir2) {
22005
22027
  const out4 = [];
22006
22028
  const walk = (d) => {
22007
- for (const n2 of readdirSync8(d).sort()) {
22029
+ for (const n2 of readdirSync9(d).sort()) {
22008
22030
  if (n2 === BUNDLED_MARKER || n2 === ".DS_Store") continue;
22009
- const full = join16(d, n2);
22031
+ const full = join17(d, n2);
22010
22032
  if (statSync3(full).isDirectory()) walk(full);
22011
22033
  else out4.push(full);
22012
22034
  }
@@ -22026,7 +22048,7 @@ function contentSha(dir2) {
22026
22048
  }
22027
22049
  function markerSha(dir2) {
22028
22050
  try {
22029
- const parsed = JSON.parse(readFileSync11(join16(dir2, BUNDLED_MARKER), "utf8"));
22051
+ const parsed = JSON.parse(readFileSync11(join17(dir2, BUNDLED_MARKER), "utf8"));
22030
22052
  const sha2 = parsed?.sha;
22031
22053
  return typeof sha2 === "string" ? sha2 : null;
22032
22054
  } catch {
@@ -22039,7 +22061,7 @@ function install(src, dest, sha2) {
22039
22061
  cpSync3(src, tmp, { recursive: true });
22040
22062
  for (const f of filesUnder(tmp))
22041
22063
  if (relative5(tmp, f).split(sep6)[0] === "scripts") chmodSync7(f, statSync3(f).mode | 493);
22042
- writeFileSync11(join16(tmp, BUNDLED_MARKER), `${JSON.stringify({ sha: sha2, seededAt: (/* @__PURE__ */ new Date()).toISOString() })}
22064
+ writeFileSync11(join17(tmp, BUNDLED_MARKER), `${JSON.stringify({ sha: sha2, seededAt: (/* @__PURE__ */ new Date()).toISOString() })}
22043
22065
  `);
22044
22066
  rmSync6(dest, { recursive: true, force: true });
22045
22067
  renameSync4(tmp, dest);
@@ -22053,10 +22075,10 @@ function seedBundledSkills(hanomanHome, o = {}) {
22053
22075
  const root = hanomanSkillsDir(hanomanHome);
22054
22076
  for (const name2 of names) {
22055
22077
  try {
22056
- const src = join16(source, name2);
22057
- const dest = join16(root, name2);
22078
+ const src = join17(source, name2);
22079
+ const dest = join17(root, name2);
22058
22080
  const bundleSha = contentSha(src);
22059
- if (!existsSync10(dest)) {
22081
+ if (!existsSync11(dest)) {
22060
22082
  mkdirSync9(root, { recursive: true });
22061
22083
  install(src, dest, bundleSha);
22062
22084
  out4.seeded.push(name2);
@@ -22089,24 +22111,24 @@ var init_bundled_skills = __esm({
22089
22111
  skillDirsIn = (source) => {
22090
22112
  let names;
22091
22113
  try {
22092
- names = readdirSync8(source);
22114
+ names = readdirSync9(source);
22093
22115
  } catch {
22094
22116
  return [];
22095
22117
  }
22096
- return names.filter((n2) => !n2.startsWith(".") && existsSync10(join16(source, n2, "SKILL.md"))).sort();
22118
+ return names.filter((n2) => !n2.startsWith(".") && existsSync11(join17(source, n2, "SKILL.md"))).sort();
22097
22119
  };
22098
- isBundledCopy = (dir2) => existsSync10(join16(dir2, BUNDLED_MARKER));
22120
+ isBundledCopy = (dir2) => existsSync11(join17(dir2, BUNDLED_MARKER));
22099
22121
  }
22100
22122
  });
22101
22123
 
22102
22124
  // src/services/skill-inject.ts
22103
22125
  import { execFileSync as execFileSync4 } from "node:child_process";
22104
22126
  import { createHash as createHash8 } from "node:crypto";
22105
- import { appendFileSync as appendFileSync2, existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readlinkSync as readlinkSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "node:fs";
22106
- import { isAbsolute as isAbsolute8, join as join17 } from "node:path";
22127
+ import { appendFileSync as appendFileSync2, existsSync as existsSync12, lstatSync as lstatSync7, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readlinkSync as readlinkSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "node:fs";
22128
+ import { isAbsolute as isAbsolute8, join as join18 } from "node:path";
22107
22129
  function excludeFile(repoDir) {
22108
22130
  const common = execFileSync4("git", ["rev-parse", "--git-common-dir"], { cwd: repoDir, env: agentEnvironment(null, process.env), encoding: "utf8" }).trim();
22109
- return join17(isAbsolute8(common) ? common : join17(repoDir, common), "info", "exclude");
22131
+ return join18(isAbsolute8(common) ? common : join18(repoDir, common), "info", "exclude");
22110
22132
  }
22111
22133
  function ownedSkills(tempDir) {
22112
22134
  try {
@@ -22141,11 +22163,11 @@ function injectGlobalSkills(o) {
22141
22163
  const todo = global2.filter((s2) => !taken.has(s2.name) && !(userOwned.has(s2.name) && isBundledCopy(s2.dir)));
22142
22164
  if (!todo.length) return out4;
22143
22165
  if (o.agent === "claude") {
22144
- const root = join17(o.tempDir, "skills-root");
22166
+ const root = join18(o.tempDir, "skills-root");
22145
22167
  try {
22146
- mkdirSync10(join17(root, ".claude", "skills"), { recursive: true });
22168
+ mkdirSync10(join18(root, ".claude", "skills"), { recursive: true });
22147
22169
  for (const s2 of todo) {
22148
- const link2 = join17(root, ".claude", "skills", s2.name);
22170
+ const link2 = join18(root, ".claude", "skills", s2.name);
22149
22171
  if (!lstatSync7(link2, { throwIfNoEntry: false })) symlinkSync(s2.dir, link2);
22150
22172
  out4.injected.push(s2.name);
22151
22173
  }
@@ -22166,19 +22188,19 @@ function injectGlobalSkills(o) {
22166
22188
  let ownedLink;
22167
22189
  let ownedPair = "";
22168
22190
  try {
22169
- const link2 = join17(o.cwd, ".agents", "skills", s2.name);
22191
+ const link2 = join18(o.cwd, ".agents", "skills", s2.name);
22170
22192
  if (o.agent === "antigravity") {
22171
- for (const dir2 of [join17(o.cwd, ".agents"), join17(o.cwd, ".agents", "skills")]) {
22193
+ for (const dir2 of [join18(o.cwd, ".agents"), join18(o.cwd, ".agents", "skills")]) {
22172
22194
  const st2 = lstatSync7(dir2, { throwIfNoEntry: false });
22173
22195
  if (st2 && (!st2.isDirectory() || st2.isSymbolicLink())) throw new Error("akar skill bukan direktori aman");
22174
22196
  }
22175
22197
  }
22176
22198
  if (lstatSync7(link2, { throwIfNoEntry: false })) continue;
22177
- mkdirSync10(join17(o.cwd, ".agents", "skills"), { recursive: true });
22199
+ mkdirSync10(join18(o.cwd, ".agents", "skills"), { recursive: true });
22178
22200
  symlinkSync(s2.dir, link2);
22179
22201
  ownedLink = link2;
22180
- mkdirSync10(join17(exclude, ".."), { recursive: true });
22181
- const cur = existsSync11(exclude) ? readFileSync12(exclude, "utf8") : "";
22202
+ mkdirSync10(join18(exclude, ".."), { recursive: true });
22203
+ const cur = existsSync12(exclude) ? readFileSync12(exclude, "utf8") : "";
22182
22204
  const line = excludeLine(s2.name);
22183
22205
  const lines2 = cur.split("\n");
22184
22206
  const hasOperatorLine = lines2.some((entry2, index) => entry2 === line && !lines2[index - 1]?.startsWith(`${INJECT_MARKER}:antigravity:`));
@@ -22221,7 +22243,7 @@ ${line}
22221
22243
  function unexcludeInjected(repoDir, name2) {
22222
22244
  try {
22223
22245
  const f = excludeFile(repoDir);
22224
- if (!existsSync11(f)) return;
22246
+ if (!existsSync12(f)) return;
22225
22247
  const line = excludeLine(name2);
22226
22248
  writeFileSync12(f, readFileSync12(f, "utf8").split("\n").filter((l) => l !== line).join("\n"));
22227
22249
  } catch {
@@ -22235,31 +22257,31 @@ var init_skill_inject = __esm({
22235
22257
  init_bundled_skills();
22236
22258
  INJECT_MARKER = "# hanoman:skill-inject";
22237
22259
  excludeLine = (name2) => `/.agents/skills/${name2}`;
22238
- manifestPath = (tempDir) => join17(tempDir, "antigravity-skills.json");
22260
+ manifestPath = (tempDir) => join18(tempDir, "antigravity-skills.json");
22239
22261
  }
22240
22262
  });
22241
22263
 
22242
22264
  // src/services/antigravity-trust.ts
22243
22265
  import { randomUUID as randomUUID2 } from "node:crypto";
22244
22266
  import { closeSync as closeSync2, lstatSync as lstatSync8, mkdirSync as mkdirSync11, openSync as openSync2, readFileSync as readFileSync13, realpathSync as realpathSync6, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "node:fs";
22245
- import { homedir as homedir4 } from "node:os";
22246
- import { join as join18 } from "node:path";
22267
+ import { homedir as homedir5 } from "node:os";
22268
+ import { join as join19 } from "node:path";
22247
22269
  function safeOwned(path, directory) {
22248
22270
  const st2 = lstatSync8(path);
22249
22271
  if (st2.isSymbolicLink() || (directory ? !st2.isDirectory() : !st2.isFile()) || process.getuid && st2.uid !== process.getuid()) throw new Error("unsafe path");
22250
22272
  }
22251
- function ensureAntigravityTrust(cwd, home3 = homedir4()) {
22273
+ function ensureAntigravityTrust(cwd, home3 = homedir5()) {
22252
22274
  let temp;
22253
22275
  let fd;
22254
22276
  try {
22255
22277
  const canonical = realpathSync6(cwd);
22256
22278
  mkdirSync11(home3, { recursive: true });
22257
22279
  const root = realpathSync6(home3);
22258
- for (const dir2 of [join18(root, ".gemini"), join18(root, ".gemini", "antigravity-cli")]) {
22280
+ for (const dir2 of [join19(root, ".gemini"), join19(root, ".gemini", "antigravity-cli")]) {
22259
22281
  if (!lstatSync8(dir2, { throwIfNoEntry: false })) mkdirSync11(dir2, { mode: 448 });
22260
22282
  safeOwned(dir2, true);
22261
22283
  }
22262
- const file = join18(root, ".gemini", "antigravity-cli", "settings.json");
22284
+ const file = join19(root, ".gemini", "antigravity-cli", "settings.json");
22263
22285
  const original = lstatSync8(file, { throwIfNoEntry: false });
22264
22286
  let settings = {};
22265
22287
  if (original) {
@@ -22333,14 +22355,14 @@ var init_local_binding = __esm({
22333
22355
  });
22334
22356
 
22335
22357
  // src/services/skill-library.ts
22336
- import { join as join19 } from "node:path";
22337
- import { existsSync as existsSync12 } from "node:fs";
22358
+ import { join as join20 } from "node:path";
22359
+ import { existsSync as existsSync13 } from "node:fs";
22338
22360
  async function libraryAll() {
22339
22361
  const global2 = scanGlobalSkills(opts());
22340
22362
  const projects = await prisma.project.findMany({ orderBy: { name: "asc" }, select: { id: true, name: true } });
22341
22363
  const groups = await Promise.all(projects.map(async (p3) => {
22342
22364
  const repo = await resolveRepoDir(p3.id);
22343
- if (!repo || !existsSync12(repo)) return { projectId: p3.id, name: p3.name, skills: [], error: "repo tidak ditemukan" };
22365
+ if (!repo || !existsSync13(repo)) return { projectId: p3.id, name: p3.name, skills: [], error: "repo tidak ditemukan" };
22344
22366
  return { projectId: p3.id, name: p3.name, skills: scanProjectSkills(p3.id, repo, opts()) };
22345
22367
  }));
22346
22368
  return { global: global2, projects: groups };
@@ -22363,17 +22385,17 @@ async function targetParent(layer, source, projectId) {
22363
22385
  if (layer === "user") {
22364
22386
  const s2 = source ?? ".claude";
22365
22387
  if (!USER_SOURCES.includes(s2)) throw new SkillError(400, "source user harus .claude/.codex/.agents/.gemini/antigravity-cli");
22366
- if (s2 === ".agents") return join19(agentsSkillHome(process.env), "skills");
22367
- if (s2 === ".gemini/antigravity-cli") return join19(agentSkillHome("antigravity", process.env), "skills");
22368
- return join19(agentSkillHome(s2 === ".codex" ? "codex" : "claude", process.env), "skills");
22388
+ if (s2 === ".agents") return join20(agentsSkillHome(process.env), "skills");
22389
+ if (s2 === ".gemini/antigravity-cli") return join20(agentSkillHome("antigravity", process.env), "skills");
22390
+ return join20(agentSkillHome(s2 === ".codex" ? "codex" : "claude", process.env), "skills");
22369
22391
  }
22370
22392
  if (layer === "project") {
22371
22393
  if (!projectId) throw new SkillError(400, "projectId wajib untuk lapis project");
22372
22394
  const s2 = source ?? ".claude";
22373
22395
  if (!PROJECT_SOURCES.includes(s2)) throw new SkillError(400, "source project harus .claude/.agents/.codex");
22374
22396
  const repo = await resolveRepoDir(projectId);
22375
- if (!repo || !existsSync12(repo)) throw new SkillError(404, "repo project tidak ditemukan");
22376
- return join19(repo, s2, "skills");
22397
+ if (!repo || !existsSync13(repo)) throw new SkillError(404, "repo project tidak ditemukan");
22398
+ return join20(repo, s2, "skills");
22377
22399
  }
22378
22400
  throw new SkillError(400, "layer harus hanoman, user, atau project");
22379
22401
  }
@@ -48487,7 +48509,7 @@ var init_safe_outbound_request = __esm({
48487
48509
  // src/services/uploads.ts
48488
48510
  import { randomUUID as randomUUID3 } from "node:crypto";
48489
48511
  import { mkdir, writeFile, readFile as readFile2, unlink, rm } from "node:fs/promises";
48490
- import { join as join20, resolve as resolve9 } from "node:path";
48512
+ import { join as join21, resolve as resolve9 } from "node:path";
48491
48513
  function extFor(mimeType) {
48492
48514
  return EXT[mimeType] ?? ".bin";
48493
48515
  }
@@ -48498,17 +48520,17 @@ async function saveUpload(buf, mimeType) {
48498
48520
  const dir2 = uploadDir();
48499
48521
  await mkdir(dir2, { recursive: true, mode: 448 });
48500
48522
  const storageKey = `${randomUUID3()}${extFor(mimeType)}`;
48501
- await writeFile(join20(dir2, storageKey), buf, { mode: 384 });
48523
+ await writeFile(join21(dir2, storageKey), buf, { mode: 384 });
48502
48524
  return { storageKey, size: buf.length };
48503
48525
  }
48504
48526
  function sessionUploadDir(sessionId2) {
48505
48527
  if (!SESSION_ID.test(sessionId2)) throw new Error(`sessionId tak sah: ${sessionId2}`);
48506
- return join20(uploadDir(), "terminal", sessionId2);
48528
+ return join21(uploadDir(), "terminal", sessionId2);
48507
48529
  }
48508
48530
  async function saveSessionUpload(sessionId2, buf, mimeType) {
48509
48531
  const dir2 = sessionUploadDir(sessionId2);
48510
48532
  await mkdir(dir2, { recursive: true, mode: 448 });
48511
- const path = join20(dir2, `${randomUUID3()}${extFor(mimeType)}`);
48533
+ const path = join21(dir2, `${randomUUID3()}${extFor(mimeType)}`);
48512
48534
  await writeFile(path, buf, { mode: 384 });
48513
48535
  return { path, size: buf.length };
48514
48536
  }
@@ -48524,11 +48546,11 @@ async function dropSessionUploads(sessionId2) {
48524
48546
  }
48525
48547
  async function readUpload(storageKey) {
48526
48548
  const safe3 = storageKey.replace(/[/\\]/g, "");
48527
- return readFile2(join20(uploadDir(), safe3));
48549
+ return readFile2(join21(uploadDir(), safe3));
48528
48550
  }
48529
48551
  async function readUploadOrFetch(storageKey) {
48530
48552
  const safe3 = storageKey.replace(/[/\\]/g, "");
48531
- const target3 = join20(uploadDir(), safe3);
48553
+ const target3 = join21(uploadDir(), safe3);
48532
48554
  try {
48533
48555
  return await readFile2(target3);
48534
48556
  } catch {
@@ -48555,7 +48577,7 @@ async function readUploadOrFetch(storageKey) {
48555
48577
  }
48556
48578
  async function deleteUpload(storageKey) {
48557
48579
  const safe3 = storageKey.replace(/[/\\]/g, "");
48558
- await unlink(join20(uploadDir(), safe3)).catch(() => {
48580
+ await unlink(join21(uploadDir(), safe3)).catch(() => {
48559
48581
  });
48560
48582
  }
48561
48583
  var EXT, SESSION_ID;
@@ -49001,14 +49023,14 @@ var init_session_sandbox = __esm({
49001
49023
  });
49002
49024
 
49003
49025
  // src/services/session-event-spool.ts
49004
- import { join as join21 } from "node:path";
49026
+ import { join as join22 } from "node:path";
49005
49027
  var sessionEventSpoolRoot, sessionEventDir;
49006
49028
  var init_session_event_spool = __esm({
49007
49029
  "src/services/session-event-spool.ts"() {
49008
49030
  "use strict";
49009
49031
  init_src2();
49010
- sessionEventSpoolRoot = (env = process.env) => join21(resolveHome(env), "session-events");
49011
- sessionEventDir = (sessionId2, env = process.env) => join21(sessionEventSpoolRoot(env), sessionId2);
49032
+ sessionEventSpoolRoot = (env = process.env) => join22(resolveHome(env), "session-events");
49033
+ sessionEventDir = (sessionId2, env = process.env) => join22(sessionEventSpoolRoot(env), sessionId2);
49012
49034
  }
49013
49035
  });
49014
49036
 
@@ -49535,6 +49557,9 @@ function createSession(projectId, cwd, opts2 = {}) {
49535
49557
  }
49536
49558
  const agent = opts2.agent ?? "claude";
49537
49559
  const account = opts2.command ? null : accountResolver(agent, opts2.accountId);
49560
+ const convId = !opts2.command && agent === "claude" && !opts2.specId ? opts2.agentSessionId ?? randomUUID4() : void 0;
49561
+ const canResumeConv = !!convId && !!opts2.agentSessionId && !!opts2.resumeAgentSession && claudeConversationExists(convId, account?.env?.CLAUDE_CONFIG_DIR);
49562
+ const convArg = convId ? `${canResumeConv ? "--resume" : "--session-id"} ${sq(convId)}` : "";
49538
49563
  let argv;
49539
49564
  if (opts2.command) {
49540
49565
  argv = opts2.command.map(sq).join(" ");
@@ -49577,7 +49602,7 @@ function createSession(projectId, cwd, opts2 = {}) {
49577
49602
  process.stderr.write(`hanoman: penyuntikan skill global gagal: ${e.message}
49578
49603
  `);
49579
49604
  }
49580
- argv = (agent === "antigravity" ? [sq(agentBin(agent)), flags, ...promptArg ? ["--prompt-interactive", promptArg] : []] : [sq(agentBin(agent)), promptArg, flags, agentsArg, nativeAgentArgs, skillsArg]).filter(Boolean).join(" ");
49605
+ argv = (agent === "antigravity" ? [sq(agentBin(agent)), flags, ...promptArg ? ["--prompt-interactive", promptArg] : []] : [sq(agentBin(agent)), promptArg, flags, convArg, agentsArg, nativeAgentArgs, skillsArg]).filter(Boolean).join(" ");
49581
49606
  }
49582
49607
  const envPairs = [];
49583
49608
  if (!opts2.command && agent === "claude") {
@@ -49750,7 +49775,8 @@ function createSession(projectId, cwd, opts2 = {}) {
49750
49775
  effort: opts2.effort,
49751
49776
  branch: opts2.branch,
49752
49777
  cwd,
49753
- accountId: account?.id
49778
+ accountId: account?.id,
49779
+ ...convId ? { agentSessionId: convId } : {}
49754
49780
  });
49755
49781
  return {
49756
49782
  id: id2,
@@ -50083,6 +50109,7 @@ var init_pty = __esm({
50083
50109
  init_src();
50084
50110
  init_session_phases();
50085
50111
  init_session_id();
50112
+ init_claude_conversation();
50086
50113
  init_antigravity_env_bootstrap();
50087
50114
  init_antigravity_runtime();
50088
50115
  init_skill_inject();
@@ -50388,7 +50415,7 @@ var init_redact_known = __esm({
50388
50415
  // src/services/transcript-store.ts
50389
50416
  import { randomUUID as randomUUID5 } from "node:crypto";
50390
50417
  import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile3, unlink as unlink2, readdir as readdir2, stat as stat3 } from "node:fs/promises";
50391
- import { join as join22, resolve as resolve10, basename as basename6 } from "node:path";
50418
+ import { join as join23, resolve as resolve10, basename as basename6 } from "node:path";
50392
50419
  function transcriptDir() {
50393
50420
  return resolve10(effectiveStr("HANOMAN_TRANSCRIPT_DIR")?.trim() || resolveDataDirs().transcripts);
50394
50421
  }
@@ -50408,13 +50435,13 @@ async function saveTranscript(text2) {
50408
50435
  const dir2 = transcriptDir();
50409
50436
  await mkdir2(dir2, { recursive: true, mode: 448 });
50410
50437
  const key = `${randomUUID5()}.log`;
50411
- await writeFile2(join22(dir2, key), body, { encoding: "utf8", mode: 384 });
50438
+ await writeFile2(join23(dir2, key), body, { encoding: "utf8", mode: 384 });
50412
50439
  return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
50413
50440
  }
50414
50441
  async function readTranscript(key) {
50415
50442
  if (!key) return null;
50416
50443
  try {
50417
- return await readFile3(join22(transcriptDir(), basename6(key)), "utf8");
50444
+ return await readFile3(join23(transcriptDir(), basename6(key)), "utf8");
50418
50445
  } catch {
50419
50446
  return null;
50420
50447
  }
@@ -50422,7 +50449,7 @@ async function readTranscript(key) {
50422
50449
  async function deleteTranscript(key) {
50423
50450
  if (!key) return;
50424
50451
  try {
50425
- await unlink2(join22(transcriptDir(), basename6(key)));
50452
+ await unlink2(join23(transcriptDir(), basename6(key)));
50426
50453
  } catch (e) {
50427
50454
  if (e.code !== "ENOENT") throw e;
50428
50455
  }
@@ -50440,7 +50467,7 @@ async function listTranscripts() {
50440
50467
  for (const name2 of names) {
50441
50468
  if (!name2.endsWith(".log")) continue;
50442
50469
  try {
50443
- rows.push({ key: name2, mtimeMs: (await stat3(join22(dir2, name2))).mtimeMs });
50470
+ rows.push({ key: name2, mtimeMs: (await stat3(join23(dir2, name2))).mtimeMs });
50444
50471
  } catch {
50445
50472
  }
50446
50473
  }
@@ -50771,6 +50798,8 @@ var init_settings3 = __esm({
50771
50798
  notifyDecisionSound: "alert",
50772
50799
  notifyDecisionTelegram: true,
50773
50800
  // ADR-0226 · tak berefek tanpa bot token + allowlist
50801
+ autoResumeSessions: false,
50802
+ // ADR-0235 · auto-resume semua sesi setelah reboot/crash (opt-in)
50774
50803
  agentAccessEnabled: false,
50775
50804
  // SPEC-257 · akses AI agent off sampai dibuka manusia
50776
50805
  scheduler: SCHEDULER_DEFAULTS,
@@ -55737,11 +55766,11 @@ var init_tombstone = __esm({
55737
55766
  // src/services/qa-attachment-sync.ts
55738
55767
  import { createHash as createHash9, randomUUID as randomUUID7 } from "node:crypto";
55739
55768
  import { chmod, mkdir as mkdir4, rename as rename2, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
55740
- import { join as join24 } from "node:path";
55769
+ import { join as join25 } from "node:path";
55741
55770
  async function qaBytesPresent(storageKey) {
55742
55771
  if (!QA_STORAGE_KEY.test(storageKey)) return false;
55743
55772
  try {
55744
- return (await stat4(join24(uploadDir(), storageKey))).isFile();
55773
+ return (await stat4(join25(uploadDir(), storageKey))).isFile();
55745
55774
  } catch {
55746
55775
  return false;
55747
55776
  }
@@ -55766,10 +55795,10 @@ async function storeQaBytes(storageKey, buf) {
55766
55795
  if (!QA_STORAGE_KEY.test(storageKey)) throw new Error("storageKey tak sah");
55767
55796
  const dir2 = uploadDir();
55768
55797
  await mkdir4(dir2, { recursive: true, mode: 448 });
55769
- const tmp = join24(dir2, `${storageKey}.tmp-${randomUUID7()}`);
55798
+ const tmp = join25(dir2, `${storageKey}.tmp-${randomUUID7()}`);
55770
55799
  await writeFile4(tmp, buf, { mode: 384 });
55771
- await rename2(tmp, join24(dir2, storageKey));
55772
- await chmod(join24(dir2, storageKey), 384).catch(() => {
55800
+ await rename2(tmp, join25(dir2, storageKey));
55801
+ await chmod(join25(dir2, storageKey), 384).catch(() => {
55773
55802
  });
55774
55803
  }
55775
55804
  var QA_STORAGE_KEY, QA_SYNC_MAX_BYTES, SNIFFABLE2, isUtf8Text2;
@@ -56420,7 +56449,7 @@ var init_sync_notify = __esm({
56420
56449
  // src/services/safe-repo-path.ts
56421
56450
  import { constants as constants3 } from "node:fs";
56422
56451
  import { lstat, mkdir as mkdir5, open as open2, realpath, rename as rename3, unlink as unlink4 } from "node:fs/promises";
56423
- import { isAbsolute as isAbsolute10, join as join25, relative as relative6, resolve as resolve13, sep as sep7 } from "node:path";
56452
+ import { isAbsolute as isAbsolute10, join as join26, relative as relative6, resolve as resolve13, sep as sep7 } from "node:path";
56424
56453
  import { randomUUID as randomUUID8 } from "node:crypto";
56425
56454
  import { closeSync as closeSync3, fstatSync as fstatSync2, lstatSync as lstatSync9, mkdirSync as mkdirSync13, openSync as openSync3, readFileSync as readFileSync15, realpathSync as realpathSync7, renameSync as renameSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "node:fs";
56426
56455
  function components(rel) {
@@ -56444,7 +56473,7 @@ async function ensureRepoParents(root, rel) {
56444
56473
  const parts = components(rel).slice(0, -1);
56445
56474
  let current = base3;
56446
56475
  for (const part of parts) {
56447
- current = join25(current, part);
56476
+ current = join26(current, part);
56448
56477
  try {
56449
56478
  await mkdir5(current, { mode: 448 });
56450
56479
  } catch (error) {
@@ -56461,7 +56490,7 @@ function ensureRepoParentsSync(root, rel) {
56461
56490
  const parts = components(rel).slice(0, -1);
56462
56491
  let current = base3;
56463
56492
  for (const part of parts) {
56464
- current = join25(current, part);
56493
+ current = join26(current, part);
56465
56494
  try {
56466
56495
  mkdirSync13(current, { mode: 448 });
56467
56496
  } catch (error) {
@@ -56477,7 +56506,7 @@ async function resolveRepoEntry(root, rel, opts2 = {}) {
56477
56506
  const parts = components(rel);
56478
56507
  let current = base3;
56479
56508
  for (let i = 0; i < parts.length; i++) {
56480
- current = join25(current, parts[i]);
56509
+ current = join26(current, parts[i]);
56481
56510
  const stat8 = await lstat(current).catch((error) => {
56482
56511
  if (error.code === "ENOENT" && opts2.allowMissingTail) return null;
56483
56512
  if (error.code === "ENOENT" && opts2.allowMissingFinal && i === parts.length - 1) return null;
@@ -56498,7 +56527,7 @@ function assertSafeRepoPathSync(root, rel, allowMissingFinal = false, allowMissi
56498
56527
  const parts = components(rel);
56499
56528
  let current = base3;
56500
56529
  for (let i = 0; i < parts.length; i++) {
56501
- current = join25(current, parts[i]);
56530
+ current = join26(current, parts[i]);
56502
56531
  try {
56503
56532
  const stat8 = lstatSync9(current);
56504
56533
  if (stat8.isSymbolicLink()) denied("symlink");
@@ -56528,7 +56557,7 @@ function writeRepoFileAtomicSync(root, rel, data) {
56528
56557
  const parent = resolve13(path, "..");
56529
56558
  const parentRel = relative6(realpathSync7(root), parent);
56530
56559
  if (parentRel) assertSafeRepoPathSync(root, parentRel);
56531
- const temp = join25(parent, `.hanoman-${randomUUID8()}.tmp`);
56560
+ const temp = join26(parent, `.hanoman-${randomUUID8()}.tmp`);
56532
56561
  const fd = openSync3(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 384);
56533
56562
  try {
56534
56563
  writeFileSync15(fd, data);
@@ -56575,7 +56604,7 @@ async function writeRepoFileAtomic(root, rel, data) {
56575
56604
  await mkdir5(entry2.parent, { recursive: false, mode: 448 }).catch((error) => {
56576
56605
  if (error.code !== "EEXIST") throw error;
56577
56606
  });
56578
- const temp = join25(entry2.parent, `.hanoman-${randomUUID8()}.tmp`);
56607
+ const temp = join26(entry2.parent, `.hanoman-${randomUUID8()}.tmp`);
56579
56608
  const handle = await open2(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 384);
56580
56609
  try {
56581
56610
  await handle.writeFile(data);
@@ -56612,7 +56641,7 @@ var init_safe_repo_path = __esm({
56612
56641
  import { execFile as execFile5 } from "node:child_process";
56613
56642
  import { promisify as promisify4 } from "node:util";
56614
56643
  import { rmSync as rmSync9 } from "node:fs";
56615
- import { join as join26 } from "node:path";
56644
+ import { join as join27 } from "node:path";
56616
56645
  async function worktreeForBranch(repoDir, branch) {
56617
56646
  let path = null;
56618
56647
  for (const line of (await out(repoDir, ["worktree", "list", "--porcelain"])).split("\n")) {
@@ -56645,7 +56674,7 @@ async function integrateBranch(repoDir, src, op, target3) {
56645
56674
  const tgt = await resolveTarget(repoDir, target3);
56646
56675
  if (!tgt) return { status: "error", code: 400, error: `target "${target3}" tidak dikenal` };
56647
56676
  await sh(repoDir, ["fetch", "origin"]);
56648
- const wt = join26(repoDir, ".worktrees", `merge-${sanitize(src.mergeId)}`);
56677
+ const wt = join27(repoDir, ".worktrees", `merge-${sanitize(src.mergeId)}`);
56649
56678
  await reclaim(repoDir, wt);
56650
56679
  const baseRef = op === "merge" ? tgt.ref : source;
56651
56680
  const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
@@ -56706,7 +56735,7 @@ async function mergeIntoCurrent(repoDir, source, opts2 = {}) {
56706
56735
  const src = await resolveGraphSource(repoDir, source);
56707
56736
  if (!src) return { status: "error", code: 400, error: `source "${source}" tak dikenal` };
56708
56737
  await sh(repoDir, ["fetch", "origin"]);
56709
- const wt = join26(repoDir, ".worktrees", `merge-${sanitize(current)}`);
56738
+ const wt = join27(repoDir, ".worktrees", `merge-${sanitize(current)}`);
56710
56739
  await reclaim(repoDir, wt);
56711
56740
  const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
56712
56741
  if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
@@ -56744,7 +56773,7 @@ async function replayCurrent(repoDir, source, cmd) {
56744
56773
  const current = await out(repoDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
56745
56774
  if (!current || current === "HEAD")
56746
56775
  return { status: "error", code: 409, error: "HEAD detached \u2014 checkout sebuah branch dulu" };
56747
- const wt = join26(repoDir, ".worktrees", `merge-${sanitize(current)}`);
56776
+ const wt = join27(repoDir, ".worktrees", `merge-${sanitize(current)}`);
56748
56777
  await reclaim(repoDir, wt);
56749
56778
  const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
56750
56779
  if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
@@ -56804,11 +56833,11 @@ import { execFile as execFile6 } from "node:child_process";
56804
56833
  import { promisify as promisify5 } from "node:util";
56805
56834
  import { mkdtemp, copyFile, rm as rm2 } from "node:fs/promises";
56806
56835
  import { tmpdir as tmpdir5 } from "node:os";
56807
- import { join as join27, resolve as resolve15 } from "node:path";
56836
+ import { join as join28, resolve as resolve15 } from "node:path";
56808
56837
  async function withTempIndex(wt, fn) {
56809
56838
  const idx = (await exec4("git", ["rev-parse", "--git-path", "index"], { cwd: wt, ...GIT3 })).stdout.trim();
56810
- const dir2 = await mkdtemp(join27(tmpdir5(), "hanoman-idx-"));
56811
- const tmp = join27(dir2, "index");
56839
+ const dir2 = await mkdtemp(join28(tmpdir5(), "hanoman-idx-"));
56840
+ const tmp = join28(dir2, "index");
56812
56841
  await copyFile(resolve15(wt, idx), tmp);
56813
56842
  const env = { ...process.env, GIT_INDEX_FILE: tmp };
56814
56843
  try {
@@ -56954,7 +56983,7 @@ var init_spec_review2 = __esm({
56954
56983
  GIT3 = { maxBuffer: 1 << 24 };
56955
56984
  MAX = 256 * 1024;
56956
56985
  emptySpecReview = (reason) => ({ base: "", files: [], changed: [], empty: true, reason });
56957
- worktreeDir = (repoDir, specId) => join27(repoDir, ".worktrees", specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
56986
+ worktreeDir = (repoDir, specId) => join28(repoDir, ".worktrees", specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
56958
56987
  splitZ = (s2) => s2.split("\0").filter(Boolean);
56959
56988
  EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
56960
56989
  }
@@ -57531,6 +57560,12 @@ async function recordFailure(specId, title, projectId, reason) {
57531
57560
  }).catch(() => {
57532
57561
  });
57533
57562
  }
57563
+ async function recordFreeResumeIssue(sessionId2, title, projectId, reason) {
57564
+ await prisma.notification.create({
57565
+ data: { type: "fail", key: `fail:resume:${sessionId2}:${Date.now()}`, specId: null, sessionId: sessionId2, title: `Gagal: ${title} \u2014 ${reason}`, projectId }
57566
+ }).catch(() => {
57567
+ });
57568
+ }
57534
57569
  async function recordAutoMerge(specId, projectId, title) {
57535
57570
  const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
57536
57571
  await prisma.notification.create({
@@ -57655,7 +57690,7 @@ var init_notifications2 = __esm({
57655
57690
 
57656
57691
  // src/services/codex-trust.ts
57657
57692
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, readFileSync as readFileSync16, realpathSync as realpathSync9 } from "node:fs";
57658
- import { homedir as homedir5 } from "node:os";
57693
+ import { homedir as homedir6 } from "node:os";
57659
57694
  function ensureCodexTrust(repoDir, home3) {
57660
57695
  const path = codexConfigPath(home3);
57661
57696
  let canonical = repoDir;
@@ -57684,7 +57719,7 @@ var codexHome, codexConfigPath;
57684
57719
  var init_codex_trust = __esm({
57685
57720
  "src/services/codex-trust.ts"() {
57686
57721
  "use strict";
57687
- codexHome = (home3) => home3 ?? process.env.CODEX_HOME ?? `${homedir5()}/.codex`;
57722
+ codexHome = (home3) => home3 ?? process.env.CODEX_HOME ?? `${homedir6()}/.codex`;
57688
57723
  codexConfigPath = (home3) => `${codexHome(home3)}/config.toml`;
57689
57724
  }
57690
57725
  });
@@ -57812,7 +57847,7 @@ var init_device_sockets = __esm({
57812
57847
  // src/services/exit-guard.ts
57813
57848
  import { spawn as spawn3 } from "node:child_process";
57814
57849
  import { writeFileSync as writeFileSync16 } from "node:fs";
57815
- import { join as join31 } from "node:path";
57850
+ import { join as join32 } from "node:path";
57816
57851
  function watchdogScript(pid, graceMs) {
57817
57852
  const secs = Math.max(1, Math.ceil(graceMs / 1e3));
57818
57853
  return `s=$(ps -o lstart= -p ${pid}); sleep ${secs}; [ -n "$s" ] && [ "$(ps -o lstart= -p ${pid})" = "$s" ] && kill -9 ${pid}`;
@@ -57829,7 +57864,7 @@ function exitGuarded(code, d = {}) {
57829
57864
  const pid = d.pid ?? process.pid;
57830
57865
  try {
57831
57866
  const write2 = d.writeIntent ?? ((p3, t) => writeFileSync16(p3, t, { mode: 384 }));
57832
- write2(join31(d.home ?? resolveHome(), EXIT_INTENT_FILE), formatExitIntent(pid, code));
57867
+ write2(join32(d.home ?? resolveHome(), EXIT_INTENT_FILE), formatExitIntent(pid, code));
57833
57868
  } catch {
57834
57869
  }
57835
57870
  try {
@@ -58306,7 +58341,7 @@ var init_remote_session = __esm({
58306
58341
 
58307
58342
  // src/services/spec-attachment-dir.ts
58308
58343
  import { mkdir as mkdir6, readdir as readdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "node:fs/promises";
58309
- import { join as join32 } from "node:path";
58344
+ import { join as join33 } from "node:path";
58310
58345
  function uniqueName(taken, filename) {
58311
58346
  if (!taken.has(filename)) {
58312
58347
  taken.add(filename);
@@ -58354,19 +58389,19 @@ async function syncSpecAttachmentsDir(specId, projectId) {
58354
58389
  for (const a of rows) {
58355
58390
  let bytes;
58356
58391
  try {
58357
- bytes = await readFile6(join32(uploadDir(), a.storageKey));
58392
+ bytes = await readFile6(join33(uploadDir(), a.storageKey));
58358
58393
  } catch {
58359
58394
  continue;
58360
58395
  }
58361
58396
  const filename = uniqueName(taken, a.filename);
58362
- const path = join32(dir2, filename);
58397
+ const path = join33(dir2, filename);
58363
58398
  await writeFile5(path, bytes, { mode: 384 });
58364
58399
  items.push({ filename, mimeType: a.mimeType, size: a.size, path });
58365
58400
  }
58366
- await writeFile5(join32(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
58401
+ await writeFile5(join33(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
58367
58402
  const keep = /* @__PURE__ */ new Set([INDEX, ...items.map((a) => a.filename)]);
58368
58403
  for (const name2 of await readdir5(dir2)) {
58369
- if (!keep.has(name2)) await rm4(join32(dir2, name2), { recursive: true, force: true }).catch(() => {
58404
+ if (!keep.has(name2)) await rm4(join33(dir2, name2), { recursive: true, force: true }).catch(() => {
58370
58405
  });
58371
58406
  }
58372
58407
  return items;
@@ -58385,7 +58420,7 @@ var init_spec_attachment_dir = __esm({
58385
58420
  init_local_binding();
58386
58421
  init_session_id();
58387
58422
  init_uploads();
58388
- specAttachmentsDir = (repoDir, sessionId2) => join32(repoDir, ".worktrees", ".attachments", sessionId2);
58423
+ specAttachmentsDir = (repoDir, sessionId2) => join33(repoDir, ".worktrees", ".attachments", sessionId2);
58389
58424
  INDEX = "INDEX.md";
58390
58425
  humanSize = (n2) => n2 >= 1024 * 1024 ? `${(n2 / 1024 / 1024).toFixed(1)} MB` : n2 >= 1024 ? `${Math.round(n2 / 1024)} KB` : `${n2} B`;
58391
58426
  }
@@ -59243,10 +59278,10 @@ var init_keychain = __esm({
59243
59278
  // src/services/limits.ts
59244
59279
  import { execFileSync as execFileSync7 } from "node:child_process";
59245
59280
  import { readFileSync as readFileSync18 } from "node:fs";
59246
- import { homedir as homedir6, userInfo as userInfo2 } from "node:os";
59247
- import { join as join34 } from "node:path";
59281
+ import { homedir as homedir7, userInfo as userInfo2 } from "node:os";
59282
+ import { join as join35 } from "node:path";
59248
59283
  function credsFile() {
59249
- return join34(effectiveStr("CLAUDE_CONFIG_DIR") ?? join34(homedir6(), ".claude"), ".credentials.json");
59284
+ return join35(effectiveStr("CLAUDE_CONFIG_DIR") ?? join35(homedir7(), ".claude"), ".credentials.json");
59250
59285
  }
59251
59286
  function keychainAccessToken(run6 = securityRun2, account = osAccount2()) {
59252
59287
  return readClaudeKeychainToken("Claude Code-credentials", run6, account);
@@ -59345,8 +59380,8 @@ var init_limits = __esm({
59345
59380
 
59346
59381
  // src/services/codex-limits.ts
59347
59382
  import { open as open3, readdir as readdir6, stat as stat6 } from "node:fs/promises";
59348
- import { homedir as homedir7 } from "node:os";
59349
- import { join as join35 } from "node:path";
59383
+ import { homedir as homedir8 } from "node:os";
59384
+ import { join as join36 } from "node:path";
59350
59385
  function windowLabel(minutes) {
59351
59386
  if (minutes === 300) return "Sesi 5 jam";
59352
59387
  if (minutes === 10080) return "Mingguan";
@@ -59377,7 +59412,7 @@ async function recentRollouts(dir2) {
59377
59412
  return [];
59378
59413
  }
59379
59414
  const stamped = await Promise.all(entries4.map(async (rel) => {
59380
- const full = join35(dir2, rel);
59415
+ const full = join36(dir2, rel);
59381
59416
  try {
59382
59417
  return { full, mtime: (await stat6(full)).mtimeMs };
59383
59418
  } catch {
@@ -59457,7 +59492,7 @@ var init_codex_limits = __esm({
59457
59492
  MAX_FILES = 8;
59458
59493
  cache2 = null;
59459
59494
  freshUntil2 = 0;
59460
- codexSessionsDir = () => join35(process.env.CODEX_HOME ?? join35(homedir7(), ".codex"), "sessions");
59495
+ codexSessionsDir = () => join36(process.env.CODEX_HOME ?? join36(homedir8(), ".codex"), "sessions");
59461
59496
  UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
59462
59497
  severityOf = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
59463
59498
  }
@@ -59466,8 +59501,8 @@ var init_codex_limits = __esm({
59466
59501
  // src/services/antigravity-limits.ts
59467
59502
  import { execFileSync as execFileSync8 } from "node:child_process";
59468
59503
  import { readFileSync as readFileSync19 } from "node:fs";
59469
- import { homedir as homedir8 } from "node:os";
59470
- import { join as join36 } from "node:path";
59504
+ import { homedir as homedir9 } from "node:os";
59505
+ import { join as join37 } from "node:path";
59471
59506
  function parseAgyToken(raw) {
59472
59507
  try {
59473
59508
  let s2 = raw.trim();
@@ -59490,7 +59525,7 @@ function readAgyToken(run6 = securityRun3) {
59490
59525
  }
59491
59526
  function defaultProject() {
59492
59527
  try {
59493
- return readFileSync19(join36(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
59528
+ return readFileSync19(join37(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
59494
59529
  } catch {
59495
59530
  return null;
59496
59531
  }
@@ -59580,7 +59615,7 @@ var init_antigravity_limits = __esm({
59580
59615
  freshUntil3 = 0;
59581
59616
  UNAVAILABLE2 = { status: "unavailable", windows: [], fetchedAt: null };
59582
59617
  securityRun3 = (args) => execFileSync8("security", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
59583
- agyHome = () => join36(homedir8(), ".gemini", "antigravity-cli");
59618
+ agyHome = () => join37(homedir9(), ".gemini", "antigravity-cli");
59584
59619
  severityOf2 = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
59585
59620
  prodDeps = {
59586
59621
  platform: process.platform,
@@ -60038,7 +60073,7 @@ var init_prompt2 = __esm({
60038
60073
  });
60039
60074
 
60040
60075
  // src/services/lead/verdict.ts
60041
- import { existsSync as existsSync18 } from "node:fs";
60076
+ import { existsSync as existsSync19 } from "node:fs";
60042
60077
  import { isAbsolute as isAbsolute11, resolve as resolvePath2, relative as relative7 } from "node:path";
60043
60078
  function extractJsonBlock(raw) {
60044
60079
  const re = /```json\s*\n([\s\S]*?)```/g;
@@ -60074,7 +60109,7 @@ function keepExistingRefs(refs2, repoDir) {
60074
60109
  const full = resolvePath2(repoDir, ref);
60075
60110
  const rel = relative7(repoDir, full);
60076
60111
  if (rel.startsWith("..")) continue;
60077
- if (existsSync18(full)) out4.push(ref);
60112
+ if (existsSync19(full)) out4.push(ref);
60078
60113
  }
60079
60114
  return [...new Set(out4)];
60080
60115
  }
@@ -65924,15 +65959,15 @@ var init_client2 = __esm({
65924
65959
 
65925
65960
  // src/services/logs/spool.ts
65926
65961
  import { appendFile, mkdir as mkdir7, readFile as readFile8, readdir as readdir7, rm as rm5, stat as stat7 } from "node:fs/promises";
65927
- import { join as join37 } from "node:path";
65962
+ import { join as join38 } from "node:path";
65928
65963
  function spoolDir(lane) {
65929
- return join37(resolveDataDirs().home, "log-spool", lane);
65964
+ return join38(resolveDataDirs().home, "log-spool", lane);
65930
65965
  }
65931
65966
  async function currentSegment(lane) {
65932
65967
  const dir2 = spoolDir(lane);
65933
65968
  await mkdir7(dir2, { recursive: true, mode: 448 });
65934
65969
  if (currentFile && currentBytes < LOG_SPOOL_SEGMENT_BYTES) return currentFile;
65935
- currentFile = join37(dir2, `${Date.now()}-${++segCounter}.ndjson`);
65970
+ currentFile = join38(dir2, `${Date.now()}-${++segCounter}.ndjson`);
65936
65971
  currentBytes = 0;
65937
65972
  return currentFile;
65938
65973
  }
@@ -65954,7 +65989,7 @@ async function readSpoolSegments(lane) {
65954
65989
  const files2 = names.filter((n2) => n2.endsWith(".ndjson")).sort();
65955
65990
  const out4 = [];
65956
65991
  for (const name2 of files2) {
65957
- const file = join37(dir2, name2);
65992
+ const file = join38(dir2, name2);
65958
65993
  const raw = await readFile8(file, "utf8").catch(() => "");
65959
65994
  const entries4 = raw.split("\n").filter(Boolean).map((l) => JSON.parse(l));
65960
65995
  out4.push({ file, entries: entries4 });
@@ -66619,8 +66654,8 @@ var init_git_token = __esm({
66619
66654
 
66620
66655
  // src/services/project-checkout.ts
66621
66656
  import { execFile as execFile14, spawnSync as spawnSync4 } from "node:child_process";
66622
- import { existsSync as existsSync19, readdirSync as readdirSync10, rmSync as rmSync10 } from "node:fs";
66623
- import { join as join38 } from "node:path";
66657
+ import { existsSync as existsSync20, readdirSync as readdirSync11, rmSync as rmSync10 } from "node:fs";
66658
+ import { join as join39 } from "node:path";
66624
66659
  import { promisify as promisify13 } from "node:util";
66625
66660
  async function runGit(args, o) {
66626
66661
  const full = o.token ? ["-c", "credential.helper=", ...args] : args;
@@ -66642,8 +66677,8 @@ async function doClone(projectId, opts2) {
66642
66677
  if (!project) throw new CheckoutError(`project "${projectId}" tidak ada`, "not-found");
66643
66678
  if (!project.gitRemote) throw new CheckoutError(`project "${projectId}" tidak punya gitRemote untuk di-clone`, "no-remote");
66644
66679
  const remote = project.gitRemote;
66645
- const dest = opts2.dir ?? join38(checkoutRoot(), slug(project.id));
66646
- if (existsSync19(dest) && !isEmptyDir(dest)) {
66680
+ const dest = opts2.dir ?? join39(checkoutRoot(), slug(project.id));
66681
+ if (existsSync20(dest) && !isEmptyDir(dest)) {
66647
66682
  const origin = originOf2(dest);
66648
66683
  if (origin && sameRemote(origin, remote)) {
66649
66684
  await setBinding(projectId, dest);
@@ -66706,7 +66741,7 @@ var init_project_checkout = __esm({
66706
66741
  CLONE_TIMEOUT_MS = 10 * 6e4;
66707
66742
  FETCH_TIMEOUT_MS = 2 * 6e4;
66708
66743
  AUTH_RE = /Authentication failed|could not read (Username|Password)|Invalid username or password|HTTP 40[13]|returned error: 40[13]|Repository not found|Permission denied \(publickey\)/i;
66709
- checkoutRoot = () => join38(resolveHome(), "projects");
66744
+ checkoutRoot = () => join39(resolveHome(), "projects");
66710
66745
  slug = (id2) => id2.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
66711
66746
  sameRemote = (a, b) => {
66712
66747
  const norm5 = (u) => httpsRemote(u).replace(/\.git$/, "").replace(/\/+$/, "").toLowerCase();
@@ -66715,7 +66750,7 @@ var init_project_checkout = __esm({
66715
66750
  redact = (text2, token) => token ? text2.split(token).join("***") : text2;
66716
66751
  isEmptyDir = (dir2) => {
66717
66752
  try {
66718
- return readdirSync10(dir2).length === 0;
66753
+ return readdirSync11(dir2).length === 0;
66719
66754
  } catch {
66720
66755
  return false;
66721
66756
  }
@@ -68470,16 +68505,16 @@ var init_codex_version = __esm({
68470
68505
 
68471
68506
  // src/services/agent-tool-catalog.ts
68472
68507
  import { readFileSync as readFileSync31 } from "node:fs";
68473
- import { homedir as homedir16 } from "node:os";
68474
- import { join as join56 } from "node:path";
68508
+ import { homedir as homedir17 } from "node:os";
68509
+ import { join as join57 } from "node:path";
68475
68510
  function mcpServerNames(repoDir) {
68476
68511
  const names = [];
68477
- const claudeJson = readJson2(join56(home(), ".claude.json"));
68512
+ const claudeJson = readJson2(join57(home(), ".claude.json"));
68478
68513
  names.push(...serversOf(claudeJson));
68479
68514
  if (repoDir) {
68480
68515
  const projects = claudeJson?.projects;
68481
68516
  if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
68482
- names.push(...serversOf(readJson2(join56(repoDir, ".mcp.json"))));
68517
+ names.push(...serversOf(readJson2(join57(repoDir, ".mcp.json"))));
68483
68518
  }
68484
68519
  names.push(...codexServers());
68485
68520
  return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
@@ -68492,7 +68527,7 @@ var init_agent_tool_catalog = __esm({
68492
68527
  "src/services/agent-tool-catalog.ts"() {
68493
68528
  "use strict";
68494
68529
  init_src();
68495
- home = () => process.env.HOME || homedir16();
68530
+ home = () => process.env.HOME || homedir17();
68496
68531
  readJson2 = (path) => {
68497
68532
  try {
68498
68533
  return JSON.parse(readFileSync31(path, "utf8"));
@@ -68508,7 +68543,7 @@ var init_agent_tool_catalog = __esm({
68508
68543
  codexServers = () => {
68509
68544
  let text2;
68510
68545
  try {
68511
- text2 = readFileSync31(join56(home(), ".codex", "config.toml"), "utf8");
68546
+ text2 = readFileSync31(join57(home(), ".codex", "config.toml"), "utf8");
68512
68547
  } catch {
68513
68548
  return [];
68514
68549
  }
@@ -72128,7 +72163,7 @@ import websocket from "@fastify/websocket";
72128
72163
  import cookie from "@fastify/cookie";
72129
72164
  import { fileURLToPath as fileURLToPath6 } from "node:url";
72130
72165
  import { dirname as dirname19 } from "node:path";
72131
- import { existsSync as existsSync26 } from "node:fs";
72166
+ import { existsSync as existsSync27 } from "node:fs";
72132
72167
 
72133
72168
  // src/web-dir.ts
72134
72169
  import { resolve as resolve11 } from "node:path";
@@ -72192,7 +72227,7 @@ init_uploads();
72192
72227
  import { randomUUID as randomUUID6 } from "node:crypto";
72193
72228
  import { spawn as spawn2 } from "node:child_process";
72194
72229
  import { mkdir as mkdir3, rename, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
72195
- import { basename as basename7, isAbsolute as isAbsolute9, join as join23 } from "node:path";
72230
+ import { basename as basename7, isAbsolute as isAbsolute9, join as join24 } from "node:path";
72196
72231
  import sharp from "sharp";
72197
72232
  var UPLOAD_LIMITS = {
72198
72233
  fileBytes: 5 * 1024 * 1024,
@@ -72266,16 +72301,16 @@ function safeFilename2(input, extension2) {
72266
72301
  }
72267
72302
  async function commitToStorage(buffer, extension2, deps, beforePromote) {
72268
72303
  const storageDir = deps.storageDir ?? uploadDir();
72269
- const quarantineDir = join23(storageDir, ".quarantine");
72304
+ const quarantineDir = join24(storageDir, ".quarantine");
72270
72305
  await mkdir3(quarantineDir, { recursive: true, mode: 448 });
72271
72306
  await mkdir3(storageDir, { recursive: true, mode: 448 });
72272
- const quarantine = join23(quarantineDir, `${randomUUID6()}.upload`);
72307
+ const quarantine = join24(quarantineDir, `${randomUUID6()}.upload`);
72273
72308
  const storageKey = `${randomUUID6()}${extension2}`;
72274
72309
  await writeFile3(quarantine, buffer, { mode: 384, flag: "wx" });
72275
72310
  try {
72276
72311
  await timeout((deps.scanner ?? scannerFromEnv)(quarantine), UPLOAD_LIMITS.scanMs, "UPLOAD_SCAN");
72277
72312
  await beforePromote?.();
72278
- await rename(quarantine, join23(storageDir, storageKey));
72313
+ await rename(quarantine, join24(storageDir, storageKey));
72279
72314
  } catch (error) {
72280
72315
  await unlink3(quarantine).catch(() => {
72281
72316
  });
@@ -72731,7 +72766,7 @@ init_src();
72731
72766
  init_safe_repo_path();
72732
72767
  import { execFile as execFile3 } from "node:child_process";
72733
72768
  import { promisify as promisify2 } from "node:util";
72734
- import { existsSync as existsSync13, rmSync as rmSync8 } from "node:fs";
72769
+ import { existsSync as existsSync14, rmSync as rmSync8 } from "node:fs";
72735
72770
  import { resolve as resolve14 } from "node:path";
72736
72771
  var exec = promisify2(execFile3);
72737
72772
  async function listRepoDocs(repoDir) {
@@ -72756,12 +72791,12 @@ function docsDirOf(repoDir) {
72756
72791
  }
72757
72792
  function resolveIndex(repoDir, docsDir) {
72758
72793
  const rel = `${docsDir}/README.md`;
72759
- return existsSync13(resolve14(repoDir, rel)) ? rel : "";
72794
+ return existsSync14(resolve14(repoDir, rel)) ? rel : "";
72760
72795
  }
72761
72796
  var catOf = (rel) => rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
72762
72797
  var nameOf = (rel) => rel.includes("/") ? rel.slice(rel.lastIndexOf("/") + 1) : rel;
72763
72798
  async function scanRepoDocs(repoDir) {
72764
- if (!repoDir || !existsSync13(repoDir)) return { coverage: 0, tree: [] };
72799
+ if (!repoDir || !existsSync14(repoDir)) return { coverage: 0, tree: [] };
72765
72800
  const files2 = await listRepoDocs(repoDir);
72766
72801
  const docsDir = docsDirOf(repoDir);
72767
72802
  const index = resolveIndex(repoDir, docsDir);
@@ -72809,7 +72844,7 @@ function writeDocFile(repoDir, rel, content) {
72809
72844
  }
72810
72845
  function deleteDocFile(repoDir, rel) {
72811
72846
  const abs = docAbsPath(repoDir, rel);
72812
- if (!existsSync13(abs)) return false;
72847
+ if (!existsSync14(abs)) return false;
72813
72848
  rmSync8(abs);
72814
72849
  return true;
72815
72850
  }
@@ -73128,7 +73163,7 @@ async function projects_default(app3) {
73128
73163
  init_src();
73129
73164
  init_src2();
73130
73165
  init_integrate();
73131
- import { existsSync as existsSync17 } from "node:fs";
73166
+ import { existsSync as existsSync18 } from "node:fs";
73132
73167
  import { basename as basename9 } from "node:path";
73133
73168
 
73134
73169
  // src/services/worktree-delete.ts
@@ -73147,7 +73182,7 @@ init_spec_review2();
73147
73182
  init_safe_repo_path();
73148
73183
  import { execFile as execFile7 } from "node:child_process";
73149
73184
  import { promisify as promisify6 } from "node:util";
73150
- import { existsSync as existsSync14 } from "node:fs";
73185
+ import { existsSync as existsSync15 } from "node:fs";
73151
73186
  import { readdir as readdir3 } from "node:fs/promises";
73152
73187
  import { resolve as resolve16 } from "node:path";
73153
73188
  var exec5 = promisify6(execFile7);
@@ -73160,7 +73195,7 @@ function repoAbsPath(repoDir, rel, allowMissingFinal = false) {
73160
73195
  return resolve16(repoDir, rel);
73161
73196
  }
73162
73197
  async function listRepoTree(repoDir, ref = "") {
73163
- if (!repoDir || !existsSync14(repoDir)) return [];
73198
+ if (!repoDir || !existsSync15(repoDir)) return [];
73164
73199
  try {
73165
73200
  const { stdout } = ref ? await exec5("git", ["ls-tree", "-r", "--name-only", "-z", ref], { cwd: repoDir, ...GIT4 }) : await exec5("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: repoDir, ...GIT4 });
73166
73201
  return [...new Set(splitZ2(stdout))].sort();
@@ -73170,7 +73205,7 @@ async function listRepoTree(repoDir, ref = "") {
73170
73205
  }
73171
73206
  async function listIgnoredEntries(repoDir) {
73172
73207
  const empty = { files: [], dirs: [] };
73173
- if (!repoDir || !existsSync14(repoDir)) return empty;
73208
+ if (!repoDir || !existsSync15(repoDir)) return empty;
73174
73209
  try {
73175
73210
  const { stdout } = await exec5(
73176
73211
  "git",
@@ -73223,7 +73258,7 @@ async function currentBranch(repoDir) {
73223
73258
  }
73224
73259
  async function repoStatus(repoDir) {
73225
73260
  const empty = { branch: "", ahead: 0, behind: 0, staged: [], unstaged: [], untracked: [], clean: true };
73226
- if (!repoDir || !existsSync14(repoDir)) return empty;
73261
+ if (!repoDir || !existsSync15(repoDir)) return empty;
73227
73262
  try {
73228
73263
  const { stdout } = await exec5("git", ["status", "--porcelain=v1", "-z", "-b"], { cwd: repoDir, ...GIT4 });
73229
73264
  const parts = stdout.split("\0");
@@ -73256,7 +73291,7 @@ async function repoStatus(repoDir) {
73256
73291
  }
73257
73292
  }
73258
73293
  async function listStashes(repoDir) {
73259
- if (!repoDir || !existsSync14(repoDir)) return [];
73294
+ if (!repoDir || !existsSync15(repoDir)) return [];
73260
73295
  try {
73261
73296
  const { stdout } = await exec5("git", ["stash", "list", `--format=%gd${US}%s${US}%aI`], { cwd: repoDir, ...GIT4 });
73262
73297
  return stdout.split("\n").filter(Boolean).map((line) => {
@@ -73268,7 +73303,7 @@ async function listStashes(repoDir) {
73268
73303
  }
73269
73304
  }
73270
73305
  async function listGraph(repoDir, limit = 200, opts2 = {}) {
73271
- if (!repoDir || !existsSync14(repoDir)) return { commits: [], current: "", total: 0 };
73306
+ if (!repoDir || !existsSync15(repoDir)) return { commits: [], current: "", total: 0 };
73272
73307
  try {
73273
73308
  const fmt = ["%H", "%P", "%an", "%aI", "%s", "%D"].join(US);
73274
73309
  const refArgs = opts2.branches?.length ? ["--end-of-options", ...opts2.branches] : [
@@ -73338,7 +73373,7 @@ async function changedOf(repoDir, sha2) {
73338
73373
  }
73339
73374
  async function compareCommits(repoDir, from, to) {
73340
73375
  const empty = { from, to, changed: [] };
73341
- if (!repoDir || !existsSync14(repoDir)) return empty;
73376
+ if (!repoDir || !existsSync15(repoDir)) return empty;
73342
73377
  try {
73343
73378
  const [num, name2] = await Promise.all([
73344
73379
  exec5("git", ["diff", "--numstat", "-z", "--no-renames", "--end-of-options", from, to], { cwd: repoDir, ...GIT4 }),
@@ -73350,7 +73385,7 @@ async function compareCommits(repoDir, from, to) {
73350
73385
  }
73351
73386
  }
73352
73387
  async function searchCommits(repoDir, q2, by = "all") {
73353
- if (!repoDir || !existsSync14(repoDir) || !q2) return [];
73388
+ if (!repoDir || !existsSync15(repoDir) || !q2) return [];
73354
73389
  const run6 = async (extra) => {
73355
73390
  try {
73356
73391
  return (await exec5("git", ["log", "--all", "--format=%H", ...extra], { cwd: repoDir, ...GIT4 })).stdout.split("\n").filter(Boolean);
@@ -73597,7 +73632,7 @@ async function runGitOp(repoDir, op) {
73597
73632
  }
73598
73633
  }
73599
73634
  async function workingStatus(repoDir) {
73600
- if (!repoDir || !existsSync14(repoDir)) return { branch: "", staged: [], unstaged: [] };
73635
+ if (!repoDir || !existsSync15(repoDir)) return { branch: "", staged: [], unstaged: [] };
73601
73636
  const [branch, staged, unstaged] = await Promise.all([
73602
73637
  currentBranch(repoDir),
73603
73638
  changedFiles(repoDir, ["--cached"]),
@@ -73606,7 +73641,7 @@ async function workingStatus(repoDir) {
73606
73641
  return { branch, staged, unstaged };
73607
73642
  }
73608
73643
  async function workingFileDiff(repoDir, path, staged) {
73609
- if (!repoDir || !existsSync14(repoDir)) return null;
73644
+ if (!repoDir || !existsSync15(repoDir)) return null;
73610
73645
  repoAbsPath(repoDir, path, true);
73611
73646
  const changed = staged ? await changedFiles(repoDir, ["--cached"]) : await withTempIndex(repoDir, (env) => changedFiles(repoDir, [], env));
73612
73647
  const cf = changed.find((c) => c.path === path);
@@ -73815,7 +73850,7 @@ async function deleteBranches(repoDir, names, opts2) {
73815
73850
  import { execFile as execFile9 } from "node:child_process";
73816
73851
  import { realpathSync as realpathSync8 } from "node:fs";
73817
73852
  import { stat as stat5 } from "node:fs/promises";
73818
- import { basename as basename8, dirname as dirname13, join as join28, resolve as resolve17, sep as sep8 } from "node:path";
73853
+ import { basename as basename8, dirname as dirname13, join as join29, resolve as resolve17, sep as sep8 } from "node:path";
73819
73854
  import { promisify as promisify8 } from "node:util";
73820
73855
  var exec7 = promisify8(execFile9);
73821
73856
  var GIT6 = { timeout: 6e4, maxBuffer: 1 << 24, encoding: "utf8" };
@@ -73857,7 +73892,7 @@ var real = (p3) => {
73857
73892
  } catch {
73858
73893
  const path = resolve17(p3);
73859
73894
  const parent = dirname13(path);
73860
- return parent === path ? path : join28(real(parent), basename8(path));
73895
+ return parent === path ? path : join29(real(parent), basename8(path));
73861
73896
  }
73862
73897
  };
73863
73898
  function hasWorktreeSession(path, sessionId2, sessions) {
@@ -73869,7 +73904,7 @@ function hasWorktreeSession(path, sessionId2, sessions) {
73869
73904
  }
73870
73905
  async function bornAt(path) {
73871
73906
  try {
73872
- const st2 = await stat5(join28(path, ".git"));
73907
+ const st2 = await stat5(join29(path, ".git"));
73873
73908
  const ms = st2.birthtimeMs > 0 ? st2.birthtimeMs : st2.mtimeMs;
73874
73909
  return new Date(ms).toISOString();
73875
73910
  } catch {
@@ -74097,7 +74132,7 @@ init_notifications2();
74097
74132
  import { execFile as execFile11 } from "node:child_process";
74098
74133
  import { readdir as readdir4, rm as rm3 } from "node:fs/promises";
74099
74134
  import { promisify as promisify10 } from "node:util";
74100
- import { join as join29, resolve as resolve19 } from "node:path";
74135
+ import { join as join30, resolve as resolve19 } from "node:path";
74101
74136
  var TICK_MS = 6e4;
74102
74137
  var trashDirOf = (repoDir) => resolve19(repoDir, ".worktrees", ".trash");
74103
74138
  var exec8 = promisify10(execFile11);
@@ -74158,7 +74193,7 @@ async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
74158
74193
  }
74159
74194
  let removed = 0;
74160
74195
  for (const entry2 of entries4) {
74161
- const path = join29(dir2, entry2);
74196
+ const path = join30(dir2, entry2);
74162
74197
  const known2 = pending.get(path);
74163
74198
  const row = known2 ?? { path, repoDir, projectId, entry: entry2, sessionId: sessionIdOf(entry2), since: Date.now() };
74164
74199
  pending.set(path, row);
@@ -74301,8 +74336,10 @@ async function beginSession(b) {
74301
74336
  effort: b.effort ?? null,
74302
74337
  branch: b.branch ?? null,
74303
74338
  cwd: b.cwd,
74304
- accountId: b.accountId ?? null
74339
+ accountId: b.accountId ?? null,
74305
74340
  // ADR-0234
74341
+ agentSessionId: b.agentSessionId ?? null
74342
+ // ADR-0235
74306
74343
  }
74307
74344
  });
74308
74345
  }
@@ -74480,6 +74517,35 @@ async function reconciledRuntimesSince(cutoff) {
74480
74517
  }
74481
74518
  return out4;
74482
74519
  }
74520
+ var FREE_FLOW_KINDS = /* @__PURE__ */ new Set(["reverse", "prd", "scaffold", "breakdown"]);
74521
+ async function reconciledFreeSessionsSince(cutoff) {
74522
+ const rows = await prisma.sessionHistory.findMany({
74523
+ where: { endedReason: RECONCILED, reconciledAt: { gte: cutoff }, specId: null },
74524
+ orderBy: { startedAt: "desc" }
74525
+ });
74526
+ const seen2 = /* @__PURE__ */ new Set();
74527
+ const out4 = [];
74528
+ for (const r of rows) {
74529
+ if (seen2.has(r.sessionId)) continue;
74530
+ seen2.add(r.sessionId);
74531
+ const eligible = r.kind === "terminal" || r.kind === "shell" || FREE_FLOW_KINDS.has(r.kind) || r.kind === "worktree" && r.sessionId.startsWith("term-");
74532
+ if (!eligible) continue;
74533
+ out4.push({
74534
+ sessionId: r.sessionId,
74535
+ projectId: r.projectId,
74536
+ kind: r.kind,
74537
+ flow: r.flow,
74538
+ agent: r.agent === "antigravity" ? "antigravity" : r.agent === "codex" ? "codex" : "claude",
74539
+ ...r.model ? { model: r.model } : {},
74540
+ ...r.effort ? { effort: r.effort } : {},
74541
+ ...r.accountId ? { accountId: r.accountId } : {},
74542
+ ...r.branch ? { branch: r.branch } : {},
74543
+ cwd: r.cwd,
74544
+ ...r.agentSessionId ? { agentSessionId: r.agentSessionId } : {}
74545
+ });
74546
+ }
74547
+ return out4.reverse();
74548
+ }
74483
74549
  function installSessionHistory() {
74484
74550
  registerSessionHooks({
74485
74551
  onBirth: (b) => {
@@ -74626,8 +74692,8 @@ function appendSourceHistory(current, entry2) {
74626
74692
 
74627
74693
  // src/services/spec-reset.ts
74628
74694
  import { execFile as execFile13 } from "node:child_process";
74629
- import { existsSync as existsSync15 } from "node:fs";
74630
- import { join as join30 } from "node:path";
74695
+ import { existsSync as existsSync16 } from "node:fs";
74696
+ import { join as join31 } from "node:path";
74631
74697
  import { promisify as promisify12 } from "node:util";
74632
74698
 
74633
74699
  // src/services/stage-artifacts.ts
@@ -74682,14 +74748,14 @@ async function planSpecReset(spec) {
74682
74748
  const repoDir = await resolveRepoDir(spec.projectId);
74683
74749
  if (!repoDir) return EMPTY3;
74684
74750
  const sid = sessionIdForSpec(spec.id);
74685
- const wt = join30(repoDir, ".worktrees", sid);
74751
+ const wt = join31(repoDir, ".worktrees", sid);
74686
74752
  const branch = `hanoman/${sid}`;
74687
74753
  const hasBranch = await shaResolvable(repoDir, `refs/heads/${branch}`);
74688
74754
  return {
74689
74755
  // Berkas fase seluruh stage yang ditinggalkan — rentang yang sama persis dengan revert stage
74690
74756
  // SPEC-167, karena tujuan akhirnya memang sama: item berdiri lagi di `brainstorming`.
74691
74757
  wouldDelete: await artifactsToRemove(spec.projectId, spec.id, "brainstorming", spec.stage),
74692
- worktree: existsSync15(wt) ? wt : null,
74758
+ worktree: existsSync16(wt) ? wt : null,
74693
74759
  branch: hasBranch ? branch : null
74694
74760
  };
74695
74761
  }
@@ -74770,9 +74836,9 @@ init_uploads();
74770
74836
  // src/services/spec-mockups.ts
74771
74837
  init_local_binding();
74772
74838
  init_spec_review2();
74773
- import { existsSync as existsSync16, readdirSync as readdirSync9, realpathSync as realpathSync10, statSync as statSync5 } from "node:fs";
74839
+ import { existsSync as existsSync17, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync5 } from "node:fs";
74774
74840
  import { readFile as readFile7 } from "node:fs/promises";
74775
- import { extname as extname2, join as join33, sep as sep10 } from "node:path";
74841
+ import { extname as extname2, join as join34, sep as sep10 } from "node:path";
74776
74842
  var MOCKUP_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*\.(png|jpe?g|webp)$/i;
74777
74843
  var MIME = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp" };
74778
74844
  var isDir = (p3) => {
@@ -74785,18 +74851,18 @@ var isDir = (p3) => {
74785
74851
  async function resolveMockupDir(spec) {
74786
74852
  const repoDir = await resolveRepoDir(spec.projectId);
74787
74853
  if (!repoDir) return null;
74788
- const rel = join33("docs", "mockups", spec.id.toLowerCase());
74789
- const worktree = join33(worktreeDir(repoDir, spec.id), rel);
74854
+ const rel = join34("docs", "mockups", spec.id.toLowerCase());
74855
+ const worktree = join34(worktreeDir(repoDir, spec.id), rel);
74790
74856
  if (isDir(worktree)) return { source: "worktree", dir: worktree };
74791
- const repo = join33(repoDir, rel);
74857
+ const repo = join34(repoDir, rel);
74792
74858
  return isDir(repo) ? { source: "repo", dir: repo } : null;
74793
74859
  }
74794
74860
  async function listMockups(spec) {
74795
74861
  const found = await resolveMockupDir(spec);
74796
74862
  if (!found) return { source: null, dir: null, items: [] };
74797
- const items = readdirSync9(found.dir).filter((n2) => MOCKUP_NAME.test(n2)).sort().flatMap((name2) => {
74863
+ const items = readdirSync10(found.dir).filter((n2) => MOCKUP_NAME.test(n2)).sort().flatMap((name2) => {
74798
74864
  try {
74799
- const st2 = statSync5(join33(found.dir, name2));
74865
+ const st2 = statSync5(join34(found.dir, name2));
74800
74866
  return st2.isFile() ? [{ name: name2, size: st2.size, modifiedAt: st2.mtime.toISOString() }] : [];
74801
74867
  } catch {
74802
74868
  return [];
@@ -74808,8 +74874,8 @@ async function readMockup(spec, name2) {
74808
74874
  if (!MOCKUP_NAME.test(name2)) return null;
74809
74875
  const found = await resolveMockupDir(spec);
74810
74876
  if (!found) return null;
74811
- const file = join33(found.dir, name2);
74812
- if (!existsSync16(file)) return null;
74877
+ const file = join34(found.dir, name2);
74878
+ if (!existsSync17(file)) return null;
74813
74879
  try {
74814
74880
  const root = realpathSync10(found.dir);
74815
74881
  const real2 = realpathSync10(file);
@@ -77835,7 +77901,7 @@ ${item.context}` : item.context;
77835
77901
  });
77836
77902
  const specWithProject = (id2) => prisma.spec.findUnique({ where: { id: id2 }, include: { project: true } });
77837
77903
  const resolveReview = async (repoDir, spec) => {
77838
- if (existsSync17(worktreeDir(repoDir, spec.id))) return { wt: true };
77904
+ if (existsSync18(worktreeDir(repoDir, spec.id))) return { wt: true };
77839
77905
  if (spec.baseSha && spec.headSha && await shaResolvable(repoDir, spec.baseSha) && await shaResolvable(repoDir, spec.headSha))
77840
77906
  return { wt: false, base: spec.baseSha, head: spec.headSha };
77841
77907
  const r = await specCommitRange(repoDir, spec.id);
@@ -78133,7 +78199,7 @@ import { spawn as spawn5 } from "node:child_process";
78133
78199
  // src/services/git-remotes.ts
78134
78200
  import { execFile as execFile16 } from "node:child_process";
78135
78201
  import { promisify as promisify15 } from "node:util";
78136
- import { existsSync as existsSync20 } from "node:fs";
78202
+ import { existsSync as existsSync21 } from "node:fs";
78137
78203
  var exec10 = promisify15(execFile16);
78138
78204
  var GIT7 = { maxBuffer: 1 << 24, encoding: "utf8" };
78139
78205
  function parseRemote(url2) {
@@ -78152,7 +78218,7 @@ function prUrl(remoteUrl, branch, base3 = "main") {
78152
78218
  return null;
78153
78219
  }
78154
78220
  async function listRemotes(repoDir) {
78155
- if (!repoDir || !existsSync20(repoDir)) return [];
78221
+ if (!repoDir || !existsSync21(repoDir)) return [];
78156
78222
  try {
78157
78223
  const { stdout } = await exec10("git", ["remote", "-v"], { cwd: repoDir, ...GIT7 });
78158
78224
  const map = /* @__PURE__ */ new Map();
@@ -78200,7 +78266,7 @@ init_integrate();
78200
78266
  init_safe_repo_path();
78201
78267
  import { lstat as lstat2, rename as rename4, rm as rm6 } from "node:fs/promises";
78202
78268
  import { createWriteStream } from "node:fs";
78203
- import { join as join39 } from "node:path";
78269
+ import { join as join40 } from "node:path";
78204
78270
  import { randomUUID as randomUUID11 } from "node:crypto";
78205
78271
  import { pipeline } from "node:stream/promises";
78206
78272
  var EntryExistsError = class extends Error {
@@ -78261,7 +78327,7 @@ async function saveUpload2(repoDir, rel, source, opts2 = {}) {
78261
78327
  if (current && !opts2.overwrite) return { status: "exists" };
78262
78328
  if (current && !current.isFile())
78263
78329
  throw new PathContainmentError("repository path ditolak: target bukan file regular");
78264
- const temp = join39(entry2.parent, `.hanoman-${randomUUID11()}.tmp`);
78330
+ const temp = join40(entry2.parent, `.hanoman-${randomUUID11()}.tmp`);
78265
78331
  try {
78266
78332
  await pipeline(source, createWriteStream(temp, { flags: "wx", mode: 384 }));
78267
78333
  if (opts2.isTruncated?.()) {
@@ -78751,15 +78817,15 @@ async function finishGraphOp(reply, id2, repoDir, r, verb) {
78751
78817
 
78752
78818
  // src/routes/fs.ts
78753
78819
  import { readdir as readdir8 } from "node:fs/promises";
78754
- import { homedir as homedir9 } from "node:os";
78755
- import { resolve as resolve22, dirname as dirname16, join as join40 } from "node:path";
78820
+ import { homedir as homedir10 } from "node:os";
78821
+ import { resolve as resolve22, dirname as dirname16, join as join41 } from "node:path";
78756
78822
  async function fs_default(app3) {
78757
78823
  app3.get("/fs/browse", async (req, reply) => {
78758
78824
  const q2 = req.query.path;
78759
- const dir2 = q2 && q2.trim() ? resolve22(q2.trim()) : homedir9();
78825
+ const dir2 = q2 && q2.trim() ? resolve22(q2.trim()) : homedir10();
78760
78826
  try {
78761
78827
  const ents = await readdir8(dir2, { withFileTypes: true });
78762
- const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join40(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
78828
+ const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join41(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
78763
78829
  const parent = dirname16(dir2);
78764
78830
  return { path: dir2, parent: parent === dir2 ? null : parent, entries: entries4 };
78765
78831
  } catch {
@@ -78774,7 +78840,7 @@ init_src();
78774
78840
  init_src2();
78775
78841
  init_session_phases();
78776
78842
  init_spec_review2();
78777
- import { existsSync as existsSync22 } from "node:fs";
78843
+ import { existsSync as existsSync23 } from "node:fs";
78778
78844
  import { execFileSync as execFileSync10 } from "node:child_process";
78779
78845
  import { randomUUID as randomUUID14 } from "node:crypto";
78780
78846
  init_integrate();
@@ -79134,13 +79200,13 @@ init_local_binding();
79134
79200
 
79135
79201
  // src/services/terminal-diag.ts
79136
79202
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync17, statSync as statSync6, rmSync as rmSync11 } from "node:fs";
79137
- import { join as join41 } from "node:path";
79203
+ import { join as join42 } from "node:path";
79138
79204
  var DIAG_MAX_BYTES = 2 * 1024 * 1024;
79139
79205
  var KINDS = /* @__PURE__ */ new Set(["key", "comp", "data", "ack", "pred"]);
79140
79206
  var ID = /^[A-Za-z0-9_-]{1,64}$/;
79141
79207
  function diagFile(home3, sessionId2) {
79142
79208
  if (!ID.test(sessionId2)) throw new Error(`id sesi tak sah untuk diag: ${sessionId2}`);
79143
- return join41(home3, "diag", `${sessionId2}.jsonl`);
79209
+ return join42(home3, "diag", `${sessionId2}.jsonl`);
79144
79210
  }
79145
79211
  function usable(ev) {
79146
79212
  if (!ev || typeof ev !== "object") return false;
@@ -79151,7 +79217,7 @@ function appendDiag(home3, sessionId2, events) {
79151
79217
  const file = diagFile(home3, sessionId2);
79152
79218
  const rows = events.filter(usable);
79153
79219
  if (!rows.length) return;
79154
- mkdirSync17(join41(home3, "diag"), { recursive: true });
79220
+ mkdirSync17(join42(home3, "diag"), { recursive: true });
79155
79221
  try {
79156
79222
  if (statSync6(file).size > DIAG_MAX_BYTES) rmSync11(file);
79157
79223
  } catch {
@@ -79167,19 +79233,19 @@ import { randomUUID as randomUUID12 } from "node:crypto";
79167
79233
  import { rmSync as rmSync12 } from "node:fs";
79168
79234
 
79169
79235
  // src/services/runtime-accounts/home.ts
79170
- import { chmodSync as chmodSync10, existsSync as existsSync21, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readlinkSync as readlinkSync3, renameSync as renameSync7, symlinkSync as symlinkSync2, writeFileSync as writeFileSync18 } from "node:fs";
79171
- import { join as join43 } from "node:path";
79236
+ import { chmodSync as chmodSync10, existsSync as existsSync22, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readlinkSync as readlinkSync3, renameSync as renameSync7, symlinkSync as symlinkSync2, writeFileSync as writeFileSync18 } from "node:fs";
79237
+ import { join as join44 } from "node:path";
79172
79238
 
79173
79239
  // src/services/runtime-accounts/paths.ts
79174
79240
  init_src2();
79175
- import { homedir as homedir10 } from "node:os";
79176
- import { join as join42, resolve as resolve23, sep as sep11 } from "node:path";
79177
- var accountsRoot = (env = process.env) => join42(resolveHome(env), "accounts");
79178
- var accountHomeDir = (runtime, id2, env = process.env) => join42(accountsRoot(env), runtime, id2);
79241
+ import { homedir as homedir11 } from "node:os";
79242
+ import { join as join43, resolve as resolve23, sep as sep11 } from "node:path";
79243
+ var accountsRoot = (env = process.env) => join43(resolveHome(env), "accounts");
79244
+ var accountHomeDir = (runtime, id2, env = process.env) => join43(accountsRoot(env), runtime, id2);
79179
79245
  var baseHome = (runtime, env = process.env) => agentSkillHome(runtime, env);
79180
- function baseClaudeJson(env = process.env, osHome = homedir10()) {
79246
+ function baseClaudeJson(env = process.env, osHome = homedir11()) {
79181
79247
  const own = env.HANOMAN_CLAUDE_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim();
79182
- return own ? join42(own, ".claude.json") : join42(osHome, ".claude.json");
79248
+ return own ? join43(own, ".claude.json") : join43(osHome, ".claude.json");
79183
79249
  }
79184
79250
  function insideAccountsRoot(path, env = process.env) {
79185
79251
  return resolve23(path).startsWith(resolve23(accountsRoot(env)) + sep11);
@@ -79207,19 +79273,19 @@ function classify(target3, link2) {
79207
79273
  } catch {
79208
79274
  st2 = null;
79209
79275
  }
79210
- if (!st2) return existsSync21(target3) ? "absent" : "no-target";
79276
+ if (!st2) return existsSync22(target3) ? "absent" : "no-target";
79211
79277
  if (st2.isSymbolicLink()) return readlinkSync3(link2) === target3 ? "ok" : "drift";
79212
79278
  return "drift";
79213
79279
  }
79214
79280
  function inspectAccountHome(runtime, homeDir, base3 = baseHome(runtime)) {
79215
- return SHARED_ENTRIES[runtime].filter((e) => classify(join43(base3, e), join43(homeDir, e)) === "drift");
79281
+ return SHARED_ENTRIES[runtime].filter((e) => classify(join44(base3, e), join44(homeDir, e)) === "drift");
79216
79282
  }
79217
79283
  function ensureAccountHome(runtime, homeDir, base3 = baseHome(runtime)) {
79218
79284
  mkdirSync18(homeDir, { recursive: true, mode: 448 });
79219
79285
  chmodSync10(homeDir, 448);
79220
79286
  const linked = [], drift = [];
79221
79287
  for (const entry2 of SHARED_ENTRIES[runtime]) {
79222
- const target3 = join43(base3, entry2), link2 = join43(homeDir, entry2);
79288
+ const target3 = join44(base3, entry2), link2 = join44(homeDir, entry2);
79223
79289
  const state = classify(target3, link2);
79224
79290
  if (state === "drift") drift.push(entry2);
79225
79291
  else if (state === "absent") {
@@ -79236,7 +79302,7 @@ function mirrorClaudeJson(accountHome, baseJsonPath = baseClaudeJson()) {
79236
79302
  } catch {
79237
79303
  return;
79238
79304
  }
79239
- const path = join43(accountHome, ".claude.json");
79305
+ const path = join44(accountHome, ".claude.json");
79240
79306
  let acct = {};
79241
79307
  try {
79242
79308
  acct = JSON.parse(readFileSync20(path, "utf8"));
@@ -79267,7 +79333,7 @@ init_keychain();
79267
79333
  init_pty();
79268
79334
  import { execFile as execFile17 } from "node:child_process";
79269
79335
  import { readFileSync as readFileSync21 } from "node:fs";
79270
- import { join as join44 } from "node:path";
79336
+ import { join as join45 } from "node:path";
79271
79337
  var NONE2 = { loggedIn: false, identity: null };
79272
79338
  var execOut = (bin, args, env) => new Promise((res, rej) => {
79273
79339
  execFile17(bin, args, { env, encoding: "utf8", timeout: 15e3 }, (err, stdout) => stdout ? res(stdout) : rej(err));
@@ -79303,7 +79369,7 @@ function decodeJwtClaims(token) {
79303
79369
  function probeCodexLogin(codexHome2) {
79304
79370
  let j;
79305
79371
  try {
79306
- j = JSON.parse(readFileSync21(join44(codexHome2, "auth.json"), "utf8"));
79372
+ j = JSON.parse(readFileSync21(join45(codexHome2, "auth.json"), "utf8"));
79307
79373
  } catch {
79308
79374
  return NONE2;
79309
79375
  }
@@ -79523,8 +79589,8 @@ init_antigravity_limits();
79523
79589
  init_pty();
79524
79590
  init_keychain();
79525
79591
  import { chmodSync as chmodSync11, mkdirSync as mkdirSync19, readFileSync as readFileSync22, rmSync as rmSync13, writeFileSync as writeFileSync19 } from "node:fs";
79526
- import { homedir as homedir11 } from "node:os";
79527
- import { join as join45 } from "node:path";
79592
+ import { homedir as homedir12 } from "node:os";
79593
+ import { join as join46 } from "node:path";
79528
79594
  var BUILTIN = builtinAccountId("antigravity");
79529
79595
  var isAgyLoginSession = (sessionId2) => sessionId2 === "acct-login-default-antigravity" || accountSnapshot().rows.some((r) => r.runtime === "antigravity" && sessionId2 === `acct-login-${r.id}`);
79530
79596
  var liveAgySessions = () => listSessions().filter((s2) => !s2.exited && (s2.agent === "antigravity" || isAgyLoginSession(s2.id)));
@@ -79542,20 +79608,20 @@ var fsAgyIo = () => ({
79542
79608
  chmodSync11(p3, 384);
79543
79609
  },
79544
79610
  removeFile: (p3) => rmSync13(p3, { force: true }),
79545
- tokenFile: join45(homedir11(), ".gemini", "antigravity-cli", "antigravity-oauth-token"),
79546
- accountsDir: join45(accountsRoot(), "antigravity"),
79611
+ tokenFile: join46(homedir12(), ".gemini", "antigravity-cli", "antigravity-oauth-token"),
79612
+ accountsDir: join46(accountsRoot(), "antigravity"),
79547
79613
  // Sesi agy hidup memakai akun yang AKTIF saat ini (satu Keychain), apa pun @hanoman_account-nya.
79548
79614
  liveAgyAccountIds: () => liveAgySessions().map((s2) => s2.accountId ?? BUILTIN)
79549
79615
  });
79550
- var slotDir = (io, id2) => join45(io.accountsDir, isBuiltinAccountId(id2) ? "default" : id2);
79551
- var activeFile = (io) => join45(io.accountsDir, "active");
79616
+ var slotDir = (io, id2) => join46(io.accountsDir, isBuiltinAccountId(id2) ? "default" : id2);
79617
+ var activeFile = (io) => join46(io.accountsDir, "active");
79552
79618
  function activeAgyAccount(io = fsAgyIo()) {
79553
79619
  return io.readFile(activeFile(io))?.trim() || BUILTIN;
79554
79620
  }
79555
79621
  function readSlot(accountId, io = fsAgyIo()) {
79556
79622
  let raw;
79557
79623
  try {
79558
- raw = readFileSync22(join45(slotDir(io, accountId), "slot.enc"), "utf8");
79624
+ raw = readFileSync22(join46(slotDir(io, accountId), "slot.enc"), "utf8");
79559
79625
  } catch {
79560
79626
  return null;
79561
79627
  }
@@ -79570,7 +79636,7 @@ function readSlot(accountId, io = fsAgyIo()) {
79570
79636
  function writeSlot(accountId, slot, io) {
79571
79637
  const d = slotDir(io, accountId);
79572
79638
  mkdirSync19(d, { recursive: true, mode: 448 });
79573
- const p3 = join45(d, "slot.enc");
79639
+ const p3 = join46(d, "slot.enc");
79574
79640
  writeFileSync19(p3, encryptSecret(JSON.stringify(slot)), { mode: 384 });
79575
79641
  chmodSync11(p3, 384);
79576
79642
  }
@@ -79783,7 +79849,7 @@ init_src();
79783
79849
  init_db();
79784
79850
  import { createHash as createHash20, randomUUID as randomUUID13 } from "node:crypto";
79785
79851
  import { execFileSync as execFileSync9 } from "node:child_process";
79786
- import { homedir as homedir12 } from "node:os";
79852
+ import { homedir as homedir13 } from "node:os";
79787
79853
  import { readFileSync as readFileSync23, realpathSync as realpathSync11, statSync as statSync7 } from "node:fs";
79788
79854
  import { isAbsolute as isAbsolute12, relative as relative8, resolve as resolve24 } from "node:path";
79789
79855
 
@@ -79793,7 +79859,7 @@ init_session_event_spool();
79793
79859
  init_ingress_policy();
79794
79860
  import { constants as constants4 } from "node:fs";
79795
79861
  import { mkdir as mkdir8, open as open4, readdir as readdir9, rm as rm7 } from "node:fs/promises";
79796
- import { join as join46 } from "node:path";
79862
+ import { join as join47 } from "node:path";
79797
79863
  var MAX_EVENT_BYTES = 1e6;
79798
79864
  var MAX_FILES_PER_DRAIN = 1e3;
79799
79865
  var SESSION_ID_RE = /^[a-z0-9_-]+$/;
@@ -79844,7 +79910,7 @@ async function drainSpool(app3, root, observation, host2) {
79844
79910
  let readBuffer;
79845
79911
  for (const session of await readdir9(root, { withFileTypes: true })) {
79846
79912
  if (!session.isDirectory() || !SESSION_ID_RE.test(session.name)) continue;
79847
- const dir2 = join46(root, session.name);
79913
+ const dir2 = join47(root, session.name);
79848
79914
  let entries4;
79849
79915
  try {
79850
79916
  entries4 = await readdir9(dir2, { withFileTypes: true });
@@ -79856,7 +79922,7 @@ async function drainSpool(app3, root, observation, host2) {
79856
79922
  if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue;
79857
79923
  const eventAt = /^(\d{1,15})-/.exec(entry2.name)?.[1];
79858
79924
  if (++examined > MAX_FILES_PER_DRAIN) return delivered;
79859
- const path = join46(dir2, entry2.name);
79925
+ const path = join47(dir2, entry2.name);
79860
79926
  let payload;
79861
79927
  let handle;
79862
79928
  try {
@@ -79974,8 +80040,8 @@ var utf8Prefix = (value, maxBytes) => {
79974
80040
  return out4;
79975
80041
  };
79976
80042
  var transcriptRoots = () => [
79977
- resolve24(process.env.CLAUDE_CONFIG_DIR ?? `${homedir12()}/.claude`),
79978
- resolve24(process.env.CODEX_HOME ?? `${homedir12()}/.codex`),
80043
+ resolve24(process.env.CLAUDE_CONFIG_DIR ?? `${homedir13()}/.claude`),
80044
+ resolve24(process.env.CODEX_HOME ?? `${homedir13()}/.codex`),
79979
80045
  // ADR-0234 · home akun runtime (CLAUDE_CONFIG_DIR/CODEX_HOME per sesi). Transkrip claude akun lewat
79980
80046
  // symlink `projects/` tetap berada di bawah akar ini.
79981
80047
  resolve24(accountsRoot())
@@ -80344,13 +80410,13 @@ init_ask();
80344
80410
  // src/services/decision-guard.ts
80345
80411
  init_src2();
80346
80412
  init_db();
80347
- import { mkdirSync as mkdirSync20, readFileSync as readFileSync24, readdirSync as readdirSync11, rmSync as rmSync14, statSync as statSync8, writeFileSync as writeFileSync20 } from "node:fs";
80348
- import { join as join47 } from "node:path";
80413
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync24, readdirSync as readdirSync12, rmSync as rmSync14, statSync as statSync8, writeFileSync as writeFileSync20 } from "node:fs";
80414
+ import { join as join48 } from "node:path";
80349
80415
  var SESSION_HEADER = "x-hanoman-session";
80350
80416
  var SAFE_ID = /^[A-Za-z0-9._-]{1,200}$/;
80351
80417
  var launchers = /* @__PURE__ */ new Map();
80352
80418
  var STALE_MS = 30 * 24 * 60 * 6e4;
80353
- var launcherDir = (env = process.env) => join47(resolveHome(env), "session-launchers");
80419
+ var launcherDir = (env = process.env) => join48(resolveHome(env), "session-launchers");
80354
80420
  function recordLauncher(sessionId2, agentId, env = process.env) {
80355
80421
  if (!SAFE_ID.test(sessionId2)) return;
80356
80422
  const known2 = launchersFor(sessionId2, env);
@@ -80358,11 +80424,11 @@ function recordLauncher(sessionId2, agentId, env = process.env) {
80358
80424
  try {
80359
80425
  const dir2 = launcherDir(env);
80360
80426
  mkdirSync20(dir2, { recursive: true, mode: 448 });
80361
- writeFileSync20(join47(dir2, sessionId2), [...known2].join("\n"), { mode: 384 });
80427
+ writeFileSync20(join48(dir2, sessionId2), [...known2].join("\n"), { mode: 384 });
80362
80428
  const now = Date.now();
80363
- for (const f of readdirSync11(dir2)) {
80429
+ for (const f of readdirSync12(dir2)) {
80364
80430
  try {
80365
- if (now - statSync8(join47(dir2, f)).mtimeMs > STALE_MS) rmSync14(join47(dir2, f), { force: true });
80431
+ if (now - statSync8(join48(dir2, f)).mtimeMs > STALE_MS) rmSync14(join48(dir2, f), { force: true });
80366
80432
  } catch {
80367
80433
  }
80368
80434
  }
@@ -80374,7 +80440,7 @@ function launchersFor(sessionId2, env) {
80374
80440
  if (hot) return hot;
80375
80441
  const ids = /* @__PURE__ */ new Set();
80376
80442
  try {
80377
- for (const l of readFileSync24(join47(launcherDir(env), sessionId2), "utf8").split("\n")) if (l.trim()) ids.add(l.trim());
80443
+ for (const l of readFileSync24(join48(launcherDir(env), sessionId2), "utf8").split("\n")) if (l.trim()) ids.add(l.trim());
80378
80444
  } catch {
80379
80445
  }
80380
80446
  launchers.set(sessionId2, ids);
@@ -80837,7 +80903,7 @@ async function terminal_default(app3, opts2) {
80837
80903
  if (!repoDir) return { ok: false, code: 409, msg: "project belum punya repoDir" };
80838
80904
  if (!s2.cwd.includes("/.worktrees/"))
80839
80905
  return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu", empty: "no-worktree" };
80840
- if (!existsSync22(s2.cwd))
80906
+ if (!existsSync23(s2.cwd))
80841
80907
  return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu", empty: "no-commit" };
80842
80908
  return { ok: true, id: s2.id, repoDir };
80843
80909
  };
@@ -81100,8 +81166,8 @@ async function terminalWorkspaceRoutes(app3) {
81100
81166
  // src/routes/vps.ts
81101
81167
  init_db();
81102
81168
  init_src();
81103
- import { readFileSync as readFileSync29, existsSync as existsSync25 } from "node:fs";
81104
- import { homedir as homedir14 } from "node:os";
81169
+ import { readFileSync as readFileSync29, existsSync as existsSync26 } from "node:fs";
81170
+ import { homedir as homedir15 } from "node:os";
81105
81171
 
81106
81172
  // src/vps/catalog/catalog.data.ts
81107
81173
  var RAW_SECTIONS = [{ "id": "ssh", "title": "SSH Hardening", "icon": "\u{1F511}", "count": 14 }, { "id": "firewall", "title": "Firewall & Network", "icon": "\u{1F525}", "count": 14 }, { "id": "aapanel", "title": "aaPanel Security", "icon": "\u{1F5A5}\uFE0F", "count": 14 }, { "id": "user", "title": "User & Permission Management", "icon": "\u{1F464}", "count": 14 }, { "id": "system", "title": "System Hardening (OS Level)", "icon": "\u2699\uFE0F", "count": 16 }, { "id": "webserver", "title": "Web Server Hardening", "icon": "\u{1F310}", "count": 14 }, { "id": "database", "title": "Database Security", "icon": "\u{1F5C4}\uFE0F", "count": 15 }, { "id": "ssl", "title": "SSL/TLS & Enkripsi", "icon": "\u{1F512}", "count": 13 }, { "id": "ids", "title": "Intrusion Detection & Prevention", "icon": "\u{1F6A8}", "count": 9 }, { "id": "logging", "title": "Logging & Monitoring", "icon": "\u{1F4CA}", "count": 13 }, { "id": "backup", "title": "Backup & Disaster Recovery", "icon": "\u{1F4BE}", "count": 12 }, { "id": "ddos", "title": "DDoS Protection", "icon": "\u{1F30A}", "count": 11 }, { "id": "kernel", "title": "Kernel Hardening", "icon": "\u{1F9E0}", "count": 12 }, { "id": "malware", "title": "File Integrity & Malware Detection", "icon": "\u{1F9A0}", "count": 7 }, { "id": "enterprise", "title": "Advanced Enterprise & Government Level", "icon": "\u{1F3E2}", "count": 31 }, { "id": "checklist", "title": "Checklist Rutin", "icon": "\u2705", "count": 23 }];
@@ -81315,7 +81381,7 @@ init_config2();
81315
81381
  import { spawn as spawn6 } from "node:child_process";
81316
81382
  import { mkdtempSync as mkdtempSync4, rmSync as rmSync15, writeFileSync as writeFileSync21 } from "node:fs";
81317
81383
  import { tmpdir as tmpdir7 } from "node:os";
81318
- import { dirname as dirname17, join as join48 } from "node:path";
81384
+ import { dirname as dirname17, join as join49 } from "node:path";
81319
81385
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
81320
81386
  function consoleArgv(t) {
81321
81387
  return [
@@ -81330,8 +81396,8 @@ function consoleArgv(t) {
81330
81396
  ];
81331
81397
  }
81332
81398
  function askpassScript() {
81333
- const dir2 = mkdtempSync4(join48(tmpdir7(), "hanoman-askpass-"));
81334
- const path = join48(dir2, "askpass.sh");
81399
+ const dir2 = mkdtempSync4(join49(tmpdir7(), "hanoman-askpass-"));
81400
+ const path = join49(dir2, "askpass.sh");
81335
81401
  writeFileSync21(path, `#!/bin/sh
81336
81402
  printf '%s' "$HANOMAN_SSH_PASSWORD"
81337
81403
  `, { mode: 448 });
@@ -81392,8 +81458,8 @@ function sshExec(t, remoteCmd, opts2 = {}) {
81392
81458
  // src/services/vps-audit.ts
81393
81459
  init_db();
81394
81460
  init_deps();
81395
- import { existsSync as existsSync23, readFileSync as readFileSync25 } from "node:fs";
81396
- import { join as join49 } from "node:path";
81461
+ import { existsSync as existsSync24, readFileSync as readFileSync25 } from "node:fs";
81462
+ import { join as join50 } from "node:path";
81397
81463
  import { fileURLToPath as fileURLToPath5 } from "node:url";
81398
81464
  init_sync_notify();
81399
81465
 
@@ -81498,7 +81564,7 @@ var packagedScript = (f) => fileURLToPath5(new URL(`../scripts/vps/${f}`, import
81498
81564
  var moduleDir2 = () => fileURLToPath5(new URL(".", import.meta.url));
81499
81565
  var scriptPath = (f) => {
81500
81566
  const packed = packagedScript(f);
81501
- return existsSync23(packed) ? packed : join49(repoRoot(moduleDir2()), "server", "scripts", "vps", f);
81567
+ return existsSync24(packed) ? packed : join50(repoRoot(moduleDir2()), "server", "scripts", "vps", f);
81502
81568
  };
81503
81569
  async function itemStatesOf(vpsId) {
81504
81570
  const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
@@ -81743,30 +81809,30 @@ async function listVpsWithScore() {
81743
81809
  init_src2();
81744
81810
  init_config2();
81745
81811
  import { execFileSync as execFileSync11 } from "node:child_process";
81746
- import { chmodSync as chmodSync12, copyFileSync, existsSync as existsSync24, mkdirSync as mkdirSync21, readFileSync as readFileSync28, unlinkSync as unlinkSync5 } from "node:fs";
81747
- import { homedir as homedir13 } from "node:os";
81748
- import { join as join50 } from "node:path";
81812
+ import { chmodSync as chmodSync12, copyFileSync, existsSync as existsSync25, mkdirSync as mkdirSync21, readFileSync as readFileSync28, unlinkSync as unlinkSync5 } from "node:fs";
81813
+ import { homedir as homedir14 } from "node:os";
81814
+ import { join as join51 } from "node:path";
81749
81815
  var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
81750
81816
  var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
81751
81817
  function adoptLegacyKey(dir2) {
81752
81818
  if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
81753
- const legacy = join50(homedir13(), ".hanoman");
81819
+ const legacy = join51(homedir14(), ".hanoman");
81754
81820
  if (legacy === dir2) return;
81755
- if (!KEY_FILES.every((f) => existsSync24(join50(legacy, f)))) return;
81821
+ if (!KEY_FILES.every((f) => existsSync25(join51(legacy, f)))) return;
81756
81822
  mkdirSync21(dir2, { recursive: true, mode: 448 });
81757
81823
  for (const f of KEY_FILES) {
81758
- copyFileSync(join50(legacy, f), join50(dir2, f));
81759
- chmodSync12(join50(dir2, f), 384);
81824
+ copyFileSync(join51(legacy, f), join51(dir2, f));
81825
+ chmodSync12(join51(dir2, f), 384);
81760
81826
  }
81761
- for (const f of KEY_FILES) unlinkSync5(join50(legacy, f));
81827
+ for (const f of KEY_FILES) unlinkSync5(join51(legacy, f));
81762
81828
  console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
81763
81829
  }
81764
81830
  function ensureHanomanKey() {
81765
81831
  const dir2 = keyDir();
81766
- const privPath = join50(dir2, KEY_FILES[0]);
81832
+ const privPath = join51(dir2, KEY_FILES[0]);
81767
81833
  const pubPath = `${privPath}.pub`;
81768
- if (!existsSync24(privPath)) adoptLegacyKey(dir2);
81769
- if (!existsSync24(privPath)) {
81834
+ if (!existsSync25(privPath)) adoptLegacyKey(dir2);
81835
+ if (!existsSync25(privPath)) {
81770
81836
  mkdirSync21(dir2, { recursive: true, mode: 448 });
81771
81837
  execFileSync11(
81772
81838
  "ssh-keygen",
@@ -81794,7 +81860,7 @@ init_session_launch_gate();
81794
81860
  init_settings3();
81795
81861
  init_sync_notify();
81796
81862
  function keyMissing(v) {
81797
- return !!v.keyPath && !existsSync25(v.keyPath);
81863
+ return !!v.keyPath && !existsSync26(v.keyPath);
81798
81864
  }
81799
81865
  async function vps_default(app3) {
81800
81866
  app3.get("/vps", async () => listVpsWithScore());
@@ -82047,7 +82113,7 @@ async function vps_default(app3) {
82047
82113
  const v = await prisma.vps.findUnique({ where: { id: req.params.id } });
82048
82114
  if (!v) return reply.code(404).send({ error: "not found" });
82049
82115
  if (keyMissing(v)) return reply.code(409).send({ error: "key VPS tidak ada di mesin ini", keyMissing: true });
82050
- const s2 = await createOperatorSession(`vps-console:${v.id}`, homedir14(), { id: `vpsc-${v.id}`, command: consoleArgv(v) });
82116
+ const s2 = await createOperatorSession(`vps-console:${v.id}`, homedir15(), { id: `vpsc-${v.id}`, command: consoleArgv(v) });
82051
82117
  return reply.code(201).send({ id: s2.id });
82052
82118
  });
82053
82119
  app3.post("/vps/:id/session", async (req, reply) => {
@@ -82056,7 +82122,7 @@ async function vps_default(app3) {
82056
82122
  if (keyMissing(v)) return reply.code(409).send({ error: "key VPS tidak ada di mesin ini", keyMissing: true });
82057
82123
  const checks = v.audit ?? [];
82058
82124
  const { model, effort: effort2 } = await sessionModel();
82059
- const s2 = await createAgentSession(`vps:${v.id}`, homedir14(), {
82125
+ const s2 = await createAgentSession(`vps:${v.id}`, homedir15(), {
82060
82126
  model,
82061
82127
  effort: effort2,
82062
82128
  prompt: [
@@ -82083,7 +82149,7 @@ init_codex_limits();
82083
82149
  init_antigravity_limits();
82084
82150
  init_keychain();
82085
82151
  import { readFileSync as readFileSync30 } from "node:fs";
82086
- import { join as join51 } from "node:path";
82152
+ import { join as join52 } from "node:path";
82087
82153
  var TTL_MS5 = 3e4;
82088
82154
  var UNAVAILABLE4 = { status: "unavailable", windows: [], fetchedAt: null };
82089
82155
  function readClaudeAccountToken(homeDir, platform2 = process.platform) {
@@ -82092,7 +82158,7 @@ function readClaudeAccountToken(homeDir, platform2 = process.platform) {
82092
82158
  if (t) return t;
82093
82159
  }
82094
82160
  try {
82095
- const t = JSON.parse(readFileSync30(join51(homeDir, ".credentials.json"), "utf8"))?.claudeAiOauth?.accessToken;
82161
+ const t = JSON.parse(readFileSync30(join52(homeDir, ".credentials.json"), "utf8"))?.claudeAiOauth?.accessToken;
82096
82162
  return typeof t === "string" && t ? t : null;
82097
82163
  } catch {
82098
82164
  return null;
@@ -82132,7 +82198,7 @@ async function loadRow(row, d) {
82132
82198
  return windows ? { status: "ok", windows, fetchedAt: new Date(d.now()).toISOString() } : UNAVAILABLE4;
82133
82199
  }
82134
82200
  if (row.runtime === "codex") {
82135
- const c = await d.codexLimits(join51(row.homeDir, "sessions"));
82201
+ const c = await d.codexLimits(join52(row.homeDir, "sessions"));
82136
82202
  return { status: c.status, windows: c.windows, fetchedAt: c.fetchedAt };
82137
82203
  }
82138
82204
  return d.antigravity(row);
@@ -82168,7 +82234,7 @@ init_session_sandbox();
82168
82234
  init_src();
82169
82235
  init_pty();
82170
82236
  init_session_launch_gate();
82171
- import { homedir as homedir15 } from "node:os";
82237
+ import { homedir as homedir16 } from "node:os";
82172
82238
  var LOGIN_PREFIX = "acct-login-";
82173
82239
  var loginSessionId = (accountId) => `${LOGIN_PREFIX}${accountId.replace(/[^a-z0-9]/gi, "-")}`;
82174
82240
  function accountIdForLoginSession(sessionId2) {
@@ -82206,7 +82272,7 @@ async function openLoginSession(accountId, opts2) {
82206
82272
  }
82207
82273
  const argv = loginArgv(runtime, opts2);
82208
82274
  beforeLogin(runtime, accountId);
82209
- const s2 = await createOperatorSession(`account:${accountId}`, homeDir ?? homedir15(), {
82275
+ const s2 = await createOperatorSession(`account:${accountId}`, homeDir ?? homedir16(), {
82210
82276
  id: loginSessionId(accountId),
82211
82277
  command: argv,
82212
82278
  env
@@ -82375,7 +82441,7 @@ init_src2();
82375
82441
  init_src();
82376
82442
  import { randomUUID as randomUUID15 } from "node:crypto";
82377
82443
  import { readFile as readFile9, writeFile as writeFile6, rename as rename5, mkdir as mkdir9, rm as rm9 } from "node:fs/promises";
82378
- import { dirname as dirname18, join as join53 } from "node:path";
82444
+ import { dirname as dirname18, join as join54 } from "node:path";
82379
82445
 
82380
82446
  // src/services/model-catalog-parser.ts
82381
82447
  init_zod();
@@ -82482,7 +82548,7 @@ init_session_sandbox();
82482
82548
  import { spawn as spawn7 } from "node:child_process";
82483
82549
  import { mkdtemp as mkdtemp2, rm as rm8 } from "node:fs/promises";
82484
82550
  import { tmpdir as tmpdir8 } from "node:os";
82485
- import { join as join52 } from "node:path";
82551
+ import { join as join53 } from "node:path";
82486
82552
  var MAX_BYTES = 4 * 1024 * 1024;
82487
82553
  var TIMEOUT_MS = 2e4;
82488
82554
  var quote3 = (s2) => "'" + s2.replaceAll("'", "'\\''") + "'";
@@ -82581,7 +82647,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
82581
82647
  }
82582
82648
  async function probeModelCatalog(agent) {
82583
82649
  assertAntigravityLaunch(agent);
82584
- const cwd = await mkdtemp2(join52(tmpdir8(), "hanoman-models-"));
82650
+ const cwd = await mkdtemp2(join53(tmpdir8(), "hanoman-models-"));
82585
82651
  const env = agentEnvironment(agent, process.env);
82586
82652
  for (const key of ["HANOMAN_CLAUDE_BIN", "HANOMAN_CODEX_BIN", "HANOMAN_PODMAN_BIN", "HANOMAN_ANTIGRAVITY_BIN"]) {
82587
82653
  const value = effectiveStr(key);
@@ -82692,7 +82758,7 @@ async function writeModelCatalogCache(file, catalog, current = () => true) {
82692
82758
  await rm9(temp, { force: true });
82693
82759
  }
82694
82760
  }
82695
- var cacheFile = () => join53(resolveHome(), "model-catalog.json");
82761
+ var cacheFile = () => join54(resolveHome(), "model-catalog.json");
82696
82762
  var modelCatalogService = createModelCatalogService({
82697
82763
  probe: probeModelCatalog,
82698
82764
  key: catalogProbeKey,
@@ -83882,7 +83948,7 @@ init_pty();
83882
83948
  init_config5();
83883
83949
  init_inbox();
83884
83950
  import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync22 } from "node:fs";
83885
- import { join as join54 } from "node:path";
83951
+ import { join as join55 } from "node:path";
83886
83952
  init_human_input();
83887
83953
  var idle = /* @__PURE__ */ new Set();
83888
83954
  var sending = /* @__PURE__ */ new Set();
@@ -83893,7 +83959,7 @@ function isLeadIdle(id2) {
83893
83959
  return idle.has(id2);
83894
83960
  }
83895
83961
  var HUMAN_QUIET_MS = 12e4;
83896
- var batchDir = (projectId) => join54(resolveHome(), "lead-batches", projectId);
83962
+ var batchDir = (projectId) => join55(resolveHome(), "lead-batches", projectId);
83897
83963
  var briefFn = async () => "(situasi tak tersedia \u2014 panggil /brief)";
83898
83964
  function setBriefBuilder(fn) {
83899
83965
  briefFn = fn;
@@ -83914,7 +83980,7 @@ var prodCourierDeps = {
83914
83980
  write: (projectId, text2) => {
83915
83981
  const dir2 = batchDir(projectId);
83916
83982
  mkdirSync22(dir2, { recursive: true });
83917
- const file = join54(dir2, `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.md`);
83983
+ const file = join55(dir2, `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.md`);
83918
83984
  writeFileSync22(file, text2);
83919
83985
  return file;
83920
83986
  },
@@ -84716,7 +84782,7 @@ init_src();
84716
84782
  init_db();
84717
84783
  import { gunzipSync, gzipSync } from "node:zlib";
84718
84784
  import { mkdir as mkdir10, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
84719
- import { join as join55 } from "node:path";
84785
+ import { join as join56 } from "node:path";
84720
84786
 
84721
84787
  // src/services/device-auth.ts
84722
84788
  function bearerToken2(req) {
@@ -84974,9 +85040,9 @@ async function sync_default(app3) {
84974
85040
  const deviceId = req.device.id;
84975
85041
  const withTranscriptKey = await Promise.all(parsed.data.entries.map(async (e) => {
84976
85042
  if (!e.transcript) return e;
84977
- const dir2 = join55(resolveDataDirs().home, "remote-transcripts", deviceId);
85043
+ const dir2 = join56(resolveDataDirs().home, "remote-transcripts", deviceId);
84978
85044
  await mkdir10(dir2, { recursive: true, mode: 448 });
84979
- const final = join55(dir2, `${e.seq}.txt`);
85045
+ const final = join56(dir2, `${e.seq}.txt`);
84980
85046
  const tmp = `${final}.tmp`;
84981
85047
  await writeFile7(tmp, e.transcript, { encoding: "utf8", mode: 384 });
84982
85048
  await rename6(tmp, final);
@@ -88381,7 +88447,7 @@ init_uploads();
88381
88447
  init_ask_via2();
88382
88448
  import { createHash as createHash23 } from "node:crypto";
88383
88449
  import { execFileSync as execFileSync12 } from "node:child_process";
88384
- import { join as join57 } from "node:path";
88450
+ import { join as join58 } from "node:path";
88385
88451
  var QaSessionError = class extends Error {
88386
88452
  constructor(code, message, needsBind = false) {
88387
88453
  super(message);
@@ -88408,7 +88474,7 @@ async function startQaFindingSession(d, fid) {
88408
88474
  const saved = await saveSessionUpload(id2, bytes, a.mimeType);
88409
88475
  files2.push(`${JSON.stringify(a.filename)} (${a.mimeType}): ${saved.path}`);
88410
88476
  }
88411
- const wt = join57(repoDir, ".worktrees", id2);
88477
+ const wt = join58(repoDir, ".worktrees", id2);
88412
88478
  const branch = `qa/${id2.slice(3)}`;
88413
88479
  const reused = realGit.worktreeAlive(wt);
88414
88480
  try {
@@ -89953,7 +90019,7 @@ init_src();
89953
90019
  init_db();
89954
90020
  import { mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync5, rmSync as rmSync16, writeFileSync as writeFileSync23 } from "node:fs";
89955
90021
  import { tmpdir as tmpdir9 } from "node:os";
89956
- import { join as join58 } from "node:path";
90022
+ import { join as join59 } from "node:path";
89957
90023
  var STAGE_LABEL = {
89958
90024
  brainstorming: "Dirumuskan",
89959
90025
  objective: "Dirumuskan",
@@ -89996,7 +90062,7 @@ function renderTicketDoc(tickets2) {
89996
90062
  ${baris.join("\n")}`;
89997
90063
  }
89998
90064
  async function buildChatWorkspace(projectId) {
89999
- const dir2 = mkdtempSync5(join58(tmpdir9(), "hanoman-portal-chat-"));
90065
+ const dir2 = mkdtempSync5(join59(tmpdir9(), "hanoman-portal-chat-"));
90000
90066
  try {
90001
90067
  const project = await prisma.project.findUnique({
90002
90068
  where: { id: projectId },
@@ -90011,7 +90077,7 @@ async function buildChatWorkspace(projectId) {
90011
90077
  });
90012
90078
  const files2 = [];
90013
90079
  const tulis = (rel, isi) => {
90014
- writeFileSync23(join58(dir2, rel), isi, { mode: 384 });
90080
+ writeFileSync23(join59(dir2, rel), isi, { mode: 384 });
90015
90081
  files2.push(rel);
90016
90082
  };
90017
90083
  tulis("project.md", renderProjectDoc(project));
@@ -90019,11 +90085,11 @@ async function buildChatWorkspace(projectId) {
90019
90085
  tulis("laporan.md", renderTicketDoc(tickets2.map((t) => toPortalTicket(t, null))));
90020
90086
  const prds = await listPrds(projectId);
90021
90087
  if (prds.length) {
90022
- mkdirSync23(join58(dir2, "dokumen"), { mode: 448 });
90088
+ mkdirSync23(join59(dir2, "dokumen"), { mode: 448 });
90023
90089
  for (const prd of prds) {
90024
90090
  const isi = await readPrd(projectId, prd.path);
90025
90091
  if (!isi) continue;
90026
- tulis(join58("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
90092
+ tulis(join59("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
90027
90093
  }
90028
90094
  }
90029
90095
  return { dir: dir2, files: files2, cleanup: () => rmSync16(dir2, { recursive: true, force: true }) };
@@ -90550,13 +90616,13 @@ init_db();
90550
90616
  // src/services/bootstrap.ts
90551
90617
  import { createHash as createHash24, randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
90552
90618
  import { chmod as chmod2, lstat as lstat3, mkdir as mkdir11, readFile as readFile10, unlink as unlink5, writeFile as writeFile8 } from "node:fs/promises";
90553
- import { join as join59 } from "node:path";
90619
+ import { join as join60 } from "node:path";
90554
90620
  var TTL_MS8 = 15 * 6e4;
90555
90621
  var SETUP_TOKEN_FILE = "setup.token";
90556
90622
  var BootstrapError = class extends Error {
90557
90623
  code = "BOOTSTRAP_PROOF";
90558
90624
  };
90559
- var tokenPath = (home3) => join59(home3, SETUP_TOKEN_FILE);
90625
+ var tokenPath = (home3) => join60(home3, SETUP_TOKEN_FILE);
90560
90626
  async function readStored(home3) {
90561
90627
  try {
90562
90628
  const info = await lstat3(tokenPath(home3));
@@ -91394,7 +91460,7 @@ init_event_log();
91394
91460
  init_transcript_store();
91395
91461
  init_src2();
91396
91462
  import { readFile as readFile11 } from "node:fs/promises";
91397
- import { join as join60 } from "node:path";
91463
+ import { join as join61 } from "node:path";
91398
91464
  function decodeCursor(c) {
91399
91465
  if (!c) return null;
91400
91466
  try {
@@ -91432,7 +91498,7 @@ async function readRemoteTranscript(id2) {
91432
91498
  const row = await prisma.logEntry.findUnique({ where: { id: id2 } });
91433
91499
  if (!row || row.lane !== "transcript" || !row.transcriptKey) return null;
91434
91500
  if (row.deviceId === "local") return readTranscript(row.transcriptKey);
91435
- const path = join60(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
91501
+ const path = join61(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
91436
91502
  try {
91437
91503
  return await readFile11(path, "utf8");
91438
91504
  } catch {
@@ -91629,7 +91695,7 @@ var SETUP_PUBLIC = /* @__PURE__ */ new Set([
91629
91695
  "POST /api/setup"
91630
91696
  ]);
91631
91697
  function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {}) {
91632
- const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname19(fileURLToPath6(import.meta.url)), env, existsSync26);
91698
+ const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname19(fileURLToPath6(import.meta.url)), env, existsSync27);
91633
91699
  const ingress = loadIngressPolicy(env);
91634
91700
  const app3 = Fastify({ logger: false, trustProxy: trustProxyFromEnv(env) });
91635
91701
  app3.setErrorHandler((error, _req, reply) => {
@@ -91779,7 +91845,7 @@ function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {})
91779
91845
  await api.register(qaBacklog);
91780
91846
  }, { prefix: "/api" });
91781
91847
  if (shouldServeWeb(env)) {
91782
- const dist = pickWebDir(dirname19(fileURLToPath6(import.meta.url)), env, existsSync26);
91848
+ const dist = pickWebDir(dirname19(fileURLToPath6(import.meta.url)), env, existsSync27);
91783
91849
  if (dist) {
91784
91850
  app3.register(fastifyStatic, { root: dist });
91785
91851
  app3.setNotFoundHandler((req, reply) => req.url.startsWith("/api") ? reply.code(404).send({ error: "not found" }) : req.url.startsWith("/assets/") ? reply.code(404).type("text/plain").send("not found") : reply.sendFile("index.html"));
@@ -92179,9 +92245,46 @@ function registerTriaseSource() {
92179
92245
  // src/services/session-boot-resume.ts
92180
92246
  init_src();
92181
92247
  init_db();
92248
+ import { existsSync as existsSync28 } from "node:fs";
92182
92249
  init_session_launch();
92250
+ init_session_launch_gate();
92183
92251
  init_session_admission2();
92184
92252
  init_notifications2();
92253
+ init_local_binding();
92254
+ init_session_phases();
92255
+ init_pty();
92256
+ var prodFreeStart = async (c) => {
92257
+ if (!existsSync28(c.cwd)) throw new Error(`direktori ${c.cwd} sudah tidak ada`);
92258
+ if (c.kind === "shell") {
92259
+ await createOperatorSession(c.projectId, c.cwd, { id: c.sessionId, command: [shellBin()] });
92260
+ return;
92261
+ }
92262
+ if (c.accountId && !accountExists(c.accountId))
92263
+ throw new Error(`akun runtime ${c.accountId} sudah dihapus \u2014 buka sesi baru dan pilih akun lain`);
92264
+ const base3 = {
92265
+ id: c.sessionId,
92266
+ agent: c.agent,
92267
+ model: c.model,
92268
+ effort: c.effort,
92269
+ accountId: c.accountId,
92270
+ ...c.branch ? { branch: c.branch } : {},
92271
+ ...c.agentSessionId ? { agentSessionId: c.agentSessionId, resumeAgentSession: true } : {}
92272
+ };
92273
+ if (c.kind === "terminal" || c.kind === "worktree") {
92274
+ await createOperatorSession(c.projectId, c.cwd, base3, { exempt: false });
92275
+ return;
92276
+ }
92277
+ if (c.agent !== "claude" || !c.agentSessionId)
92278
+ throw new Error("percakapan agennya tak tercatat \u2014 mulai ulang dari layar project");
92279
+ const repoDir = await resolveRepoDir(c.projectId);
92280
+ if (!repoDir) throw new Error("project belum di-bind ke checkout lokal");
92281
+ await createOperatorSession(c.projectId, c.cwd, {
92282
+ ...base3,
92283
+ flow: c.flow,
92284
+ phaseFile: phaseFilePath(repoDir, c.sessionId),
92285
+ decisionFile: decisionFilePath(repoDir, c.sessionId)
92286
+ }, { exempt: false });
92287
+ };
92185
92288
  var prodResumeStart = async (spec, runtime) => {
92186
92289
  if (runtime?.accountId && !accountExists(runtime.accountId))
92187
92290
  throw new Error(`akun runtime ${runtime.accountId} sudah dihapus \u2014 lanjutkan manual dan pilih akun lain`);
@@ -92194,12 +92297,50 @@ var prodDeps6 = {
92194
92297
  // titik waktu paling rawan menumpuk kandidat resume. Gerbang dependency ADR-0093 tak berubah.
92195
92298
  startSpec: prodResumeStart,
92196
92299
  recordFail: recordFailure,
92197
- recordDeferred: recordFailure
92300
+ recordDeferred: recordFailure,
92301
+ startFree: prodFreeStart,
92302
+ recordFreeIssue: recordFreeResumeIssue
92198
92303
  };
92199
92304
  async function resumeReconciledSessions(cutoff, deps = prodDeps6) {
92200
92305
  const report = { resumed: [], failed: [], deferred: [] };
92306
+ await resumeSpecs(cutoff, deps, report);
92307
+ await resumeFree(cutoff, deps, report);
92308
+ return report;
92309
+ }
92310
+ async function resumeFree(cutoff, deps, report) {
92311
+ if (!deps.startFree) return;
92312
+ const candidates2 = await reconciledFreeSessionsSince(cutoff);
92313
+ const note = deps.recordFreeIssue ?? (async () => {
92314
+ });
92315
+ for (const c of candidates2) {
92316
+ const title = `sesi ${c.sessionId}`;
92317
+ try {
92318
+ await deps.startFree(c);
92319
+ report.resumed.push(c.sessionId);
92320
+ } catch (e) {
92321
+ if (e instanceof LaunchAdmissionError) {
92322
+ await note(
92323
+ c.sessionId,
92324
+ title,
92325
+ c.projectId,
92326
+ `ditunda \u2014 cap/beban host penuh saat boot (${e.kind}). Buka lagi manual saat slot kosong.`
92327
+ );
92328
+ report.deferred.push(c.sessionId);
92329
+ continue;
92330
+ }
92331
+ await note(
92332
+ c.sessionId,
92333
+ title,
92334
+ c.projectId,
92335
+ `gagal dilanjutkan otomatis \u2014 ${e instanceof Error ? e.message : String(e)}`
92336
+ );
92337
+ report.failed.push(c.sessionId);
92338
+ }
92339
+ }
92340
+ }
92341
+ async function resumeSpecs(cutoff, deps, report) {
92201
92342
  const specIds = await reconciledSpecIdsSince(cutoff);
92202
- if (!specIds.length) return report;
92343
+ if (!specIds.length) return;
92203
92344
  const specs = await prisma.spec.findMany({ where: { id: { in: specIds }, stage: { not: "done" } } });
92204
92345
  const runtimes = await reconciledRuntimesSince(cutoff);
92205
92346
  for (const spec of specs) {
@@ -92222,7 +92363,6 @@ async function resumeReconciledSessions(cutoff, deps = prodDeps6) {
92222
92363
  report.failed.push(spec.id);
92223
92364
  }
92224
92365
  }
92225
- return report;
92226
92366
  }
92227
92367
 
92228
92368
  // src/services/runtime-accounts/install.ts
@@ -92763,9 +92903,9 @@ async function pruneLogs(now, retention, opts2 = {}) {
92763
92903
  }
92764
92904
  async function reconcileRemoteTranscripts() {
92765
92905
  const { readdir: readdir10, unlink: unlink6, stat: stat8 } = await import("node:fs/promises");
92766
- const { join: join61 } = await import("node:path");
92906
+ const { join: join62 } = await import("node:path");
92767
92907
  const { resolveDataDirs: resolveDataDirs2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
92768
- const root = join61(resolveDataDirs2().home, "remote-transcripts");
92908
+ const root = join62(resolveDataDirs2().home, "remote-transcripts");
92769
92909
  let deviceDirs;
92770
92910
  try {
92771
92911
  deviceDirs = await readdir10(root);
@@ -92779,16 +92919,16 @@ async function reconcileRemoteTranscripts() {
92779
92919
  for (const dev of deviceDirs) {
92780
92920
  let files2;
92781
92921
  try {
92782
- files2 = await readdir10(join61(root, dev));
92922
+ files2 = await readdir10(join62(root, dev));
92783
92923
  } catch {
92784
92924
  continue;
92785
92925
  }
92786
92926
  for (const f of files2) {
92787
92927
  const key = `${dev}/${f}`;
92788
92928
  if (referenced.has(key)) continue;
92789
- const st2 = await stat8(join61(root, dev, f)).catch(() => null);
92929
+ const st2 = await stat8(join62(root, dev, f)).catch(() => null);
92790
92930
  if (!st2 || Date.now() - st2.mtimeMs < gracePeriodMs) continue;
92791
- await unlink6(join61(root, dev, f)).catch(() => {
92931
+ await unlink6(join62(root, dev, f)).catch(() => {
92792
92932
  });
92793
92933
  orphans++;
92794
92934
  }
@@ -93043,7 +93183,7 @@ bootstrapReady.then(async () => {
93043
93183
  for (const row of await detectOrphanWorktrees()) {
93044
93184
  console.log(`worktree yatim: ${row.projectId} \u2014 ${row.count} menunggu konfirmasi di tab Worktrees`);
93045
93185
  }
93046
- if (n2) {
93186
+ if (n2 && (await getSetting()).autoResumeSessions) {
93047
93187
  const { resumed, failed, deferred } = await resumeReconciledSessions(reconcileCutoff);
93048
93188
  if (resumed.length)
93049
93189
  console.log(`auto-resume: ${resumed.length} sesi dilanjutkan otomatis (${resumed.join(", ")})`);