zelari-code 2.43.0 → 2.44.0

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.
@@ -38898,7 +38898,7 @@ var CORE_VERSION;
38898
38898
  var init_version = __esm({
38899
38899
  "packages/core/dist/version.js"() {
38900
38900
  "use strict";
38901
- CORE_VERSION = "2.43.0";
38901
+ CORE_VERSION = "2.44.0";
38902
38902
  }
38903
38903
  });
38904
38904
 
@@ -55346,6 +55346,45 @@ var init_crossProviderFailover = __esm({
55346
55346
  }
55347
55347
  });
55348
55348
 
55349
+ // src/cli/serve/sessionControl.ts
55350
+ import { AsyncLocalStorage } from "node:async_hooks";
55351
+ function runWithSession(sessionId2, fn) {
55352
+ return dispatchContext.run({ sessionId: sessionId2, token: {} }, fn);
55353
+ }
55354
+ function getCurrentHarnessSessionId() {
55355
+ return dispatchContext.getStore()?.sessionId;
55356
+ }
55357
+ function registerLiveTurnControl(control) {
55358
+ const store6 = dispatchContext.getStore();
55359
+ if (!store6) return void 0;
55360
+ const registered = { ...control, token: store6.token };
55361
+ liveTurns.set(store6.sessionId, registered);
55362
+ return () => {
55363
+ if (liveTurns.get(store6.sessionId) === registered) {
55364
+ liveTurns.delete(store6.sessionId);
55365
+ }
55366
+ };
55367
+ }
55368
+ function getLiveTurnControl(sessionId2) {
55369
+ return liveTurns.get(sessionId2);
55370
+ }
55371
+ function clearSessionTurnControl(sessionId2) {
55372
+ const store6 = dispatchContext.getStore();
55373
+ if (!store6 || store6.sessionId !== sessionId2) return;
55374
+ const registered = liveTurns.get(sessionId2);
55375
+ if (registered && registered.token === store6.token) {
55376
+ liveTurns.delete(sessionId2);
55377
+ }
55378
+ }
55379
+ var dispatchContext, liveTurns;
55380
+ var init_sessionControl = __esm({
55381
+ "src/cli/serve/sessionControl.ts"() {
55382
+ "use strict";
55383
+ dispatchContext = new AsyncLocalStorage();
55384
+ liveTurns = /* @__PURE__ */ new Map();
55385
+ }
55386
+ });
55387
+
55349
55388
  // src/cli/safety/toolPermissions.ts
55350
55389
  var toolPermissions_exports = {};
55351
55390
  __export(toolPermissions_exports, {
@@ -55362,27 +55401,42 @@ __export(toolPermissions_exports, {
55362
55401
  parsePermissionPreset: () => parsePermissionPreset,
55363
55402
  resolveToolPermission: () => resolveToolPermission
55364
55403
  });
55365
- function grantSessionTool(toolName) {
55404
+ function grantsBucket(sessionId2) {
55405
+ const key = sessionId2 && sessionId2.trim() ? sessionId2 : GLOBAL_BUCKET;
55406
+ let bucket = grantsBySession.get(key);
55407
+ if (!bucket) {
55408
+ bucket = { tools: /* @__PURE__ */ new Set(), categories: /* @__PURE__ */ new Set() };
55409
+ grantsBySession.set(key, bucket);
55410
+ }
55411
+ return bucket;
55412
+ }
55413
+ function grantSessionTool(toolName, sessionId2) {
55366
55414
  const n = toolName.trim();
55367
- if (n) sessionToolGrants.add(n);
55415
+ if (n) grantsBucket(sessionId2).tools.add(n);
55368
55416
  }
55369
- function grantSessionCategory(cat) {
55370
- sessionCategoryGrants.add(cat);
55417
+ function grantSessionCategory(cat, sessionId2) {
55418
+ grantsBucket(sessionId2).categories.add(cat);
55371
55419
  }
55372
- function clearSessionPermissionGrants() {
55373
- sessionToolGrants.clear();
55374
- sessionCategoryGrants.clear();
55420
+ function clearSessionPermissionGrants(sessionId2) {
55421
+ if (sessionId2 === void 0) {
55422
+ grantsBySession.clear();
55423
+ return;
55424
+ }
55425
+ grantsBySession.delete(sessionId2 && sessionId2.trim() ? sessionId2 : GLOBAL_BUCKET);
55375
55426
  }
55376
- function listSessionPermissionGrants() {
55427
+ function listSessionPermissionGrants(sessionId2) {
55428
+ const bucket = grantsBucket(sessionId2);
55377
55429
  return {
55378
- tools: [...sessionToolGrants].sort(),
55379
- categories: [...sessionCategoryGrants]
55430
+ tools: [...bucket.tools].sort(),
55431
+ categories: [...bucket.categories]
55380
55432
  };
55381
55433
  }
55382
- function isSessionGranted(toolName, required2) {
55383
- if (sessionToolGrants.has(toolName)) return true;
55434
+ function isSessionGranted(toolName, required2, sessionId2) {
55435
+ const scope = sessionId2 ?? getCurrentHarnessSessionId();
55436
+ const bucket = grantsBucket(scope);
55437
+ if (bucket.tools.has(toolName)) return true;
55384
55438
  if (!required2.length) return false;
55385
- return required2.every((c) => sessionCategoryGrants.has(c));
55439
+ return required2.every((c) => bucket.categories.has(c));
55386
55440
  }
55387
55441
  function parseAction(raw, fallback) {
55388
55442
  const v = raw?.trim().toLowerCase();
@@ -55464,12 +55518,13 @@ function resolveToolPermission(toolName, required2, policy) {
55464
55518
  categories: hit
55465
55519
  };
55466
55520
  }
55467
- var sessionToolGrants, sessionCategoryGrants, PERMISSION_PRESETS, ACTION_RANK2;
55521
+ var GLOBAL_BUCKET, grantsBySession, PERMISSION_PRESETS, ACTION_RANK2;
55468
55522
  var init_toolPermissions = __esm({
55469
55523
  "src/cli/safety/toolPermissions.ts"() {
55470
55524
  "use strict";
55471
- sessionToolGrants = /* @__PURE__ */ new Set();
55472
- sessionCategoryGrants = /* @__PURE__ */ new Set();
55525
+ init_sessionControl();
55526
+ GLOBAL_BUCKET = "";
55527
+ grantsBySession = /* @__PURE__ */ new Map();
55473
55528
  PERMISSION_PRESETS = {
55474
55529
  standard: { read: "allow", write: "allow", execute: "ask", network: "ask" },
55475
55530
  strict: { read: "allow", write: "ask", execute: "ask", network: "deny" },
@@ -69697,42 +69752,6 @@ var init_harnessStateEmit = __esm({
69697
69752
  }
69698
69753
  });
69699
69754
 
69700
- // src/cli/serve/sessionControl.ts
69701
- import { AsyncLocalStorage } from "node:async_hooks";
69702
- function runWithSession(sessionId2, fn) {
69703
- return dispatchContext.run({ sessionId: sessionId2, token: {} }, fn);
69704
- }
69705
- function registerLiveTurnControl(control) {
69706
- const store6 = dispatchContext.getStore();
69707
- if (!store6) return void 0;
69708
- const registered = { ...control, token: store6.token };
69709
- liveTurns.set(store6.sessionId, registered);
69710
- return () => {
69711
- if (liveTurns.get(store6.sessionId) === registered) {
69712
- liveTurns.delete(store6.sessionId);
69713
- }
69714
- };
69715
- }
69716
- function getLiveTurnControl(sessionId2) {
69717
- return liveTurns.get(sessionId2);
69718
- }
69719
- function clearSessionTurnControl(sessionId2) {
69720
- const store6 = dispatchContext.getStore();
69721
- if (!store6 || store6.sessionId !== sessionId2) return;
69722
- const registered = liveTurns.get(sessionId2);
69723
- if (registered && registered.token === store6.token) {
69724
- liveTurns.delete(sessionId2);
69725
- }
69726
- }
69727
- var dispatchContext, liveTurns;
69728
- var init_sessionControl = __esm({
69729
- "src/cli/serve/sessionControl.ts"() {
69730
- "use strict";
69731
- dispatchContext = new AsyncLocalStorage();
69732
- liveTurns = /* @__PURE__ */ new Map();
69733
- }
69734
- });
69735
-
69736
69755
  // src/cli/headless/controlReader.ts
69737
69756
  function parseControlEvent(raw) {
69738
69757
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
@@ -74705,6 +74724,7 @@ function createServePermissionBridge(write, timeoutMs2 = 12e4) {
74705
74724
  type: "permission.settled",
74706
74725
  requestId,
74707
74726
  decision,
74727
+ ...entry.sessionId ? { sessionId: entry.sessionId } : {},
74708
74728
  ...timedOut ? { timedOut: true } : {}
74709
74729
  })
74710
74730
  );
@@ -74719,11 +74739,13 @@ function createServePermissionBridge(write, timeoutMs2 = 12e4) {
74719
74739
  const timer = setTimeout(() => {
74720
74740
  settle(requestId, "deny", true);
74721
74741
  }, timeoutMs2);
74722
- pending.set(requestId, { resolve: resolve10, timer, payload });
74742
+ const sessionId2 = getCurrentHarnessSessionId();
74743
+ pending.set(requestId, { resolve: resolve10, timer, payload, sessionId: sessionId2 });
74723
74744
  write(
74724
74745
  JSON.stringify({
74725
74746
  type: "permission.request",
74726
74747
  requestId,
74748
+ ...sessionId2 ? { sessionId: sessionId2 } : {},
74727
74749
  tool: payload.tool,
74728
74750
  category: payload.category,
74729
74751
  categories,
@@ -74733,12 +74755,19 @@ function createServePermissionBridge(write, timeoutMs2 = 12e4) {
74733
74755
  );
74734
74756
  });
74735
74757
  },
74736
- respond: (requestId, decision) => settle(requestId, decision, false),
74758
+ respond: (requestId, decision, scopeSessionId) => {
74759
+ const entry = pending.get(requestId);
74760
+ if (entry && scopeSessionId && entry.sessionId !== scopeSessionId) {
74761
+ return false;
74762
+ }
74763
+ return settle(requestId, decision, false);
74764
+ },
74765
+ sessionOf: (requestId) => pending.get(requestId)?.sessionId,
74737
74766
  releaseGranted() {
74738
74767
  let n = 0;
74739
74768
  for (const [id3, entry] of [...pending]) {
74740
74769
  const cats = entry.payload.categories && entry.payload.categories.length > 0 ? entry.payload.categories : entry.payload.category ? entry.payload.category.split(",").map((c) => c.trim()).filter(Boolean) : [];
74741
- if (isSessionGranted(entry.payload.tool, cats)) {
74770
+ if (isSessionGranted(entry.payload.tool, cats, entry.sessionId)) {
74742
74771
  if (settle(id3, "allow", false)) n += 1;
74743
74772
  }
74744
74773
  }
@@ -74751,7 +74780,7 @@ function servePermissionRespond(bridge, params) {
74751
74780
  if (!params || typeof params !== "object") {
74752
74781
  return { accepted: false, reason: "permission.respond requires an object params" };
74753
74782
  }
74754
- const { requestId, decision } = params;
74783
+ const { requestId, decision, sessionId: sessionId2 } = params;
74755
74784
  if (typeof requestId !== "string" || requestId.length === 0) {
74756
74785
  return { accepted: false, reason: "permission.respond requires a non-empty string requestId" };
74757
74786
  }
@@ -74761,7 +74790,11 @@ function servePermissionRespond(bridge, params) {
74761
74790
  reason: "permission.respond decision must be 'allow' | 'deny' | 'always-tool' | 'always-category'"
74762
74791
  };
74763
74792
  }
74764
- return { accepted: bridge.respond(requestId, decision) };
74793
+ const scope = typeof sessionId2 === "string" && sessionId2.length > 0 ? sessionId2 : void 0;
74794
+ if (scope && bridge.sessionOf(requestId) !== scope) {
74795
+ return { accepted: false, reason: "session_mismatch: requestId belongs to another session" };
74796
+ }
74797
+ return { accepted: bridge.respond(requestId, decision, scope) };
74765
74798
  }
74766
74799
  function asRegistryAskHandler(bridge) {
74767
74800
  return async (req) => {
@@ -74774,14 +74807,15 @@ function asRegistryAskHandler(bridge) {
74774
74807
  ...req.claims && req.claims.length > 0 ? { inputPreview: req.claims.map((c) => c.summary).join(" \xB7 ") } : {}
74775
74808
  });
74776
74809
  if (decision === "deny") return false;
74810
+ const sessionId2 = getCurrentHarnessSessionId();
74777
74811
  if (decision === "always-tool") {
74778
- grantSessionTool(req.toolName);
74812
+ grantSessionTool(req.toolName, sessionId2);
74779
74813
  bridge.releaseGranted();
74780
74814
  } else if (decision === "always-category") {
74781
74815
  for (const cat of req.categories) {
74782
- grantSessionCategory(cat);
74816
+ grantSessionCategory(cat, sessionId2);
74783
74817
  }
74784
- grantSessionTool(req.toolName);
74818
+ grantSessionTool(req.toolName, sessionId2);
74785
74819
  bridge.releaseGranted();
74786
74820
  }
74787
74821
  return true;
@@ -74792,6 +74826,7 @@ var init_permissionBridge = __esm({
74792
74826
  "src/cli/serve/permissionBridge.ts"() {
74793
74827
  "use strict";
74794
74828
  init_toolPermissions();
74829
+ init_sessionControl();
74795
74830
  SERVE_PERMISSION_PRESETS = ["standard", "strict", "yolo"];
74796
74831
  PRESET_ENV = "ZELARI_PERMISSION_PRESET";
74797
74832
  PERMISSION_DECISIONS = [
@@ -74817,6 +74852,7 @@ function createServeAskUserBridge(write, timeoutMs2 = askUserTimeoutMs()) {
74817
74852
  type: "ask_user.settled",
74818
74853
  requestId,
74819
74854
  answer,
74855
+ ...entry.sessionId ? { sessionId: entry.sessionId } : {},
74820
74856
  ...timedOut ? { timedOut: true } : {}
74821
74857
  })
74822
74858
  );
@@ -74833,11 +74869,13 @@ function createServeAskUserBridge(write, timeoutMs2 = askUserTimeoutMs()) {
74833
74869
  const timer = setTimeout(() => {
74834
74870
  settle(requestId, null, true);
74835
74871
  }, timeoutMs2);
74836
- pending.set(requestId, { resolve: resolve10, timer });
74872
+ const sessionId2 = getCurrentHarnessSessionId();
74873
+ pending.set(requestId, { resolve: resolve10, timer, sessionId: sessionId2 });
74837
74874
  write(
74838
74875
  JSON.stringify({
74839
74876
  type: "ask_user.request",
74840
74877
  requestId,
74878
+ ...sessionId2 ? { sessionId: sessionId2 } : {},
74841
74879
  question,
74842
74880
  choices,
74843
74881
  ...req.context ? { context: req.context } : {}
@@ -74845,7 +74883,14 @@ function createServeAskUserBridge(write, timeoutMs2 = askUserTimeoutMs()) {
74845
74883
  );
74846
74884
  });
74847
74885
  },
74848
- respond: (requestId, answer) => settle(requestId, answer, false),
74886
+ respond: (requestId, answer, scopeSessionId) => {
74887
+ const entry = pending.get(requestId);
74888
+ if (entry && scopeSessionId && entry.sessionId && entry.sessionId !== scopeSessionId) {
74889
+ return false;
74890
+ }
74891
+ return settle(requestId, answer, false);
74892
+ },
74893
+ sessionOf: (requestId) => pending.get(requestId)?.sessionId,
74849
74894
  pendingCount: () => pending.size
74850
74895
  };
74851
74896
  }
@@ -74853,20 +74898,25 @@ function serveAskUserRespond(bridge, params) {
74853
74898
  if (!params || typeof params !== "object") {
74854
74899
  return { accepted: false, reason: "ask_user.respond requires an object params" };
74855
74900
  }
74856
- const { requestId, answer } = params;
74901
+ const { requestId, answer, sessionId: sessionId2 } = params;
74857
74902
  if (typeof requestId !== "string" || requestId.length === 0) {
74858
74903
  return { accepted: false, reason: "ask_user.respond requires a non-empty string requestId" };
74859
74904
  }
74860
74905
  if (answer !== null && typeof answer !== "string") {
74861
74906
  return { accepted: false, reason: "ask_user.respond answer must be a string or null" };
74862
74907
  }
74908
+ const scope = typeof sessionId2 === "string" && sessionId2.length > 0 ? sessionId2 : void 0;
74909
+ if (scope && bridge.sessionOf(requestId) && bridge.sessionOf(requestId) !== scope) {
74910
+ return { accepted: false, reason: "session_mismatch: requestId belongs to another session" };
74911
+ }
74863
74912
  const text = typeof answer === "string" ? answer.trim() : null;
74864
- return { accepted: bridge.respond(requestId, text && text.length > 0 ? text : null) };
74913
+ return { accepted: bridge.respond(requestId, text && text.length > 0 ? text : null, scope) };
74865
74914
  }
74866
74915
  var init_askUserBridge = __esm({
74867
74916
  "src/cli/serve/askUserBridge.ts"() {
74868
74917
  "use strict";
74869
74918
  init_askUserTimeout();
74919
+ init_sessionControl();
74870
74920
  }
74871
74921
  });
74872
74922
 
@@ -75030,17 +75080,23 @@ function createCliRunTurn(onPermissionAsk, onAskUser) {
75030
75080
  const opts = bindHarnessTurnOptions(input, deps.session.workspaceRoot);
75031
75081
  if (onPermissionAsk) opts.onPermissionAsk = onPermissionAsk;
75032
75082
  if (onAskUser) opts.onAskUser = onAskUser;
75083
+ const priorPreset = process.env[PRESET_ENV2];
75033
75084
  applyTurnPermissionPreset(input);
75034
- const lspProvider = resolveTurnLspProvider(deps.services);
75035
- const exitCode = await dispatchHeadlessTurn(
75036
- opts,
75037
- provider,
75038
- model,
75039
- stream,
75040
- void 0,
75041
- lspProvider ? { lspProvider } : void 0
75042
- );
75043
- return { exitCode };
75085
+ try {
75086
+ const lspProvider = resolveTurnLspProvider(deps.services);
75087
+ const exitCode = await dispatchHeadlessTurn(
75088
+ opts,
75089
+ provider,
75090
+ model,
75091
+ stream,
75092
+ void 0,
75093
+ lspProvider ? { lspProvider } : void 0
75094
+ );
75095
+ return { exitCode };
75096
+ } finally {
75097
+ if (priorPreset === void 0) delete process.env[PRESET_ENV2];
75098
+ else process.env[PRESET_ENV2] = priorPreset;
75099
+ }
75044
75100
  };
75045
75101
  }
75046
75102
  function unknownSessionEnvelope(id3, rawSessionId) {
@@ -75121,6 +75177,7 @@ function startHarnessServer(options = {}) {
75121
75177
  return { id: req.id ?? null, ok: false, error: { code: "unknown_session", message: `no session '${String(params.sessionId ?? "")}'` } };
75122
75178
  }
75123
75179
  await session.dispose();
75180
+ clearSessionPermissionGrants(session.id);
75124
75181
  return { id: req.id ?? null, ok: true, result: { disposed: true } };
75125
75182
  }
75126
75183
  // t32: session-scoped steer/cancel over the SAME protocol v2 — no new
@@ -75236,6 +75293,7 @@ async function runHarnessServer() {
75236
75293
  process.once("SIGTERM", finish2);
75237
75294
  });
75238
75295
  }
75296
+ var PRESET_ENV2;
75239
75297
  var init_harnessServer = __esm({
75240
75298
  "src/cli/serve/harnessServer.ts"() {
75241
75299
  "use strict";
@@ -75251,8 +75309,10 @@ var init_harnessServer = __esm({
75251
75309
  init_policyGate();
75252
75310
  init_policyLoadMode();
75253
75311
  init_permissionBridge();
75312
+ init_toolPermissions();
75254
75313
  init_askUserBridge();
75255
75314
  init_spineLockSweep();
75315
+ PRESET_ENV2 = "ZELARI_PERMISSION_PRESET";
75256
75316
  }
75257
75317
  });
75258
75318