@hasna/assistants 0.6.38 → 0.6.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12540,6 +12540,11 @@ var init_codes = __esm(() => {
12540
12540
  VALIDATION_OUT_OF_RANGE: "VALIDATION_OUT_OF_RANGE",
12541
12541
  VALIDATION_SCHEMA_ERROR: "VALIDATION_SCHEMA_ERROR",
12542
12542
  HOOK_EXECUTION_FAILED: "HOOK_EXECUTION_FAILED",
12543
+ JOB_NOT_FOUND: "JOB_NOT_FOUND",
12544
+ JOB_ALREADY_RUNNING: "JOB_ALREADY_RUNNING",
12545
+ JOB_TIMEOUT: "JOB_TIMEOUT",
12546
+ JOB_CANCELLED: "JOB_CANCELLED",
12547
+ JOB_EXECUTION_FAILED: "JOB_EXECUTION_FAILED",
12543
12548
  UNKNOWN_ERROR: "UNKNOWN_ERROR"
12544
12549
  };
12545
12550
  });
@@ -47894,8 +47899,8 @@ var require_core3 = __commonJS((exports) => {
47894
47899
  function many1(p) {
47895
47900
  return ab(p, many(p), (head, tail) => [head, ...tail]);
47896
47901
  }
47897
- function ab(pa, pb, join13) {
47898
- return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join13(ma.value, vb, data, i, j)));
47902
+ function ab(pa, pb, join15) {
47903
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join15(ma.value, vb, data, i, j)));
47899
47904
  }
47900
47905
  function left(pa, pb) {
47901
47906
  return ab(pa, pb, (va) => va);
@@ -47903,8 +47908,8 @@ var require_core3 = __commonJS((exports) => {
47903
47908
  function right(pa, pb) {
47904
47909
  return ab(pa, pb, (va, vb) => vb);
47905
47910
  }
47906
- function abc(pa, pb, pc, join13) {
47907
- return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join13(ma.value, mb.value, vc, data, i, j))));
47911
+ function abc(pa, pb, pc, join15) {
47912
+ return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join15(ma.value, mb.value, vc, data, i, j))));
47908
47913
  }
47909
47914
  function middle(pa, pb, pc) {
47910
47915
  return abc(pa, pb, pc, (ra, rb) => rb);
@@ -60830,11 +60835,11 @@ __export(exports_anthropic, {
60830
60835
  });
60831
60836
  import { readFileSync as readFileSync4, existsSync as existsSync9 } from "fs";
60832
60837
  import { homedir as homedir11 } from "os";
60833
- import { join as join14 } from "path";
60838
+ import { join as join16 } from "path";
60834
60839
  function loadApiKeyFromSecrets() {
60835
60840
  const envHome = process.env.HOME || process.env.USERPROFILE;
60836
60841
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir11();
60837
- const secretsPath = join14(homeDir, ".secrets");
60842
+ const secretsPath = join16(homeDir, ".secrets");
60838
60843
  if (existsSync9(secretsPath)) {
60839
60844
  try {
60840
60845
  const content = readFileSync4(secretsPath, "utf-8");
@@ -61182,7 +61187,7 @@ var require_dist_cjs = __commonJS((exports) => {
61182
61187
 
61183
61188
  // node_modules/.pnpm/@smithy+protocol-http@5.3.8/node_modules/@smithy/protocol-http/dist-cjs/index.js
61184
61189
  var require_dist_cjs2 = __commonJS((exports) => {
61185
- var types4 = require_dist_cjs();
61190
+ var types5 = require_dist_cjs();
61186
61191
  var getHttpHandlerExtensionConfiguration = (runtimeConfig) => {
61187
61192
  return {
61188
61193
  setHttpHandler(handler) {
@@ -61209,7 +61214,7 @@ var require_dist_cjs2 = __commonJS((exports) => {
61209
61214
  name;
61210
61215
  kind;
61211
61216
  values;
61212
- constructor({ name, kind = types4.FieldPosition.HEADER, values = [] }) {
61217
+ constructor({ name, kind = types5.FieldPosition.HEADER, values = [] }) {
61213
61218
  this.name = name;
61214
61219
  this.kind = kind;
61215
61220
  this.values = values;
@@ -61341,8 +61346,8 @@ var require_dist_cjs2 = __commonJS((exports) => {
61341
61346
 
61342
61347
  // node_modules/.pnpm/@smithy+util-middleware@4.2.8/node_modules/@smithy/util-middleware/dist-cjs/index.js
61343
61348
  var require_dist_cjs3 = __commonJS((exports) => {
61344
- var types4 = require_dist_cjs();
61345
- var getSmithyContext = (context) => context[types4.SMITHY_CONTEXT_KEY] || (context[types4.SMITHY_CONTEXT_KEY] = {});
61349
+ var types5 = require_dist_cjs();
61350
+ var getSmithyContext = (context) => context[types5.SMITHY_CONTEXT_KEY] || (context[types5.SMITHY_CONTEXT_KEY] = {});
61346
61351
  var normalizeProvider = (input) => {
61347
61352
  if (typeof input === "function")
61348
61353
  return input;
@@ -66278,12 +66283,12 @@ var require_protocols = __commonJS((exports) => {
66278
66283
 
66279
66284
  // node_modules/.pnpm/@smithy+core@3.22.0/node_modules/@smithy/core/dist-cjs/index.js
66280
66285
  var require_dist_cjs16 = __commonJS((exports) => {
66281
- var types4 = require_dist_cjs();
66286
+ var types5 = require_dist_cjs();
66282
66287
  var utilMiddleware = require_dist_cjs3();
66283
66288
  var middlewareSerde = require_dist_cjs4();
66284
66289
  var protocolHttp = require_dist_cjs2();
66285
66290
  var protocols = require_protocols();
66286
- var getSmithyContext = (context) => context[types4.SMITHY_CONTEXT_KEY] || (context[types4.SMITHY_CONTEXT_KEY] = {});
66291
+ var getSmithyContext = (context) => context[types5.SMITHY_CONTEXT_KEY] || (context[types5.SMITHY_CONTEXT_KEY] = {});
66287
66292
  var resolveAuthOptions = (candidateAuthOptions, authSchemePreference) => {
66288
66293
  if (!authSchemePreference || authSchemePreference.length === 0) {
66289
66294
  return candidateAuthOptions;
@@ -66498,9 +66503,9 @@ var require_dist_cjs16 = __commonJS((exports) => {
66498
66503
  throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
66499
66504
  }
66500
66505
  const clonedRequest = protocolHttp.HttpRequest.clone(httpRequest);
66501
- if (signingProperties.in === types4.HttpApiKeyAuthLocation.QUERY) {
66506
+ if (signingProperties.in === types5.HttpApiKeyAuthLocation.QUERY) {
66502
66507
  clonedRequest.query[signingProperties.name] = identity.apiKey;
66503
- } else if (signingProperties.in === types4.HttpApiKeyAuthLocation.HEADER) {
66508
+ } else if (signingProperties.in === types5.HttpApiKeyAuthLocation.HEADER) {
66504
66509
  clonedRequest.headers[signingProperties.name] = signingProperties.scheme ? `${signingProperties.scheme} ${identity.apiKey}` : identity.apiKey;
66505
66510
  } else {
66506
66511
  throw new Error("request can only be signed with `apiKey` locations `query` or `header`, " + "but found: `" + signingProperties.in + "`");
@@ -68615,7 +68620,7 @@ var require_dist_cjs20 = __commonJS((exports) => {
68615
68620
  var require_dist_cjs21 = __commonJS((exports) => {
68616
68621
  var middlewareStack = require_dist_cjs20();
68617
68622
  var protocols = require_protocols();
68618
- var types4 = require_dist_cjs();
68623
+ var types5 = require_dist_cjs();
68619
68624
  var schema = require_schema();
68620
68625
  var serde = require_serde();
68621
68626
 
@@ -68714,7 +68719,7 @@ var require_dist_cjs21 = __commonJS((exports) => {
68714
68719
  commandName,
68715
68720
  inputFilterSensitiveLog,
68716
68721
  outputFilterSensitiveLog,
68717
- [types4.SMITHY_CONTEXT_KEY]: {
68722
+ [types5.SMITHY_CONTEXT_KEY]: {
68718
68723
  commandInstance: this,
68719
68724
  ...smithyContext
68720
68725
  },
@@ -68965,8 +68970,8 @@ var require_dist_cjs21 = __commonJS((exports) => {
68965
68970
  };
68966
68971
  var getChecksumConfiguration = (runtimeConfig) => {
68967
68972
  const checksumAlgorithms = [];
68968
- for (const id in types4.AlgorithmId) {
68969
- const algorithmId = types4.AlgorithmId[id];
68973
+ for (const id in types5.AlgorithmId) {
68974
+ const algorithmId = types5.AlgorithmId[id];
68970
68975
  if (runtimeConfig[algorithmId] === undefined) {
68971
68976
  continue;
68972
68977
  }
@@ -72690,7 +72695,7 @@ var require_dist_cjs27 = __commonJS((exports) => {
72690
72695
 
72691
72696
  // node_modules/.pnpm/@smithy+util-endpoints@3.2.8/node_modules/@smithy/util-endpoints/dist-cjs/index.js
72692
72697
  var require_dist_cjs28 = __commonJS((exports) => {
72693
- var types4 = require_dist_cjs();
72698
+ var types5 = require_dist_cjs();
72694
72699
 
72695
72700
  class EndpointCache {
72696
72701
  capacity;
@@ -72813,8 +72818,8 @@ var require_dist_cjs28 = __commonJS((exports) => {
72813
72818
  var isSet = (value) => value != null;
72814
72819
  var not = (value) => !value;
72815
72820
  var DEFAULT_PORTS = {
72816
- [types4.EndpointURLScheme.HTTP]: 80,
72817
- [types4.EndpointURLScheme.HTTPS]: 443
72821
+ [types5.EndpointURLScheme.HTTP]: 80,
72822
+ [types5.EndpointURLScheme.HTTPS]: 443
72818
72823
  };
72819
72824
  var parseURL = (value) => {
72820
72825
  const whatwgURL = (() => {
@@ -72843,7 +72848,7 @@ var require_dist_cjs28 = __commonJS((exports) => {
72843
72848
  return null;
72844
72849
  }
72845
72850
  const scheme = protocol.slice(0, -1);
72846
- if (!Object.values(types4.EndpointURLScheme).includes(scheme)) {
72851
+ if (!Object.values(types5.EndpointURLScheme).includes(scheme)) {
72847
72852
  return null;
72848
72853
  }
72849
72854
  const isIp = isIpAddress(hostname);
@@ -74024,7 +74029,7 @@ var require_readFile = __commonJS((exports) => {
74024
74029
  var promises_1 = __require("fs/promises");
74025
74030
  exports.filePromises = {};
74026
74031
  exports.fileIntercept = {};
74027
- var readFile9 = (path2, options) => {
74032
+ var readFile10 = (path2, options) => {
74028
74033
  if (exports.fileIntercept[path2] !== undefined) {
74029
74034
  return exports.fileIntercept[path2];
74030
74035
  }
@@ -74033,7 +74038,7 @@ var require_readFile = __commonJS((exports) => {
74033
74038
  }
74034
74039
  return exports.filePromises[path2];
74035
74040
  };
74036
- exports.readFile = readFile9;
74041
+ exports.readFile = readFile10;
74037
74042
  });
74038
74043
 
74039
74044
  // node_modules/.pnpm/@smithy+shared-ini-file-loader@4.4.3/node_modules/@smithy/shared-ini-file-loader/dist-cjs/index.js
@@ -74042,8 +74047,8 @@ var require_dist_cjs35 = __commonJS((exports) => {
74042
74047
  var getSSOTokenFilepath = require_getSSOTokenFilepath();
74043
74048
  var getSSOTokenFromFile = require_getSSOTokenFromFile();
74044
74049
  var path2 = __require("path");
74045
- var types4 = require_dist_cjs();
74046
- var readFile9 = require_readFile();
74050
+ var types5 = require_dist_cjs();
74051
+ var readFile10 = require_readFile();
74047
74052
  var ENV_PROFILE = "AWS_PROFILE";
74048
74053
  var DEFAULT_PROFILE2 = "default";
74049
74054
  var getProfileName = (init) => init.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE2;
@@ -74053,10 +74058,10 @@ var require_dist_cjs35 = __commonJS((exports) => {
74053
74058
  if (indexOfSeparator === -1) {
74054
74059
  return false;
74055
74060
  }
74056
- return Object.values(types4.IniSectionType).includes(key.substring(0, indexOfSeparator));
74061
+ return Object.values(types5.IniSectionType).includes(key.substring(0, indexOfSeparator));
74057
74062
  }).reduce((acc, [key, value]) => {
74058
74063
  const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
74059
- const updatedKey = key.substring(0, indexOfSeparator) === types4.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
74064
+ const updatedKey = key.substring(0, indexOfSeparator) === types5.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
74060
74065
  acc[updatedKey] = value;
74061
74066
  return acc;
74062
74067
  }, {
@@ -74082,7 +74087,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
74082
74087
  const matches = prefixKeyRegex.exec(sectionName);
74083
74088
  if (matches) {
74084
74089
  const [, prefix, , name] = matches;
74085
- if (Object.values(types4.IniSectionType).includes(prefix)) {
74090
+ if (Object.values(types5.IniSectionType).includes(prefix)) {
74086
74091
  currentSection = [prefix, name].join(CONFIG_PREFIX_SEPARATOR);
74087
74092
  }
74088
74093
  } else {
@@ -74127,10 +74132,10 @@ var require_dist_cjs35 = __commonJS((exports) => {
74127
74132
  resolvedConfigFilepath = path2.join(homeDir, configFilepath.slice(2));
74128
74133
  }
74129
74134
  const parsedFiles = await Promise.all([
74130
- readFile9.readFile(resolvedConfigFilepath, {
74135
+ readFile10.readFile(resolvedConfigFilepath, {
74131
74136
  ignoreCache: init.ignoreCache
74132
74137
  }).then(parseIni).then(getConfigData).catch(swallowError$1),
74133
- readFile9.readFile(resolvedFilepath, {
74138
+ readFile10.readFile(resolvedFilepath, {
74134
74139
  ignoreCache: init.ignoreCache
74135
74140
  }).then(parseIni).catch(swallowError$1)
74136
74141
  ]);
@@ -74139,9 +74144,9 @@ var require_dist_cjs35 = __commonJS((exports) => {
74139
74144
  credentialsFile: parsedFiles[1]
74140
74145
  };
74141
74146
  };
74142
- var getSsoSessionData = (data) => Object.entries(data).filter(([key]) => key.startsWith(types4.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {});
74147
+ var getSsoSessionData = (data) => Object.entries(data).filter(([key]) => key.startsWith(types5.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {});
74143
74148
  var swallowError = () => ({});
74144
- var loadSsoSessionData = async (init = {}) => readFile9.readFile(init.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError);
74149
+ var loadSsoSessionData = async (init = {}) => readFile10.readFile(init.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError);
74145
74150
  var mergeConfigFiles = (...files) => {
74146
74151
  const merged = {};
74147
74152
  for (const file of files) {
@@ -74161,10 +74166,10 @@ var require_dist_cjs35 = __commonJS((exports) => {
74161
74166
  };
74162
74167
  var externalDataInterceptor = {
74163
74168
  getFileRecord() {
74164
- return readFile9.fileIntercept;
74169
+ return readFile10.fileIntercept;
74165
74170
  },
74166
74171
  interceptFile(path3, contents) {
74167
- readFile9.fileIntercept[path3] = Promise.resolve(contents);
74172
+ readFile10.fileIntercept[path3] = Promise.resolve(contents);
74168
74173
  },
74169
74174
  getTokenRecord() {
74170
74175
  return getSSOTokenFromFile.tokenIntercept;
@@ -74182,7 +74187,7 @@ var require_dist_cjs35 = __commonJS((exports) => {
74182
74187
  Object.defineProperty(exports, "readFile", {
74183
74188
  enumerable: true,
74184
74189
  get: function() {
74185
- return readFile9.readFile;
74190
+ return readFile10.readFile;
74186
74191
  }
74187
74192
  });
74188
74193
  exports.CONFIG_PREFIX_SEPARATOR = CONFIG_PREFIX_SEPARATOR;
@@ -78875,14 +78880,14 @@ var init_validateTokenKey = __esm(() => {
78875
78880
 
78876
78881
  // node_modules/.pnpm/@aws-sdk+token-providers@3.980.0/node_modules/@aws-sdk/token-providers/dist-es/writeSSOTokenToFile.js
78877
78882
  import { promises as fsPromises } from "fs";
78878
- var import_shared_ini_file_loader, writeFile8, writeSSOTokenToFile = (id, ssoToken) => {
78883
+ var import_shared_ini_file_loader, writeFile10, writeSSOTokenToFile = (id, ssoToken) => {
78879
78884
  const tokenFilepath = import_shared_ini_file_loader.getSSOTokenFilepath(id);
78880
78885
  const tokenString = JSON.stringify(ssoToken, null, 2);
78881
- return writeFile8(tokenFilepath, tokenString);
78886
+ return writeFile10(tokenFilepath, tokenString);
78882
78887
  };
78883
78888
  var init_writeSSOTokenToFile = __esm(() => {
78884
78889
  import_shared_ini_file_loader = __toESM(require_dist_cjs35(), 1);
78885
- ({ writeFile: writeFile8 } = fsPromises);
78890
+ ({ writeFile: writeFile10 } = fsPromises);
78886
78891
  });
78887
78892
 
78888
78893
  // node_modules/.pnpm/@aws-sdk+token-providers@3.980.0/node_modules/@aws-sdk/token-providers/dist-es/fromSso.js
@@ -81519,7 +81524,7 @@ var require_signin = __commonJS((exports) => {
81519
81524
  import { createHash, createPrivateKey, createPublicKey, sign } from "crypto";
81520
81525
  import { promises as fs3 } from "fs";
81521
81526
  import { homedir as homedir13 } from "os";
81522
- import { dirname as dirname9, join as join21 } from "path";
81527
+ import { dirname as dirname9, join as join23 } from "path";
81523
81528
  var import_property_provider18, import_protocol_http10, import_shared_ini_file_loader6, LoginCredentialsFetcher;
81524
81529
  var init_LoginCredentialsFetcher = __esm(() => {
81525
81530
  import_property_provider18 = __toESM(require_dist_cjs17(), 1);
@@ -81679,10 +81684,10 @@ var init_LoginCredentialsFetcher = __esm(() => {
81679
81684
  await fs3.writeFile(tokenFilePath, JSON.stringify(token, null, 2), "utf8");
81680
81685
  }
81681
81686
  getTokenFilePath() {
81682
- const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ?? join21(homedir13(), ".aws", "login", "cache");
81687
+ const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ?? join23(homedir13(), ".aws", "login", "cache");
81683
81688
  const loginSessionBytes = Buffer.from(this.loginSession, "utf8");
81684
81689
  const loginSessionSha256 = createHash("sha256").update(loginSessionBytes).digest("hex");
81685
- return join21(directory, `${loginSessionSha256}.json`);
81690
+ return join23(directory, `${loginSessionSha256}.json`);
81686
81691
  }
81687
81692
  derToRawSignature(derSignature) {
81688
81693
  let offset = 2;
@@ -82721,8 +82726,8 @@ function many(p4) {
82721
82726
  function many1(p4) {
82722
82727
  return ab2(p4, many(p4), (head, tail) => [head, ...tail]);
82723
82728
  }
82724
- function ab2(pa, pb, join23) {
82725
- return (data, i4) => mapOuter(pa(data, i4), (ma) => mapInner(pb(data, ma.position), (vb, j4) => join23(ma.value, vb, data, i4, j4)));
82729
+ function ab2(pa, pb, join25) {
82730
+ return (data, i4) => mapOuter(pa(data, i4), (ma) => mapInner(pb(data, ma.position), (vb, j4) => join25(ma.value, vb, data, i4, j4)));
82726
82731
  }
82727
82732
  function left(pa, pb) {
82728
82733
  return ab2(pa, pb, (va) => va);
@@ -82730,8 +82735,8 @@ function left(pa, pb) {
82730
82735
  function right(pa, pb) {
82731
82736
  return ab2(pa, pb, (va, vb) => vb);
82732
82737
  }
82733
- function abc(pa, pb, pc, join23) {
82734
- return (data, i4) => mapOuter(pa(data, i4), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j4) => join23(ma.value, mb.value, vc, data, i4, j4))));
82738
+ function abc(pa, pb, pc, join25) {
82739
+ return (data, i4) => mapOuter(pa(data, i4), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j4) => join25(ma.value, mb.value, vc, data, i4, j4))));
82735
82740
  }
82736
82741
  function middle(pa, pb, pc) {
82737
82742
  return abc(pa, pb, pc, (ra, rb) => rb);
@@ -110932,7 +110937,7 @@ var ScrollView = import_react26.forwardRef(({
110932
110937
 
110933
110938
  // packages/core/src/agent/loop.ts
110934
110939
  init_src();
110935
- import { join as join24 } from "path";
110940
+ import { join as join26 } from "path";
110936
110941
 
110937
110942
  // packages/core/src/agent/context.ts
110938
110943
  init_src();
@@ -111638,6 +111643,7 @@ var DEFAULT_SYSTEM_PROMPT = `You are Hasna Assistant, a helpful AI assistant run
111638
111643
  - Be concise and direct in responses
111639
111644
  - Ask clarifying questions when requirements are ambiguous
111640
111645
  - Explain your reasoning when making architectural decisions
111646
+ - Use the ask_user tool to collect structured answers when you need details
111641
111647
  `;
111642
111648
  var DEFAULT_CONFIG = {
111643
111649
  llm: {
@@ -111717,6 +111723,12 @@ var DEFAULT_CONFIG = {
111717
111723
  security: {
111718
111724
  maxReadsPerHour: 10
111719
111725
  }
111726
+ },
111727
+ jobs: {
111728
+ enabled: true,
111729
+ defaultTimeoutMs: 60000,
111730
+ maxJobAgeMs: 24 * 60 * 60 * 1000,
111731
+ connectors: {}
111720
111732
  }
111721
111733
  };
111722
111734
  function mergeConfig(base2, override) {
@@ -111814,6 +111826,14 @@ function mergeConfig(base2, override) {
111814
111826
  ...base2.wallet?.security || {},
111815
111827
  ...override.wallet?.security || {}
111816
111828
  }
111829
+ },
111830
+ jobs: {
111831
+ ...base2.jobs || {},
111832
+ ...override.jobs || {},
111833
+ connectors: {
111834
+ ...base2.jobs?.connectors || {},
111835
+ ...override.jobs?.connectors || {}
111836
+ }
111817
111837
  }
111818
111838
  };
111819
111839
  }
@@ -111892,6 +111912,7 @@ async function ensureConfigDir(sessionId) {
111892
111912
  mkdir(join(configDir, "heartbeats"), { recursive: true }),
111893
111913
  mkdir(join(configDir, "state"), { recursive: true }),
111894
111914
  mkdir(join(configDir, "energy"), { recursive: true }),
111915
+ mkdir(join(configDir, "jobs"), { recursive: true }),
111895
111916
  mkdir(join(configDir, "inbox"), { recursive: true })
111896
111917
  ];
111897
111918
  if (sessionId) {
@@ -112186,12 +112207,16 @@ class ConnectorBridge {
112186
112207
  static cache = new Map;
112187
112208
  static diskCacheLoaded = false;
112188
112209
  cwd;
112210
+ jobManagerGetter = null;
112189
112211
  constructor(cwd2) {
112190
112212
  this.cwd = cwd2;
112191
112213
  if (!ConnectorBridge.diskCacheLoaded) {
112192
112214
  ConnectorBridge.loadDiskCache();
112193
112215
  }
112194
112216
  }
112217
+ setJobManagerGetter(getter) {
112218
+ this.jobManagerGetter = getter;
112219
+ }
112195
112220
  getHomeDir() {
112196
112221
  const envHome = process.env.HOME || process.env.USERPROFILE;
112197
112222
  return envHome && envHome.trim().length > 0 ? envHome : homedir2();
@@ -112528,9 +112553,21 @@ class ConnectorBridge {
112528
112553
  const options = input.options || {};
112529
112554
  const cwd2 = typeof input.cwd === "string" ? input.cwd : process.cwd();
112530
112555
  const timeoutMs = Number(options.timeoutMs || options.timeout || 15000);
112556
+ const jobManager = this.jobManagerGetter?.();
112557
+ if (jobManager && jobManager.shouldRunAsync(connector.name, input)) {
112558
+ const job = await jobManager.startJob(connector.name, command, input, connector.cli);
112559
+ return `Job started in background.
112560
+
112561
+ Job ID: ${job.id}
112562
+ Connector: ${connector.name}
112563
+ Command: ${command}
112564
+ Timeout: ${job.timeoutMs / 1000}s
112565
+
112566
+ Use job_status or job_result to check progress.`;
112567
+ }
112531
112568
  const cmdParts = [connector.cli, ...command.split(" "), ...args];
112532
112569
  for (const [key, value] of Object.entries(options)) {
112533
- if (key === "timeoutMs" || key === "timeout")
112570
+ if (key === "timeoutMs" || key === "timeout" || key === "async")
112534
112571
  continue;
112535
112572
  if (value === true) {
112536
112573
  cmdParts.push(`--${key}`);
@@ -115079,6 +115116,315 @@ class ImageTools {
115079
115116
  }
115080
115117
  }
115081
115118
 
115119
+ // packages/core/src/tools/skills.ts
115120
+ init_errors();
115121
+
115122
+ // packages/core/src/skills/create.ts
115123
+ import { join as join8 } from "path";
115124
+ import { mkdir as mkdir3, stat, writeFile as writeFile2 } from "fs/promises";
115125
+ function slugify(input) {
115126
+ return input.trim().toLowerCase().replace(/[^a-z0-9\s-_]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
115127
+ }
115128
+ function normalizeName(rawName) {
115129
+ const trimmed = rawName.trim();
115130
+ if (!trimmed) {
115131
+ throw new Error("Skill name is required.");
115132
+ }
115133
+ const withoutPrefix = trimmed.startsWith("skill-") ? trimmed.slice("skill-".length) : trimmed;
115134
+ if (/skill/i.test(withoutPrefix)) {
115135
+ throw new Error('Skill name should not include the word "skill".');
115136
+ }
115137
+ const slug = slugify(withoutPrefix);
115138
+ if (!slug) {
115139
+ throw new Error("Skill name is invalid after normalization.");
115140
+ }
115141
+ return { skillName: slug, dirName: `skill-${slug}` };
115142
+ }
115143
+ function buildFrontmatter(options, skillName) {
115144
+ const lines = [];
115145
+ lines.push("---");
115146
+ lines.push(`name: ${skillName}`);
115147
+ if (options.description) {
115148
+ lines.push(`description: ${options.description}`);
115149
+ }
115150
+ if (options.allowedTools && options.allowedTools.length > 0) {
115151
+ lines.push("allowed-tools:");
115152
+ for (const tool of options.allowedTools) {
115153
+ lines.push(` - ${tool}`);
115154
+ }
115155
+ }
115156
+ if (options.argumentHint) {
115157
+ lines.push(`argument-hint: ${options.argumentHint}`);
115158
+ }
115159
+ if (options.userInvocable === false) {
115160
+ lines.push("user-invocable: false");
115161
+ }
115162
+ if (options.disableModelInvocation === true) {
115163
+ lines.push("disable-model-invocation: true");
115164
+ }
115165
+ lines.push("---");
115166
+ return lines.join(`
115167
+ `);
115168
+ }
115169
+ function buildDefaultContent() {
115170
+ return [
115171
+ "Describe what this skill does.",
115172
+ "",
115173
+ "Use $ARGUMENTS to accept user input."
115174
+ ].join(`
115175
+ `);
115176
+ }
115177
+ function resolveSkillRoot(scope, cwd2) {
115178
+ if (scope === "global") {
115179
+ return join8(getConfigDir(), "shared", "skills");
115180
+ }
115181
+ return join8(cwd2, ".assistants", "skills");
115182
+ }
115183
+ async function pathExists(path) {
115184
+ try {
115185
+ const stats = await stat(path);
115186
+ return stats.isFile() || stats.isDirectory();
115187
+ } catch {
115188
+ return false;
115189
+ }
115190
+ }
115191
+ async function createSkill(options) {
115192
+ const scope = options.scope ?? "project";
115193
+ const { skillName, dirName } = normalizeName(options.name);
115194
+ const root = resolveSkillRoot(scope, options.cwd);
115195
+ const directory = join8(root, dirName);
115196
+ const filePath = join8(directory, "SKILL.md");
115197
+ if (!options.overwrite && await pathExists(filePath)) {
115198
+ throw new Error(`Skill already exists at ${filePath}`);
115199
+ }
115200
+ await mkdir3(directory, { recursive: true });
115201
+ const frontmatter = buildFrontmatter(options, skillName);
115202
+ const content = options.content && options.content.trim().length > 0 ? options.content.trim() : buildDefaultContent();
115203
+ const body = `${frontmatter}
115204
+
115205
+ ${content}
115206
+ `;
115207
+ await writeFile2(filePath, body);
115208
+ return {
115209
+ name: skillName,
115210
+ directory,
115211
+ filePath,
115212
+ scope
115213
+ };
115214
+ }
115215
+
115216
+ // packages/core/src/tools/skills.ts
115217
+ function normalizeScope(input) {
115218
+ if (!input)
115219
+ return null;
115220
+ const value = String(input).trim().toLowerCase();
115221
+ if (value === "project" || value === "global")
115222
+ return value;
115223
+ return null;
115224
+ }
115225
+ function normalizeAllowedTools(input) {
115226
+ if (!input)
115227
+ return;
115228
+ if (Array.isArray(input)) {
115229
+ const tools = input.map((tool) => String(tool).trim()).filter(Boolean);
115230
+ return tools.length > 0 ? tools : undefined;
115231
+ }
115232
+ if (typeof input === "string") {
115233
+ const tools = input.split(",").map((tool) => tool.trim()).filter(Boolean);
115234
+ return tools.length > 0 ? tools : undefined;
115235
+ }
115236
+ return;
115237
+ }
115238
+
115239
+ class SkillTool {
115240
+ static tool = {
115241
+ name: "skill_create",
115242
+ description: "Create a skill (SKILL.md). Requires explicit scope (project or global).",
115243
+ parameters: {
115244
+ type: "object",
115245
+ properties: {
115246
+ name: {
115247
+ type: "string",
115248
+ description: 'Skill name without the "skill-" prefix.'
115249
+ },
115250
+ scope: {
115251
+ type: "string",
115252
+ description: "Where to create the skill.",
115253
+ enum: ["project", "global"]
115254
+ },
115255
+ description: {
115256
+ type: "string",
115257
+ description: "Short description for the skill."
115258
+ },
115259
+ content: {
115260
+ type: "string",
115261
+ description: "Skill body content (markdown)."
115262
+ },
115263
+ allowed_tools: {
115264
+ type: "array",
115265
+ description: "Allowed tools for the skill (array or comma-separated string).",
115266
+ items: { type: "string", description: "Tool name" }
115267
+ },
115268
+ argument_hint: {
115269
+ type: "string",
115270
+ description: "Argument hint for invocation."
115271
+ },
115272
+ overwrite: {
115273
+ type: "boolean",
115274
+ description: "Overwrite if skill already exists.",
115275
+ default: false
115276
+ },
115277
+ cwd: {
115278
+ type: "string",
115279
+ description: "Working directory for project scope (autofilled)."
115280
+ }
115281
+ },
115282
+ required: ["name"]
115283
+ }
115284
+ };
115285
+ static executor = async (input) => {
115286
+ const rawName = String(input.name || "").trim();
115287
+ if (!rawName) {
115288
+ throw new ToolExecutionError("Skill name is required.", {
115289
+ toolName: "skill_create",
115290
+ toolInput: input,
115291
+ code: ErrorCodes.TOOL_EXECUTION_FAILED,
115292
+ recoverable: true,
115293
+ retryable: false,
115294
+ suggestion: 'Provide a skill name without the "skill-" prefix.'
115295
+ });
115296
+ }
115297
+ const scope = normalizeScope(input.scope);
115298
+ if (!scope) {
115299
+ throw new ToolExecutionError("Scope is required (project or global).", {
115300
+ toolName: "skill_create",
115301
+ toolInput: input,
115302
+ code: ErrorCodes.TOOL_EXECUTION_FAILED,
115303
+ recoverable: true,
115304
+ retryable: false,
115305
+ suggestion: "Ask the user: project (default) or global?"
115306
+ });
115307
+ }
115308
+ const cwd2 = String(input.cwd || process.cwd());
115309
+ const allowedTools = normalizeAllowedTools(input.allowed_tools ?? input.allowedTools);
115310
+ const result = await createSkill({
115311
+ name: rawName,
115312
+ scope,
115313
+ description: input.description ? String(input.description) : undefined,
115314
+ content: input.content ? String(input.content) : undefined,
115315
+ allowedTools,
115316
+ argumentHint: input.argument_hint ? String(input.argument_hint) : undefined,
115317
+ overwrite: Boolean(input.overwrite),
115318
+ cwd: cwd2
115319
+ });
115320
+ return [
115321
+ `Created skill "${result.name}" (${result.scope}).`,
115322
+ `Location: ${result.filePath}`,
115323
+ `Invoke with: $${result.name} [args] or /${result.name} [args]`
115324
+ ].join(`
115325
+ `);
115326
+ };
115327
+ }
115328
+
115329
+ // packages/core/src/tools/ask-user.ts
115330
+ init_errors();
115331
+ function createAskUserTool(getHandler) {
115332
+ const tool = {
115333
+ name: "ask_user",
115334
+ description: "Ask the user clarifying questions and return structured answers.",
115335
+ parameters: {
115336
+ type: "object",
115337
+ properties: {
115338
+ title: {
115339
+ type: "string",
115340
+ description: "Short title shown above the questions."
115341
+ },
115342
+ description: {
115343
+ type: "string",
115344
+ description: "Optional context for the user."
115345
+ },
115346
+ questions: {
115347
+ type: "array",
115348
+ description: "Questions to ask the user.",
115349
+ items: {
115350
+ type: "object",
115351
+ properties: {
115352
+ id: { type: "string", description: "Stable id for this question." },
115353
+ question: { type: "string", description: "The question text." },
115354
+ options: { type: "array", items: { type: "string", description: "Option label" } },
115355
+ placeholder: { type: "string", description: "Placeholder text for input." },
115356
+ multiline: { type: "boolean", description: "Whether the answer can be multi-line." },
115357
+ required: { type: "boolean", description: "Whether the answer is required." }
115358
+ },
115359
+ required: ["id", "question"]
115360
+ }
115361
+ }
115362
+ },
115363
+ required: ["questions"]
115364
+ }
115365
+ };
115366
+ const executor = async (input) => {
115367
+ const handler = getHandler();
115368
+ if (!handler) {
115369
+ throw new ToolExecutionError("User input is not available in this environment.", {
115370
+ toolName: "ask_user",
115371
+ toolInput: input,
115372
+ code: ErrorCodes.TOOL_EXECUTION_FAILED,
115373
+ recoverable: true,
115374
+ retryable: false,
115375
+ suggestion: "Ask the user directly in chat."
115376
+ });
115377
+ }
115378
+ const questions = Array.isArray(input.questions) ? input.questions : [];
115379
+ if (questions.length === 0) {
115380
+ throw new ToolExecutionError("ask_user requires at least one question.", {
115381
+ toolName: "ask_user",
115382
+ toolInput: input,
115383
+ code: ErrorCodes.TOOL_EXECUTION_FAILED,
115384
+ recoverable: true,
115385
+ retryable: false,
115386
+ suggestion: "Provide an array of questions with id and question fields."
115387
+ });
115388
+ }
115389
+ if (questions.length > 6) {
115390
+ throw new ToolExecutionError("ask_user supports up to 6 questions at a time.", {
115391
+ toolName: "ask_user",
115392
+ toolInput: input,
115393
+ code: ErrorCodes.TOOL_EXECUTION_FAILED,
115394
+ recoverable: true,
115395
+ retryable: false,
115396
+ suggestion: "Split into multiple ask_user calls."
115397
+ });
115398
+ }
115399
+ const request = {
115400
+ title: input.title ? String(input.title) : undefined,
115401
+ description: input.description ? String(input.description) : undefined,
115402
+ questions: questions.map((entry) => ({
115403
+ id: String(entry.id || ""),
115404
+ question: String(entry.question || ""),
115405
+ options: Array.isArray(entry.options) ? entry.options.map((opt) => String(opt)) : undefined,
115406
+ placeholder: entry.placeholder ? String(entry.placeholder) : undefined,
115407
+ multiline: Boolean(entry.multiline),
115408
+ required: entry.required !== undefined ? Boolean(entry.required) : undefined
115409
+ }))
115410
+ };
115411
+ for (const q of request.questions) {
115412
+ if (!q.id || !q.question) {
115413
+ throw new ToolExecutionError("Each ask_user question must include id and question.", {
115414
+ toolName: "ask_user",
115415
+ toolInput: input,
115416
+ code: ErrorCodes.TOOL_EXECUTION_FAILED,
115417
+ recoverable: true,
115418
+ retryable: false
115419
+ });
115420
+ }
115421
+ }
115422
+ const response = await handler(request);
115423
+ return JSON.stringify(response.answers ?? {}, null, 2);
115424
+ };
115425
+ return { tool, executor };
115426
+ }
115427
+
115082
115428
  // packages/core/src/agent/subagent.ts
115083
115429
  init_src();
115084
115430
  var DEFAULT_HOOK_TOOLS = ["read", "glob", "grep"];
@@ -115115,7 +115461,7 @@ Respond with ALLOW or DENY on the first line, followed by a short reason.`,
115115
115461
 
115116
115462
  // packages/core/src/skills/loader.ts
115117
115463
  init_src();
115118
- import { join as join8, basename, dirname as dirname4 } from "path";
115464
+ import { join as join9, basename, dirname as dirname4 } from "path";
115119
115465
  import { homedir as homedir7 } from "os";
115120
115466
  var {Glob: Glob2 } = globalThis.Bun;
115121
115467
 
@@ -115124,20 +115470,20 @@ class SkillLoader {
115124
115470
  async loadAll(projectDir = process.cwd()) {
115125
115471
  const envHome = process.env.HOME || process.env.USERPROFILE;
115126
115472
  const userHome = envHome && envHome.trim().length > 0 ? envHome : homedir7();
115127
- const userSkillsDir = join8(userHome, ".assistants", "shared", "skills");
115473
+ const userSkillsDir = join9(userHome, ".assistants", "shared", "skills");
115128
115474
  await this.loadFromDirectory(userSkillsDir);
115129
- const projectSkillsDir = join8(projectDir, ".assistants", "skills");
115475
+ const projectSkillsDir = join9(projectDir, ".assistants", "skills");
115130
115476
  await this.loadFromDirectory(projectSkillsDir);
115131
115477
  const nestedGlob = new Glob2("**/.assistants/skills/*/SKILL.md");
115132
115478
  for await (const file of nestedGlob.scan({ cwd: projectDir, dot: true })) {
115133
- await this.loadSkillFile(join8(projectDir, file));
115479
+ await this.loadSkillFile(join9(projectDir, file));
115134
115480
  }
115135
115481
  }
115136
115482
  async loadFromDirectory(dir) {
115137
115483
  try {
115138
- const { stat } = await import("fs/promises");
115484
+ const { stat: stat2 } = await import("fs/promises");
115139
115485
  try {
115140
- const stats = await stat(dir);
115486
+ const stats = await stat2(dir);
115141
115487
  if (!stats.isDirectory())
115142
115488
  return;
115143
115489
  } catch {
@@ -115146,13 +115492,13 @@ class SkillLoader {
115146
115492
  const filesToLoad = [];
115147
115493
  const skillPrefixGlob = new Glob2("skill-*/SKILL.md");
115148
115494
  for await (const file of skillPrefixGlob.scan({ cwd: dir })) {
115149
- filesToLoad.push(join8(dir, file));
115495
+ filesToLoad.push(join9(dir, file));
115150
115496
  }
115151
115497
  const regularGlob = new Glob2("*/SKILL.md");
115152
115498
  for await (const file of regularGlob.scan({ cwd: dir })) {
115153
115499
  const dirName = file.split(/[\\/]/)[0];
115154
115500
  if (!dirName.startsWith("skill-")) {
115155
- filesToLoad.push(join8(dir, file));
115501
+ filesToLoad.push(join9(dir, file));
115156
115502
  }
115157
115503
  }
115158
115504
  const loadTasks = [];
@@ -115803,14 +116149,14 @@ init_src();
115803
116149
 
115804
116150
  // packages/core/src/sessions/verification.ts
115805
116151
  init_src();
115806
- import { join as join9 } from "path";
116152
+ import { join as join10 } from "path";
115807
116153
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2 } from "fs";
115808
116154
 
115809
116155
  class VerificationSessionStore {
115810
116156
  basePath;
115811
116157
  maxSessions;
115812
116158
  constructor(basePath, maxSessions = 100) {
115813
- this.basePath = join9(basePath, "verifications");
116159
+ this.basePath = join10(basePath, "verifications");
115814
116160
  this.maxSessions = maxSessions;
115815
116161
  this.ensureDirectory();
115816
116162
  }
@@ -115836,11 +116182,11 @@ class VerificationSessionStore {
115836
116182
  return session;
115837
116183
  }
115838
116184
  save(session) {
115839
- const filePath = join9(this.basePath, `${session.id}.json`);
116185
+ const filePath = join10(this.basePath, `${session.id}.json`);
115840
116186
  writeFileSync4(filePath, JSON.stringify(session, null, 2));
115841
116187
  }
115842
116188
  get(id) {
115843
- const filePath = join9(this.basePath, `${id}.json`);
116189
+ const filePath = join10(this.basePath, `${id}.json`);
115844
116190
  if (!existsSync6(filePath)) {
115845
116191
  return null;
115846
116192
  }
@@ -115856,7 +116202,7 @@ class VerificationSessionStore {
115856
116202
  const files = this.listFiles();
115857
116203
  for (const file of files) {
115858
116204
  try {
115859
- const content = readFileSync3(join9(this.basePath, file), "utf-8");
116205
+ const content = readFileSync3(join10(this.basePath, file), "utf-8");
115860
116206
  const session = JSON.parse(content);
115861
116207
  if (session.parentSessionId === parentSessionId) {
115862
116208
  sessions.push(session);
@@ -115872,7 +116218,7 @@ class VerificationSessionStore {
115872
116218
  const files = this.listFiles();
115873
116219
  for (const file of files) {
115874
116220
  try {
115875
- const content = readFileSync3(join9(this.basePath, file), "utf-8");
116221
+ const content = readFileSync3(join10(this.basePath, file), "utf-8");
115876
116222
  const session = JSON.parse(content);
115877
116223
  sessions.push(session);
115878
116224
  } catch {
@@ -115902,7 +116248,7 @@ class VerificationSessionStore {
115902
116248
  const sessions = [];
115903
116249
  for (const file of files) {
115904
116250
  try {
115905
- const content = readFileSync3(join9(this.basePath, file), "utf-8");
116251
+ const content = readFileSync3(join10(this.basePath, file), "utf-8");
115906
116252
  const session = JSON.parse(content);
115907
116253
  sessions.push({
115908
116254
  file,
@@ -115917,7 +116263,7 @@ class VerificationSessionStore {
115917
116263
  for (const item of toRemove) {
115918
116264
  try {
115919
116265
  const { unlinkSync: unlinkSync2 } = __require("fs");
115920
- unlinkSync2(join9(this.basePath, item.file));
116266
+ unlinkSync2(join10(this.basePath, item.file));
115921
116267
  } catch {
115922
116268
  continue;
115923
116269
  }
@@ -115928,7 +116274,7 @@ class VerificationSessionStore {
115928
116274
  for (const file of files) {
115929
116275
  try {
115930
116276
  const { unlinkSync: unlinkSync2 } = __require("fs");
115931
- unlinkSync2(join9(this.basePath, file));
116277
+ unlinkSync2(join10(this.basePath, file));
115932
116278
  } catch {
115933
116279
  continue;
115934
116280
  }
@@ -116081,7 +116427,7 @@ function createScopeVerificationHook() {
116081
116427
  }
116082
116428
  // packages/core/src/commands/loader.ts
116083
116429
  import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
116084
- import { join as join10, basename as basename2, extname as extname2 } from "path";
116430
+ import { join as join11, basename as basename2, extname as extname2 } from "path";
116085
116431
  import { homedir as homedir8 } from "os";
116086
116432
 
116087
116433
  class CommandLoader {
@@ -116094,9 +116440,9 @@ class CommandLoader {
116094
116440
  this.commands.clear();
116095
116441
  const envHome = process.env.HOME || process.env.USERPROFILE;
116096
116442
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir8();
116097
- const globalDir = join10(homeDir, ".assistants", "commands");
116443
+ const globalDir = join11(homeDir, ".assistants", "commands");
116098
116444
  await this.loadFromDirectory(globalDir, "global");
116099
- const projectDir = join10(this.cwd, ".assistants", "commands");
116445
+ const projectDir = join11(this.cwd, ".assistants", "commands");
116100
116446
  await this.loadFromDirectory(projectDir, "project");
116101
116447
  }
116102
116448
  async loadFromDirectory(dir, source, prefix = "") {
@@ -116104,12 +116450,12 @@ class CommandLoader {
116104
116450
  return;
116105
116451
  const entries = readdirSync3(dir);
116106
116452
  for (const entry of entries) {
116107
- const fullPath = join10(dir, entry);
116108
- const stat = statSync2(fullPath);
116109
- if (stat.isDirectory()) {
116453
+ const fullPath = join11(dir, entry);
116454
+ const stat2 = statSync2(fullPath);
116455
+ if (stat2.isDirectory()) {
116110
116456
  const newPrefix = prefix ? `${prefix}:${entry}` : entry;
116111
116457
  await this.loadFromDirectory(fullPath, source, newPrefix);
116112
- } else if (stat.isFile() && extname2(entry) === ".md") {
116458
+ } else if (stat2.isFile() && extname2(entry) === ".md") {
116113
116459
  const command = await this.loadCommandFile(fullPath, prefix);
116114
116460
  if (command) {
116115
116461
  this.commands.set(command.name, command);
@@ -116340,18 +116686,18 @@ ${stderr}`;
116340
116686
  }
116341
116687
  }
116342
116688
  // packages/core/src/commands/builtin.ts
116343
- import { join as join13 } from "path";
116689
+ import { join as join15 } from "path";
116344
116690
  import { homedir as homedir10, platform as platform2, release, arch } from "os";
116345
116691
  import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
116346
116692
  init_src();
116347
116693
 
116348
116694
  // packages/core/src/projects/store.ts
116349
116695
  init_src();
116350
- import { join as join11 } from "path";
116351
- import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
116696
+ import { join as join12 } from "path";
116697
+ import { mkdir as mkdir4, readdir as readdir2, readFile as readFile2, unlink as unlink2, writeFile as writeFile3 } from "fs/promises";
116352
116698
  var SAFE_ID_PATTERN2 = /^[a-zA-Z0-9_-]+$/;
116353
116699
  function projectsDir(cwd2) {
116354
- return join11(cwd2, ".assistants", "projects");
116700
+ return join12(cwd2, ".assistants", "projects");
116355
116701
  }
116356
116702
  function isSafeId2(id) {
116357
116703
  return SAFE_ID_PATTERN2.test(id);
@@ -116359,12 +116705,12 @@ function isSafeId2(id) {
116359
116705
  function projectPath(cwd2, id) {
116360
116706
  if (!isSafeId2(id))
116361
116707
  return null;
116362
- return join11(projectsDir(cwd2), `${id}.json`);
116708
+ return join12(projectsDir(cwd2), `${id}.json`);
116363
116709
  }
116364
116710
  async function ensureProjectsDir(cwd2) {
116365
- await mkdir3(projectsDir(cwd2), { recursive: true });
116711
+ await mkdir4(projectsDir(cwd2), { recursive: true });
116366
116712
  }
116367
- function normalizeName(name) {
116713
+ function normalizeName2(name) {
116368
116714
  return name.trim().toLowerCase();
116369
116715
  }
116370
116716
  async function listProjects(cwd2) {
@@ -116376,7 +116722,7 @@ async function listProjects(cwd2) {
116376
116722
  if (!file.endsWith(".json"))
116377
116723
  continue;
116378
116724
  try {
116379
- const raw = await readFile2(join11(dir, file), "utf-8");
116725
+ const raw = await readFile2(join12(dir, file), "utf-8");
116380
116726
  const parsed = JSON.parse(raw);
116381
116727
  if (parsed?.id && parsed?.name) {
116382
116728
  projects.push(parsed);
@@ -116403,9 +116749,9 @@ async function readProject(cwd2, id) {
116403
116749
  }
116404
116750
  }
116405
116751
  async function findProjectByName(cwd2, name) {
116406
- const normalized = normalizeName(name);
116752
+ const normalized = normalizeName2(name);
116407
116753
  const projects = await listProjects(cwd2);
116408
- return projects.find((project) => normalizeName(project.name) === normalized) || null;
116754
+ return projects.find((project) => normalizeName2(project.name) === normalized) || null;
116409
116755
  }
116410
116756
  async function saveProject(cwd2, project) {
116411
116757
  await ensureProjectsDir(cwd2);
@@ -116413,7 +116759,7 @@ async function saveProject(cwd2, project) {
116413
116759
  if (!path) {
116414
116760
  throw new Error(`Invalid project id: ${project.id}`);
116415
116761
  }
116416
- await writeFile2(path, JSON.stringify(project, null, 2), "utf-8");
116762
+ await writeFile3(path, JSON.stringify(project, null, 2), "utf-8");
116417
116763
  }
116418
116764
  async function deleteProject(cwd2, id) {
116419
116765
  try {
@@ -116455,14 +116801,14 @@ async function ensureDefaultProject(cwd2) {
116455
116801
  return createProject(cwd2, "default", "Default project for this folder");
116456
116802
  }
116457
116803
  function hasProjectNameConflict(projects, name) {
116458
- const normalized = normalizeName(name);
116459
- return projects.some((project) => normalizeName(project.name) === normalized);
116804
+ const normalized = normalizeName2(name);
116805
+ return projects.some((project) => normalizeName2(project.name) === normalized);
116460
116806
  }
116461
116807
 
116462
116808
  // packages/core/src/projects/context.ts
116463
116809
  import { readFile as readFile3 } from "fs/promises";
116464
116810
  import { homedir as homedir9 } from "os";
116465
- import { resolve as resolve4, join as join12 } from "path";
116811
+ import { resolve as resolve4, join as join13 } from "path";
116466
116812
  var DEFAULT_MAX_FILE_BYTES = 12000;
116467
116813
  function singleLine(value) {
116468
116814
  return value.replace(/[\r\n]+/g, " ").trim();
@@ -116481,7 +116827,7 @@ function normalizeEntryLabel(entry) {
116481
116827
  }
116482
116828
  async function renderFileEntry(entry, options) {
116483
116829
  const rawPath = entry.value.trim();
116484
- const expandedPath = rawPath === "~" ? homedir9() : rawPath.startsWith("~/") ? join12(homedir9(), rawPath.slice(2)) : rawPath;
116830
+ const expandedPath = rawPath === "~" ? homedir9() : rawPath.startsWith("~/") ? join13(homedir9(), rawPath.slice(2)) : rawPath;
116485
116831
  const resolved = resolve4(options.cwd, expandedPath);
116486
116832
  const validation = await validatePath(resolved, { allowedPaths: [options.cwd] });
116487
116833
  if (!validation.valid) {
@@ -116567,7 +116913,617 @@ async function buildProjectContext(project, options) {
116567
116913
  return lines.join(`
116568
116914
  `);
116569
116915
  }
116916
+ // packages/core/src/jobs/job-manager.ts
116917
+ init_src();
116918
+
116919
+ // packages/core/src/jobs/job-store.ts
116920
+ import { join as join14 } from "path";
116921
+ import { mkdir as mkdir5, readdir as readdir3, readFile as readFile4, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
116922
+ var SAFE_ID_PATTERN3 = /^[a-zA-Z0-9_-]+$/;
116923
+ function jobsDir() {
116924
+ return join14(getConfigDir(), "jobs");
116925
+ }
116926
+ function isSafeId3(id) {
116927
+ return SAFE_ID_PATTERN3.test(id);
116928
+ }
116929
+ function jobPath(id) {
116930
+ if (!isSafeId3(id))
116931
+ return null;
116932
+ return join14(jobsDir(), `${id}.json`);
116933
+ }
116934
+ async function ensureDir() {
116935
+ await mkdir5(jobsDir(), { recursive: true });
116936
+ }
116937
+ async function saveJob(job) {
116938
+ await ensureDir();
116939
+ const path = jobPath(job.id);
116940
+ if (!path) {
116941
+ throw new Error(`Invalid job id: ${job.id}`);
116942
+ }
116943
+ await writeFile4(path, JSON.stringify(job, null, 2), "utf-8");
116944
+ }
116945
+ async function readJob(id) {
116946
+ try {
116947
+ const path = jobPath(id);
116948
+ if (!path)
116949
+ return null;
116950
+ const raw = await readFile4(path, "utf-8");
116951
+ const job = JSON.parse(raw);
116952
+ if (!job?.id)
116953
+ return null;
116954
+ return job;
116955
+ } catch {
116956
+ return null;
116957
+ }
116958
+ }
116959
+ async function deleteJob(id) {
116960
+ try {
116961
+ const path = jobPath(id);
116962
+ if (!path)
116963
+ return false;
116964
+ await unlink3(path);
116965
+ return true;
116966
+ } catch {
116967
+ return false;
116968
+ }
116969
+ }
116970
+ async function listJobs() {
116971
+ try {
116972
+ const dir = jobsDir();
116973
+ const files = await readdir3(dir);
116974
+ const jobs = [];
116975
+ for (const file of files) {
116976
+ if (!file.endsWith(".json"))
116977
+ continue;
116978
+ try {
116979
+ const raw = await readFile4(join14(dir, file), "utf-8");
116980
+ const job = JSON.parse(raw);
116981
+ if (job?.id)
116982
+ jobs.push(job);
116983
+ } catch {}
116984
+ }
116985
+ return jobs;
116986
+ } catch {
116987
+ return [];
116988
+ }
116989
+ }
116990
+ async function listJobsForSession(sessionId) {
116991
+ const jobs = await listJobs();
116992
+ return jobs.filter((job) => job.sessionId === sessionId);
116993
+ }
116994
+ async function listJobsByStatus(status) {
116995
+ const jobs = await listJobs();
116996
+ return jobs.filter((job) => job.status === status);
116997
+ }
116998
+ async function updateJob(id, updater) {
116999
+ const job = await readJob(id);
117000
+ if (!job)
117001
+ return null;
117002
+ const updated = updater(job);
117003
+ await saveJob(updated);
117004
+ return updated;
117005
+ }
117006
+ async function cleanupOldJobs(maxAgeMs) {
117007
+ const jobs = await listJobs();
117008
+ const now2 = Date.now();
117009
+ let cleaned = 0;
117010
+ for (const job of jobs) {
117011
+ if (["completed", "failed", "timeout", "cancelled"].includes(job.status) && now2 - job.createdAt > maxAgeMs) {
117012
+ const deleted = await deleteJob(job.id);
117013
+ if (deleted)
117014
+ cleaned++;
117015
+ }
117016
+ }
117017
+ return cleaned;
117018
+ }
117019
+ async function cleanupSessionJobs(sessionId) {
117020
+ const jobs = await listJobsForSession(sessionId);
117021
+ let cleaned = 0;
117022
+ for (const job of jobs) {
117023
+ if (["completed", "failed", "timeout", "cancelled"].includes(job.status)) {
117024
+ const deleted = await deleteJob(job.id);
117025
+ if (deleted)
117026
+ cleaned++;
117027
+ }
117028
+ }
117029
+ return cleaned;
117030
+ }
116570
117031
 
117032
+ // packages/core/src/jobs/job-manager.ts
117033
+ init_codes();
117034
+ var DEFAULT_TIMEOUT_MS = 60000;
117035
+ var DEFAULT_MAX_JOB_AGE_MS = 24 * 60 * 60 * 1000;
117036
+
117037
+ class JobManager {
117038
+ config;
117039
+ sessionId;
117040
+ runningJobs = new Map;
117041
+ timedOutJobs = new Set;
117042
+ cancelledJobs = new Set;
117043
+ completionCallbacks = [];
117044
+ constructor(config = {}, sessionId) {
117045
+ this.config = config;
117046
+ this.sessionId = sessionId;
117047
+ }
117048
+ onJobComplete(callback) {
117049
+ this.completionCallbacks.push(callback);
117050
+ }
117051
+ isEnabled() {
117052
+ return this.config.enabled !== false;
117053
+ }
117054
+ shouldRunAsync(connectorName, input) {
117055
+ if (!this.isEnabled())
117056
+ return false;
117057
+ if (input.async === false)
117058
+ return false;
117059
+ if (input.async === true)
117060
+ return true;
117061
+ const connectorConfig = this.config.connectors?.[connectorName];
117062
+ return connectorConfig?.enabled === true;
117063
+ }
117064
+ getTimeout(connectorName) {
117065
+ const connectorConfig = this.config.connectors?.[connectorName];
117066
+ return connectorConfig?.timeoutMs ?? this.config.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
117067
+ }
117068
+ async startJob(connectorName, command, input, cli) {
117069
+ const jobId = generateId();
117070
+ const timeoutMs = this.getTimeout(connectorName);
117071
+ const now2 = Date.now();
117072
+ const job = {
117073
+ id: jobId,
117074
+ sessionId: this.sessionId,
117075
+ connectorName,
117076
+ command,
117077
+ input,
117078
+ status: "pending",
117079
+ createdAt: now2,
117080
+ timeoutMs
117081
+ };
117082
+ await saveJob(job);
117083
+ this.executeAsync(job, cli);
117084
+ return job;
117085
+ }
117086
+ async executeAsync(job, cli) {
117087
+ const runningJob = await updateJob(job.id, (j) => ({
117088
+ ...j,
117089
+ status: "running",
117090
+ startedAt: Date.now()
117091
+ }));
117092
+ if (!runningJob)
117093
+ return;
117094
+ const args = job.input.args || [];
117095
+ const options = job.input.options || {};
117096
+ const cwd2 = typeof job.input.cwd === "string" ? job.input.cwd : process.cwd();
117097
+ const cmdParts = [cli, ...job.command.split(" "), ...args];
117098
+ for (const [key, value] of Object.entries(options)) {
117099
+ if (key === "timeoutMs" || key === "timeout" || key === "async")
117100
+ continue;
117101
+ if (value === true) {
117102
+ cmdParts.push(`--${key}`);
117103
+ } else if (value !== false && value !== undefined) {
117104
+ cmdParts.push(`--${key}`, String(value));
117105
+ }
117106
+ }
117107
+ try {
117108
+ const proc = Bun.spawn(cmdParts, {
117109
+ cwd: cwd2,
117110
+ stdin: "ignore",
117111
+ stdout: "pipe",
117112
+ stderr: "pipe"
117113
+ });
117114
+ const timer = setTimeout(() => {
117115
+ this.handleTimeout(job.id, proc);
117116
+ }, job.timeoutMs);
117117
+ this.runningJobs.set(job.id, { proc, timer });
117118
+ const [stdout, stderr] = await Promise.all([
117119
+ new Response(proc.stdout).text(),
117120
+ new Response(proc.stderr).text()
117121
+ ]);
117122
+ const exitCode = await proc.exited;
117123
+ clearTimeout(timer);
117124
+ this.runningJobs.delete(job.id);
117125
+ if (this.timedOutJobs.has(job.id)) {
117126
+ this.timedOutJobs.delete(job.id);
117127
+ return;
117128
+ }
117129
+ if (this.cancelledJobs.has(job.id)) {
117130
+ this.cancelledJobs.delete(job.id);
117131
+ return;
117132
+ }
117133
+ const currentJob = await readJob(job.id);
117134
+ if (!currentJob || currentJob.status === "cancelled") {
117135
+ return;
117136
+ }
117137
+ const completedJob = await updateJob(job.id, (j) => ({
117138
+ ...j,
117139
+ status: exitCode === 0 ? "completed" : "failed",
117140
+ completedAt: Date.now(),
117141
+ result: {
117142
+ content: stdout.trim() || stderr.trim() || "Command completed",
117143
+ exitCode
117144
+ },
117145
+ error: exitCode !== 0 ? {
117146
+ code: ErrorCodes.CONNECTOR_EXECUTION_FAILED,
117147
+ message: stderr.trim() || `Exit code: ${exitCode}`
117148
+ } : undefined
117149
+ }));
117150
+ if (completedJob) {
117151
+ this.notifyCompletion(completedJob);
117152
+ }
117153
+ } catch (error) {
117154
+ const running = this.runningJobs.get(job.id);
117155
+ if (running) {
117156
+ clearTimeout(running.timer);
117157
+ this.runningJobs.delete(job.id);
117158
+ }
117159
+ if (this.timedOutJobs.has(job.id)) {
117160
+ this.timedOutJobs.delete(job.id);
117161
+ return;
117162
+ }
117163
+ if (this.cancelledJobs.has(job.id)) {
117164
+ this.cancelledJobs.delete(job.id);
117165
+ return;
117166
+ }
117167
+ const failedJob = await updateJob(job.id, (j) => ({
117168
+ ...j,
117169
+ status: "failed",
117170
+ completedAt: Date.now(),
117171
+ error: {
117172
+ code: ErrorCodes.CONNECTOR_EXECUTION_FAILED,
117173
+ message: error instanceof Error ? error.message : String(error)
117174
+ }
117175
+ }));
117176
+ if (failedJob) {
117177
+ this.notifyCompletion(failedJob);
117178
+ }
117179
+ }
117180
+ }
117181
+ async handleTimeout(jobId, proc) {
117182
+ this.timedOutJobs.add(jobId);
117183
+ try {
117184
+ proc.kill();
117185
+ } catch {}
117186
+ this.runningJobs.delete(jobId);
117187
+ const job = await readJob(jobId);
117188
+ if (!job)
117189
+ return;
117190
+ const timedOutJob = await updateJob(jobId, (j) => ({
117191
+ ...j,
117192
+ status: "timeout",
117193
+ completedAt: Date.now(),
117194
+ error: {
117195
+ code: "JOB_TIMEOUT",
117196
+ message: `Job timed out after ${Math.round(j.timeoutMs / 1000)} seconds`
117197
+ }
117198
+ }));
117199
+ if (timedOutJob) {
117200
+ this.notifyCompletion(timedOutJob);
117201
+ }
117202
+ }
117203
+ notifyCompletion(job) {
117204
+ const event = {
117205
+ jobId: job.id,
117206
+ status: job.status,
117207
+ connector: job.connectorName,
117208
+ summary: job.result?.content?.slice(0, 200) || job.error?.message || "Job completed"
117209
+ };
117210
+ for (const callback of this.completionCallbacks) {
117211
+ try {
117212
+ callback(event);
117213
+ } catch {}
117214
+ }
117215
+ }
117216
+ async getJobStatus(jobId) {
117217
+ return readJob(jobId);
117218
+ }
117219
+ async getJobResult(jobId, waitMs) {
117220
+ const job = await readJob(jobId);
117221
+ if (!job)
117222
+ return null;
117223
+ if (["completed", "failed", "timeout", "cancelled"].includes(job.status)) {
117224
+ return job;
117225
+ }
117226
+ if (!waitMs || waitMs <= 0) {
117227
+ return job;
117228
+ }
117229
+ const startTime = Date.now();
117230
+ const pollInterval = 500;
117231
+ while (Date.now() - startTime < waitMs) {
117232
+ await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
117233
+ const currentJob = await readJob(jobId);
117234
+ if (!currentJob)
117235
+ return null;
117236
+ if (["completed", "failed", "timeout", "cancelled"].includes(currentJob.status)) {
117237
+ return currentJob;
117238
+ }
117239
+ }
117240
+ return readJob(jobId);
117241
+ }
117242
+ async cancelJob(jobId) {
117243
+ const job = await readJob(jobId);
117244
+ if (!job)
117245
+ return false;
117246
+ if (!["pending", "running"].includes(job.status)) {
117247
+ return false;
117248
+ }
117249
+ this.cancelledJobs.add(jobId);
117250
+ const running = this.runningJobs.get(jobId);
117251
+ if (running) {
117252
+ clearTimeout(running.timer);
117253
+ try {
117254
+ running.proc.kill();
117255
+ } catch {}
117256
+ this.runningJobs.delete(jobId);
117257
+ }
117258
+ const cancelled = await updateJob(jobId, (j) => ({
117259
+ ...j,
117260
+ status: "cancelled",
117261
+ completedAt: Date.now(),
117262
+ error: {
117263
+ code: "JOB_CANCELLED",
117264
+ message: "Job was cancelled by user"
117265
+ }
117266
+ }));
117267
+ if (cancelled) {
117268
+ this.notifyCompletion(cancelled);
117269
+ }
117270
+ return cancelled !== null;
117271
+ }
117272
+ async listSessionJobs() {
117273
+ return listJobsForSession(this.sessionId);
117274
+ }
117275
+ async listRunningJobs() {
117276
+ return listJobsByStatus("running");
117277
+ }
117278
+ async cleanup() {
117279
+ const maxAge = this.config.maxJobAgeMs ?? DEFAULT_MAX_JOB_AGE_MS;
117280
+ return cleanupOldJobs(maxAge);
117281
+ }
117282
+ async shutdown() {
117283
+ for (const [jobId, running] of this.runningJobs) {
117284
+ this.cancelledJobs.add(jobId);
117285
+ clearTimeout(running.timer);
117286
+ try {
117287
+ running.proc.kill();
117288
+ } catch {}
117289
+ await updateJob(jobId, (j) => ({
117290
+ ...j,
117291
+ status: "cancelled",
117292
+ completedAt: Date.now(),
117293
+ error: {
117294
+ code: "JOB_CANCELLED",
117295
+ message: "Job was cancelled due to session shutdown"
117296
+ }
117297
+ }));
117298
+ }
117299
+ this.runningJobs.clear();
117300
+ }
117301
+ }
117302
+ // packages/core/src/jobs/tools.ts
117303
+ function createJobTools(getJobManager) {
117304
+ return [
117305
+ {
117306
+ tool: jobStatusTool,
117307
+ executor: createJobStatusExecutor(getJobManager)
117308
+ },
117309
+ {
117310
+ tool: jobResultTool,
117311
+ executor: createJobResultExecutor(getJobManager)
117312
+ },
117313
+ {
117314
+ tool: jobCancelTool,
117315
+ executor: createJobCancelExecutor(getJobManager)
117316
+ },
117317
+ {
117318
+ tool: jobListTool,
117319
+ executor: createJobListExecutor(getJobManager)
117320
+ }
117321
+ ];
117322
+ }
117323
+ var jobStatusTool = {
117324
+ name: "job_status",
117325
+ description: "Check the status of a background job. Returns the current status (pending, running, completed, failed, timeout, cancelled) and any available result or error.",
117326
+ parameters: {
117327
+ type: "object",
117328
+ properties: {
117329
+ job_id: {
117330
+ type: "string",
117331
+ description: "The ID of the job to check"
117332
+ }
117333
+ },
117334
+ required: ["job_id"]
117335
+ }
117336
+ };
117337
+ function createJobStatusExecutor(getJobManager) {
117338
+ return async (input) => {
117339
+ const manager = getJobManager();
117340
+ if (!manager) {
117341
+ return "Jobs system is not enabled";
117342
+ }
117343
+ const jobId = input.job_id;
117344
+ if (!jobId) {
117345
+ return "Error: job_id is required";
117346
+ }
117347
+ const job = await manager.getJobStatus(jobId);
117348
+ if (!job) {
117349
+ return `Job not found: ${jobId}`;
117350
+ }
117351
+ const parts = [
117352
+ `Job ID: ${job.id}`,
117353
+ `Status: ${job.status}`,
117354
+ `Connector: ${job.connectorName}`,
117355
+ `Command: ${job.command}`,
117356
+ `Created: ${new Date(job.createdAt).toISOString()}`
117357
+ ];
117358
+ if (job.startedAt) {
117359
+ parts.push(`Started: ${new Date(job.startedAt).toISOString()}`);
117360
+ }
117361
+ if (job.completedAt) {
117362
+ parts.push(`Completed: ${new Date(job.completedAt).toISOString()}`);
117363
+ const duration = job.completedAt - (job.startedAt || job.createdAt);
117364
+ parts.push(`Duration: ${(duration / 1000).toFixed(1)}s`);
117365
+ }
117366
+ if (job.status === "running") {
117367
+ const elapsed = Date.now() - (job.startedAt || job.createdAt);
117368
+ const remaining = job.timeoutMs - elapsed;
117369
+ parts.push(`Elapsed: ${(elapsed / 1000).toFixed(1)}s`);
117370
+ parts.push(`Timeout in: ${Math.max(0, remaining / 1000).toFixed(1)}s`);
117371
+ }
117372
+ if (job.result) {
117373
+ parts.push(`
117374
+ Result:
117375
+ ${job.result.content}`);
117376
+ }
117377
+ if (job.error) {
117378
+ parts.push(`
117379
+ Error (${job.error.code}): ${job.error.message}`);
117380
+ }
117381
+ return parts.join(`
117382
+ `);
117383
+ };
117384
+ }
117385
+ var jobResultTool = {
117386
+ name: "job_result",
117387
+ description: "Get the result of a background job. Optionally wait up to 30 seconds for the job to complete if still running.",
117388
+ parameters: {
117389
+ type: "object",
117390
+ properties: {
117391
+ job_id: {
117392
+ type: "string",
117393
+ description: "The ID of the job to get results from"
117394
+ },
117395
+ wait: {
117396
+ type: "boolean",
117397
+ description: "Whether to wait up to 30 seconds for the job to complete (default: false)",
117398
+ default: false
117399
+ }
117400
+ },
117401
+ required: ["job_id"]
117402
+ }
117403
+ };
117404
+ function createJobResultExecutor(getJobManager) {
117405
+ return async (input) => {
117406
+ const manager = getJobManager();
117407
+ if (!manager) {
117408
+ return "Jobs system is not enabled";
117409
+ }
117410
+ const jobId = input.job_id;
117411
+ if (!jobId) {
117412
+ return "Error: job_id is required";
117413
+ }
117414
+ const wait = input.wait === true;
117415
+ const waitMs = wait ? 30000 : 0;
117416
+ const job = await manager.getJobResult(jobId, waitMs);
117417
+ if (!job) {
117418
+ return `Job not found: ${jobId}`;
117419
+ }
117420
+ if (job.status === "pending" || job.status === "running") {
117421
+ return `Job is still ${job.status}. Use job_status to monitor or job_result with wait=true to wait for completion.`;
117422
+ }
117423
+ if (job.status === "completed" && job.result) {
117424
+ return job.result.content;
117425
+ }
117426
+ if (job.error) {
117427
+ return `Job ${job.status}: ${job.error.message}`;
117428
+ }
117429
+ return `Job ${job.status}`;
117430
+ };
117431
+ }
117432
+ var jobCancelTool = {
117433
+ name: "job_cancel",
117434
+ description: "Cancel a running or pending background job.",
117435
+ parameters: {
117436
+ type: "object",
117437
+ properties: {
117438
+ job_id: {
117439
+ type: "string",
117440
+ description: "The ID of the job to cancel"
117441
+ }
117442
+ },
117443
+ required: ["job_id"]
117444
+ }
117445
+ };
117446
+ function createJobCancelExecutor(getJobManager) {
117447
+ return async (input) => {
117448
+ const manager = getJobManager();
117449
+ if (!manager) {
117450
+ return "Jobs system is not enabled";
117451
+ }
117452
+ const jobId = input.job_id;
117453
+ if (!jobId) {
117454
+ return "Error: job_id is required";
117455
+ }
117456
+ const cancelled = await manager.cancelJob(jobId);
117457
+ if (cancelled) {
117458
+ return `Job ${jobId} has been cancelled`;
117459
+ }
117460
+ const job = await readJob(jobId);
117461
+ if (!job) {
117462
+ return `Job not found: ${jobId}`;
117463
+ }
117464
+ return `Cannot cancel job ${jobId}: status is ${job.status}`;
117465
+ };
117466
+ }
117467
+ var jobListTool = {
117468
+ name: "job_list",
117469
+ description: "List all background jobs for the current session.",
117470
+ parameters: {
117471
+ type: "object",
117472
+ properties: {
117473
+ status: {
117474
+ type: "string",
117475
+ description: "Filter by status (pending, running, completed, failed, timeout, cancelled)",
117476
+ enum: ["pending", "running", "completed", "failed", "timeout", "cancelled"]
117477
+ }
117478
+ }
117479
+ }
117480
+ };
117481
+ function createJobListExecutor(getJobManager) {
117482
+ return async (input) => {
117483
+ const manager = getJobManager();
117484
+ if (!manager) {
117485
+ return "Jobs system is not enabled";
117486
+ }
117487
+ let jobs = await manager.listSessionJobs();
117488
+ const statusFilter = input.status;
117489
+ if (statusFilter) {
117490
+ jobs = jobs.filter((j) => j.status === statusFilter);
117491
+ }
117492
+ if (jobs.length === 0) {
117493
+ return statusFilter ? `No jobs with status '${statusFilter}'` : "No jobs found";
117494
+ }
117495
+ jobs.sort((a, b) => b.createdAt - a.createdAt);
117496
+ const lines = [`Found ${jobs.length} job(s):
117497
+ `];
117498
+ for (const job of jobs) {
117499
+ const age = formatAge(Date.now() - job.createdAt);
117500
+ let line = `[${job.status.toUpperCase()}] ${job.id} - ${job.connectorName} ${job.command} (${age} ago)`;
117501
+ if (job.status === "running" && job.startedAt) {
117502
+ const elapsed = ((Date.now() - job.startedAt) / 1000).toFixed(1);
117503
+ line += ` - running for ${elapsed}s`;
117504
+ }
117505
+ if (job.error) {
117506
+ line += ` - ${job.error.message.slice(0, 50)}`;
117507
+ }
117508
+ lines.push(line);
117509
+ }
117510
+ return lines.join(`
117511
+ `);
117512
+ };
117513
+ }
117514
+ function formatAge(ms) {
117515
+ const seconds = Math.floor(ms / 1000);
117516
+ if (seconds < 60)
117517
+ return `${seconds}s`;
117518
+ const minutes = Math.floor(seconds / 60);
117519
+ if (minutes < 60)
117520
+ return `${minutes}m`;
117521
+ const hours = Math.floor(minutes / 60);
117522
+ if (hours < 24)
117523
+ return `${hours}h`;
117524
+ const days = Math.floor(hours / 24);
117525
+ return `${days}d`;
117526
+ }
116571
117527
  // packages/core/src/commands/builtin.ts
116572
117528
  var VERSION = process.env.ASSISTANTS_VERSION || process.env.npm_package_version || "unknown";
116573
117529
  function resolveAuthTimeout(resolve5) {
@@ -116648,6 +117604,7 @@ class BuiltinCommands {
116648
117604
  loader.register(this.initCommand());
116649
117605
  loader.register(this.costCommand());
116650
117606
  loader.register(this.modelCommand());
117607
+ loader.register(this.skillCommand());
116651
117608
  loader.register(this.skillsCommand(loader));
116652
117609
  loader.register(this.memoryCommand());
116653
117610
  loader.register(this.feedbackCommand());
@@ -116661,6 +117618,7 @@ class BuiltinCommands {
116661
117618
  loader.register(this.verificationCommand());
116662
117619
  loader.register(this.inboxCommand());
116663
117620
  loader.register(this.walletCommand());
117621
+ loader.register(this.jobsCommand());
116664
117622
  loader.register(this.exitCommand());
116665
117623
  }
116666
117624
  voiceCommand() {
@@ -117828,6 +118786,194 @@ Rate limit: ${status.readsUsed}/${status.maxReads} reads this hour
117828
118786
  }
117829
118787
  };
117830
118788
  }
118789
+ jobsCommand() {
118790
+ return {
118791
+ name: "jobs",
118792
+ description: "List and manage background jobs",
118793
+ builtin: true,
118794
+ selfHandled: true,
118795
+ content: "",
118796
+ handler: async (args, context) => {
118797
+ const parts = args.trim().split(/\s+/);
118798
+ const subcommand = parts[0]?.toLowerCase() || "list";
118799
+ const arg = parts[1] || "";
118800
+ switch (subcommand) {
118801
+ case "list":
118802
+ case "": {
118803
+ const jobs = await listJobsForSession(context.sessionId);
118804
+ if (jobs.length === 0) {
118805
+ context.emit("text", `No jobs found for this session.
118806
+ `);
118807
+ context.emit("done");
118808
+ return { handled: true };
118809
+ }
118810
+ jobs.sort((a, b) => b.createdAt - a.createdAt);
118811
+ let output = `
118812
+ | Status | ID | Connector | Command | Age |
118813
+ `;
118814
+ output += `|--------|----|-----------|---------|----- |
118815
+ `;
118816
+ for (const job of jobs) {
118817
+ const age = this.formatAge(Date.now() - job.createdAt);
118818
+ const shortId = job.id.slice(0, 8);
118819
+ const command = job.command.slice(0, 30);
118820
+ output += `| ${job.status.toUpperCase()} | ${shortId} | ${job.connectorName} | ${command} | ${age} |
118821
+ `;
118822
+ }
118823
+ context.emit("text", output);
118824
+ context.emit("done");
118825
+ return { handled: true };
118826
+ }
118827
+ case "all": {
118828
+ const jobs = await listJobs();
118829
+ if (jobs.length === 0) {
118830
+ context.emit("text", `No jobs found.
118831
+ `);
118832
+ context.emit("done");
118833
+ return { handled: true };
118834
+ }
118835
+ jobs.sort((a, b) => b.createdAt - a.createdAt);
118836
+ let output = `
118837
+ | Status | ID | Session | Connector | Command | Age |
118838
+ `;
118839
+ output += `|--------|----|---------|-----------|---------|-----|
118840
+ `;
118841
+ for (const job of jobs) {
118842
+ const age = this.formatAge(Date.now() - job.createdAt);
118843
+ const shortId = job.id.slice(0, 8);
118844
+ const shortSession = job.sessionId.slice(0, 8);
118845
+ const command = job.command.slice(0, 20);
118846
+ output += `| ${job.status.toUpperCase()} | ${shortId} | ${shortSession} | ${job.connectorName} | ${command} | ${age} |
118847
+ `;
118848
+ }
118849
+ context.emit("text", output);
118850
+ context.emit("done");
118851
+ return { handled: true };
118852
+ }
118853
+ case "cancel": {
118854
+ if (!arg) {
118855
+ context.emit("text", `Usage: /jobs cancel <job_id>
118856
+ `);
118857
+ context.emit("done");
118858
+ return { handled: true };
118859
+ }
118860
+ const jobs = await listJobs();
118861
+ const matches = jobs.filter((j) => j.id.startsWith(arg) || j.id === arg);
118862
+ if (matches.length === 0) {
118863
+ context.emit("text", `Job not found: ${arg}
118864
+ `);
118865
+ context.emit("done");
118866
+ return { handled: true };
118867
+ }
118868
+ if (matches.length > 1) {
118869
+ context.emit("text", `Ambiguous job ID. Matches: ${matches.map((j) => j.id).join(", ")}
118870
+ `);
118871
+ context.emit("done");
118872
+ return { handled: true };
118873
+ }
118874
+ const job = matches[0];
118875
+ if (!["pending", "running"].includes(job.status)) {
118876
+ context.emit("text", `Cannot cancel job ${job.id}: status is ${job.status}
118877
+ `);
118878
+ context.emit("done");
118879
+ return { handled: true };
118880
+ }
118881
+ context.emit("text", `Job ${job.id} marked for cancellation. Use job_cancel tool for full cancellation.
118882
+ `);
118883
+ context.emit("done");
118884
+ return { handled: true };
118885
+ }
118886
+ case "clear": {
118887
+ const cleaned = await cleanupSessionJobs(context.sessionId);
118888
+ context.emit("text", `Cleared ${cleaned} completed job(s).
118889
+ `);
118890
+ context.emit("done");
118891
+ return { handled: true };
118892
+ }
118893
+ case "help": {
118894
+ const help = `
118895
+ /jobs List jobs for current session
118896
+ /jobs list List jobs for current session
118897
+ /jobs all List all jobs across sessions
118898
+ /jobs <id> Show details of a specific job
118899
+ /jobs cancel <id> Cancel a running job
118900
+ /jobs clear Clear completed jobs for this session
118901
+ /jobs help Show this help
118902
+ `;
118903
+ context.emit("text", help);
118904
+ context.emit("done");
118905
+ return { handled: true };
118906
+ }
118907
+ default: {
118908
+ const jobs = await listJobs();
118909
+ const matches = jobs.filter((j) => j.id.startsWith(subcommand) || j.id === subcommand);
118910
+ if (matches.length === 0) {
118911
+ context.emit("text", `Job not found: ${subcommand}
118912
+ Use /jobs help for usage.
118913
+ `);
118914
+ context.emit("done");
118915
+ return { handled: true };
118916
+ }
118917
+ if (matches.length > 1) {
118918
+ context.emit("text", `Ambiguous job ID. Matches: ${matches.map((j) => j.id).join(", ")}
118919
+ `);
118920
+ context.emit("done");
118921
+ return { handled: true };
118922
+ }
118923
+ const job = matches[0];
118924
+ let output = `
118925
+ Job ID: ${job.id}
118926
+ Status: ${job.status}
118927
+ Connector: ${job.connectorName}
118928
+ Command: ${job.command}
118929
+ Session: ${job.sessionId}
118930
+ Created: ${new Date(job.createdAt).toISOString()}
118931
+ `;
118932
+ if (job.startedAt) {
118933
+ output += `Started: ${new Date(job.startedAt).toISOString()}
118934
+ `;
118935
+ }
118936
+ if (job.completedAt) {
118937
+ output += `Completed: ${new Date(job.completedAt).toISOString()}
118938
+ `;
118939
+ const duration = job.completedAt - (job.startedAt || job.createdAt);
118940
+ output += `Duration: ${(duration / 1000).toFixed(1)}s
118941
+ `;
118942
+ }
118943
+ output += `Timeout: ${job.timeoutMs / 1000}s
118944
+ `;
118945
+ if (job.result) {
118946
+ output += `
118947
+ Result:
118948
+ ${job.result.content}
118949
+ `;
118950
+ }
118951
+ if (job.error) {
118952
+ output += `
118953
+ Error (${job.error.code}): ${job.error.message}
118954
+ `;
118955
+ }
118956
+ context.emit("text", output);
118957
+ context.emit("done");
118958
+ return { handled: true };
118959
+ }
118960
+ }
118961
+ }
118962
+ };
118963
+ }
118964
+ formatAge(ms) {
118965
+ const seconds = Math.floor(ms / 1000);
118966
+ if (seconds < 60)
118967
+ return `${seconds}s`;
118968
+ const minutes = Math.floor(seconds / 60);
118969
+ if (minutes < 60)
118970
+ return `${minutes}m`;
118971
+ const hours = Math.floor(minutes / 60);
118972
+ if (hours < 24)
118973
+ return `${hours}h`;
118974
+ const days = Math.floor(hours / 24);
118975
+ return `${days}d`;
118976
+ }
117831
118977
  exitCommand() {
117832
118978
  return {
117833
118979
  name: "exit",
@@ -118660,6 +119806,198 @@ Energy restored.
118660
119806
  }
118661
119807
  };
118662
119808
  }
119809
+ skillCommand() {
119810
+ return {
119811
+ name: "skill",
119812
+ description: "Create or manage skills",
119813
+ builtin: true,
119814
+ selfHandled: true,
119815
+ content: "",
119816
+ handler: async (args, context) => {
119817
+ const tokens = splitArgs(args || "");
119818
+ const subcommand = tokens.shift()?.toLowerCase();
119819
+ const emitHelp = () => {
119820
+ let message = `
119821
+ **/skill commands**
119822
+
119823
+ `;
119824
+ message += `/skill create <name> [--project|--global] [options]
119825
+ `;
119826
+ message += `
119827
+ Options:
119828
+ `;
119829
+ message += ` --project Create in project (.assistants/skills)
119830
+ `;
119831
+ message += ` --global Create globally (~/.assistants/shared/skills)
119832
+ `;
119833
+ message += ` --desc "..." Description
119834
+ `;
119835
+ message += ` --tools a,b,c Allowed tools list
119836
+ `;
119837
+ message += ` --hint "..." Argument hint
119838
+ `;
119839
+ message += ` --content "..." Skill body content
119840
+ `;
119841
+ message += ` --interactive Ask follow-up questions
119842
+ `;
119843
+ message += ` --force Overwrite existing skill
119844
+ `;
119845
+ message += ` --yes Accept default (project) scope
119846
+ `;
119847
+ message += `
119848
+ Notes:
119849
+ `;
119850
+ message += ` - Skill directories must start with "skill-"
119851
+ `;
119852
+ message += ` - Skill names should not include the word "skill"
119853
+ `;
119854
+ context.emit("text", message);
119855
+ context.emit("done");
119856
+ };
119857
+ if (!subcommand || subcommand === "help") {
119858
+ emitHelp();
119859
+ return { handled: true };
119860
+ }
119861
+ if (subcommand !== "create") {
119862
+ context.emit("text", `Unknown /skill command: ${subcommand}
119863
+ `);
119864
+ context.emit("text", `Use /skill help for available commands.
119865
+ `);
119866
+ context.emit("done");
119867
+ return { handled: true };
119868
+ }
119869
+ let name;
119870
+ let scope;
119871
+ let description;
119872
+ let argumentHint;
119873
+ let content;
119874
+ let overwrite = false;
119875
+ let yes = false;
119876
+ let interactive = false;
119877
+ let allowedTools;
119878
+ for (let i = 0;i < tokens.length; i += 1) {
119879
+ const token = tokens[i];
119880
+ if (!token)
119881
+ continue;
119882
+ if (token.startsWith("--")) {
119883
+ switch (token) {
119884
+ case "--project":
119885
+ scope = "project";
119886
+ break;
119887
+ case "--global":
119888
+ scope = "global";
119889
+ break;
119890
+ case "--desc":
119891
+ case "--description":
119892
+ description = tokens[i + 1];
119893
+ i += 1;
119894
+ break;
119895
+ case "--tools": {
119896
+ const list = tokens[i + 1] || "";
119897
+ allowedTools = list.split(",").map((tool) => tool.trim()).filter(Boolean);
119898
+ i += 1;
119899
+ break;
119900
+ }
119901
+ case "--hint":
119902
+ argumentHint = tokens[i + 1];
119903
+ i += 1;
119904
+ break;
119905
+ case "--content":
119906
+ content = tokens[i + 1];
119907
+ i += 1;
119908
+ break;
119909
+ case "--interactive":
119910
+ case "--ask":
119911
+ case "--interview":
119912
+ interactive = true;
119913
+ break;
119914
+ case "--force":
119915
+ case "--overwrite":
119916
+ overwrite = true;
119917
+ break;
119918
+ case "--yes":
119919
+ yes = true;
119920
+ break;
119921
+ default:
119922
+ break;
119923
+ }
119924
+ } else if (!name) {
119925
+ name = token;
119926
+ }
119927
+ }
119928
+ if (!name) {
119929
+ context.emit("text", `Usage: /skill create <name> [--project|--global]
119930
+ `);
119931
+ context.emit("done");
119932
+ return { handled: true };
119933
+ }
119934
+ if (interactive || !scope && !yes) {
119935
+ const known = [];
119936
+ if (scope)
119937
+ known.push(`scope: ${scope}`);
119938
+ if (description)
119939
+ known.push(`description: ${description}`);
119940
+ if (content)
119941
+ known.push(`content: provided`);
119942
+ if (allowedTools && allowedTools.length > 0)
119943
+ known.push(`allowed_tools: ${allowedTools.join(", ")}`);
119944
+ if (argumentHint)
119945
+ known.push(`argument_hint: ${argumentHint}`);
119946
+ const missing = [];
119947
+ if (!scope)
119948
+ missing.push("scope (project/global, default project)");
119949
+ if (!description)
119950
+ missing.push("description");
119951
+ if (!content)
119952
+ missing.push("content (multi-line allowed)");
119953
+ if (!allowedTools || allowedTools.length === 0)
119954
+ missing.push("allowed tools (optional)");
119955
+ if (!argumentHint)
119956
+ missing.push("argument hint (optional)");
119957
+ const knownBlock = known.length > 0 ? `Known values:\\n- ${known.join("\\n- ")}\\n\\n` : "";
119958
+ const missingBlock = missing.length > 0 ? `Ask for:\\n- ${missing.join("\\n- ")}\\n\\n` : "";
119959
+ context.emit("done");
119960
+ return {
119961
+ handled: false,
119962
+ prompt: `We are creating a new skill named "${name}".\\n\\n${knownBlock}${missingBlock}Use the ask_user tool to interview the user and collect missing fields. Then call skill_create with name, scope, and any provided fields. If the user leaves optional fields blank, omit them. If scope is not specified, default to project.`
119963
+ };
119964
+ }
119965
+ const finalScope = scope ?? "project";
119966
+ try {
119967
+ const result = await createSkill({
119968
+ name,
119969
+ scope: finalScope,
119970
+ description,
119971
+ allowedTools,
119972
+ argumentHint,
119973
+ content,
119974
+ cwd: context.cwd,
119975
+ overwrite
119976
+ });
119977
+ await context.refreshSkills?.();
119978
+ let message = `
119979
+ Created skill "${result.name}" (${result.scope}).
119980
+ `;
119981
+ message += `Location: ${result.filePath}
119982
+ `;
119983
+ message += `Invoke with: $${result.name} [args] or /${result.name} [args]
119984
+ `;
119985
+ if (!scope) {
119986
+ message += `Defaulted to project scope. Use --global for a global skill.
119987
+ `;
119988
+ }
119989
+ context.emit("text", message);
119990
+ context.emit("done");
119991
+ return { handled: true };
119992
+ } catch (error) {
119993
+ context.emit("text", `Failed to create skill: ${error instanceof Error ? error.message : String(error)}
119994
+ `);
119995
+ context.emit("done");
119996
+ return { handled: true };
119997
+ }
119998
+ }
119999
+ };
120000
+ }
118663
120001
  skillsCommand(loader) {
118664
120002
  return {
118665
120003
  name: "skills",
@@ -118674,6 +120012,9 @@ Energy restored.
118674
120012
  `;
118675
120013
  message += `Skills are invoked with $skill-name [arguments] or /skill-name [arguments]
118676
120014
 
120015
+ `;
120016
+ message += `Create a skill with /skill create <name>
120017
+
118677
120018
  `;
118678
120019
  if (context.skills.length === 0) {
118679
120020
  message += `No skills loaded.
@@ -118803,8 +120144,8 @@ ${context.skills.length} skill(s) available.
118803
120144
  `;
118804
120145
  message += `| --- | --- | --- |
118805
120146
  `;
118806
- for (const stat of errorStats.slice(0, 5)) {
118807
- message += `| ${stat.code} | ${stat.count} | ${stat.lastOccurrence} |
120147
+ for (const stat2 of errorStats.slice(0, 5)) {
120148
+ message += `| ${stat2.code} | ${stat2.count} | ${stat2.lastOccurrence} |
118808
120149
  `;
118809
120150
  }
118810
120151
  }
@@ -118838,9 +120179,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
118838
120179
  content: "",
118839
120180
  handler: async (args, context) => {
118840
120181
  const configPaths = [
118841
- join13(context.cwd, ".assistants", "config.json"),
118842
- join13(context.cwd, ".assistants", "config.local.json"),
118843
- join13(getConfigDir(), "config.json")
120182
+ join15(context.cwd, ".assistants", "config.json"),
120183
+ join15(context.cwd, ".assistants", "config.local.json"),
120184
+ join15(getConfigDir(), "config.json")
118844
120185
  ];
118845
120186
  let message = `
118846
120187
  **Configuration**
@@ -118858,9 +120199,9 @@ Format the summary as a brief bullet-point list. This summary will replace the c
118858
120199
  message += `
118859
120200
  **Commands Directories:**
118860
120201
  `;
118861
- message += ` - Project: ${join13(context.cwd, ".assistants", "commands")}
120202
+ message += ` - Project: ${join15(context.cwd, ".assistants", "commands")}
118862
120203
  `;
118863
- message += ` - Global: ${join13(homeDir, ".assistants", "commands")}
120204
+ message += ` - Global: ${join15(homeDir, ".assistants", "commands")}
118864
120205
  `;
118865
120206
  context.emit("text", message);
118866
120207
  context.emit("done");
@@ -118876,7 +120217,7 @@ Format the summary as a brief bullet-point list. This summary will replace the c
118876
120217
  selfHandled: true,
118877
120218
  content: "",
118878
120219
  handler: async (args, context) => {
118879
- const commandsDir = join13(context.cwd, ".assistants", "commands");
120220
+ const commandsDir = join15(context.cwd, ".assistants", "commands");
118880
120221
  mkdirSync4(commandsDir, { recursive: true });
118881
120222
  const exampleCommand = `---
118882
120223
  name: reflect
@@ -118892,7 +120233,7 @@ Please summarize the last interaction and suggest 2-3 next steps.
118892
120233
  - Focus on clarity
118893
120234
  - Ask a follow-up question if needed
118894
120235
  `;
118895
- const examplePath = join13(commandsDir, "reflect.md");
120236
+ const examplePath = join15(commandsDir, "reflect.md");
118896
120237
  if (!existsSync8(examplePath)) {
118897
120238
  writeFileSync5(examplePath, exampleCommand);
118898
120239
  }
@@ -119660,7 +121001,7 @@ async function createLLMClient(config) {
119660
121001
  // packages/core/src/heartbeat/manager.ts
119661
121002
  import { dirname as dirname6 } from "path";
119662
121003
  import { mkdirSync as mkdirSync5 } from "fs";
119663
- import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
121004
+ import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
119664
121005
 
119665
121006
  class HeartbeatManager {
119666
121007
  config;
@@ -119739,12 +121080,12 @@ class HeartbeatManager {
119739
121080
  }
119740
121081
  async persist(heartbeat) {
119741
121082
  try {
119742
- await writeFile3(this.config.persistPath, JSON.stringify(heartbeat, null, 2));
121083
+ await writeFile5(this.config.persistPath, JSON.stringify(heartbeat, null, 2));
119743
121084
  } catch {}
119744
121085
  }
119745
121086
  static async checkStale(path2, thresholdMs) {
119746
121087
  try {
119747
- const content = await readFile4(path2, "utf-8");
121088
+ const content = await readFile5(path2, "utf-8");
119748
121089
  const heartbeat = JSON.parse(content);
119749
121090
  const age = Date.now() - new Date(heartbeat.timestamp).getTime();
119750
121091
  return { isStale: age > thresholdMs, lastHeartbeat: heartbeat };
@@ -119756,7 +121097,7 @@ class HeartbeatManager {
119756
121097
  // packages/core/src/heartbeat/persistence.ts
119757
121098
  import { dirname as dirname7 } from "path";
119758
121099
  import { mkdirSync as mkdirSync6 } from "fs";
119759
- import { readFile as readFile5, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
121100
+ import { readFile as readFile6, writeFile as writeFile6, unlink as unlink4 } from "fs/promises";
119760
121101
 
119761
121102
  class StatePersistence {
119762
121103
  path;
@@ -119766,12 +121107,12 @@ class StatePersistence {
119766
121107
  }
119767
121108
  async save(state) {
119768
121109
  try {
119769
- await writeFile4(this.path, JSON.stringify(state, null, 2));
121110
+ await writeFile6(this.path, JSON.stringify(state, null, 2));
119770
121111
  } catch {}
119771
121112
  }
119772
121113
  async load() {
119773
121114
  try {
119774
- const content = await readFile5(this.path, "utf-8");
121115
+ const content = await readFile6(this.path, "utf-8");
119775
121116
  return JSON.parse(content);
119776
121117
  } catch {
119777
121118
  return null;
@@ -119779,7 +121120,7 @@ class StatePersistence {
119779
121120
  }
119780
121121
  async clear() {
119781
121122
  try {
119782
- await unlink3(this.path);
121123
+ await unlink4(this.path);
119783
121124
  } catch {}
119784
121125
  }
119785
121126
  }
@@ -119983,7 +121324,7 @@ class EnergyManager {
119983
121324
  // packages/core/src/energy/storage.ts
119984
121325
  import { dirname as dirname8 } from "path";
119985
121326
  import { mkdirSync as mkdirSync7 } from "fs";
119986
- import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
121327
+ import { readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
119987
121328
 
119988
121329
  class EnergyStorage {
119989
121330
  path;
@@ -119993,12 +121334,12 @@ class EnergyStorage {
119993
121334
  }
119994
121335
  async save(state) {
119995
121336
  try {
119996
- await writeFile5(this.path, JSON.stringify(state, null, 2));
121337
+ await writeFile7(this.path, JSON.stringify(state, null, 2));
119997
121338
  } catch {}
119998
121339
  }
119999
121340
  async load() {
120000
121341
  try {
120001
- const content = await readFile6(this.path, "utf-8");
121342
+ const content = await readFile7(this.path, "utf-8");
120002
121343
  return JSON.parse(content);
120003
121344
  } catch {
120004
121345
  return null;
@@ -120058,12 +121399,12 @@ function validateToolCalls(toolCalls, tools) {
120058
121399
  // packages/core/src/voice/utils.ts
120059
121400
  import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
120060
121401
  import { homedir as homedir12 } from "os";
120061
- import { join as join15 } from "path";
121402
+ import { join as join17 } from "path";
120062
121403
  import { spawnSync } from "child_process";
120063
121404
  function loadApiKeyFromSecrets2(key) {
120064
121405
  const envHome = process.env.HOME || process.env.USERPROFILE;
120065
121406
  const homeDir = envHome && envHome.trim().length > 0 ? envHome : homedir12();
120066
- const secretsPath = join15(homeDir, ".secrets");
121407
+ const secretsPath = join17(homeDir, ".secrets");
120067
121408
  if (!existsSync10(secretsPath))
120068
121409
  return;
120069
121410
  try {
@@ -120137,7 +121478,7 @@ class SystemSTT {
120137
121478
  // packages/core/src/voice/tts.ts
120138
121479
  import { spawnSync as spawnSync2 } from "child_process";
120139
121480
  import { tmpdir as tmpdir2 } from "os";
120140
- import { join as join16 } from "path";
121481
+ import { join as join18 } from "path";
120141
121482
  import { readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
120142
121483
  class ElevenLabsTTS {
120143
121484
  apiKey;
@@ -120241,7 +121582,7 @@ class SystemTTS {
120241
121582
  if (!say) {
120242
121583
  throw new Error('System TTS not available: missing "say" command.');
120243
121584
  }
120244
- const output = join16(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
121585
+ const output = join18(tmpdir2(), `assistants-tts-${Date.now()}.aiff`);
120245
121586
  const args = [];
120246
121587
  if (this.voiceId) {
120247
121588
  args.push("-v", this.voiceId);
@@ -120263,7 +121604,7 @@ class SystemTTS {
120263
121604
  }
120264
121605
  const espeak = findExecutable("espeak") || findExecutable("espeak-ng");
120265
121606
  if (espeak) {
120266
- const output = join16(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
121607
+ const output = join18(tmpdir2(), `assistants-tts-${Date.now()}.wav`);
120267
121608
  const args = ["-w", output];
120268
121609
  if (this.voiceId) {
120269
121610
  args.push("-v", this.voiceId);
@@ -120290,14 +121631,14 @@ class SystemTTS {
120290
121631
  // packages/core/src/voice/player.ts
120291
121632
  import { spawn } from "child_process";
120292
121633
  import { tmpdir as tmpdir3 } from "os";
120293
- import { join as join17 } from "path";
120294
- import { unlink as unlink4, writeFileSync as writeFileSync6 } from "fs";
121634
+ import { join as join19 } from "path";
121635
+ import { unlink as unlink5, writeFileSync as writeFileSync6 } from "fs";
120295
121636
  class AudioPlayer {
120296
121637
  currentProcess = null;
120297
121638
  playing = false;
120298
121639
  async play(audio, options = {}) {
120299
121640
  const format = options.format ?? "mp3";
120300
- const tempFile = join17(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
121641
+ const tempFile = join19(tmpdir3(), `assistants-audio-${Date.now()}.${format}`);
120301
121642
  writeFileSync6(tempFile, Buffer.from(audio));
120302
121643
  const player = this.resolvePlayer(format);
120303
121644
  if (!player) {
@@ -120309,13 +121650,13 @@ class AudioPlayer {
120309
121650
  this.currentProcess.on("close", () => {
120310
121651
  this.playing = false;
120311
121652
  this.currentProcess = null;
120312
- unlink4(tempFile, () => {});
121653
+ unlink5(tempFile, () => {});
120313
121654
  resolve5();
120314
121655
  });
120315
121656
  this.currentProcess.on("error", (error2) => {
120316
121657
  this.playing = false;
120317
121658
  this.currentProcess = null;
120318
- unlink4(tempFile, () => {});
121659
+ unlink5(tempFile, () => {});
120319
121660
  reject(error2);
120320
121661
  });
120321
121662
  });
@@ -120365,8 +121706,8 @@ class AudioPlayer {
120365
121706
  // packages/core/src/voice/recorder.ts
120366
121707
  import { spawn as spawn2 } from "child_process";
120367
121708
  import { tmpdir as tmpdir4 } from "os";
120368
- import { join as join18 } from "path";
120369
- import { readFileSync as readFileSync7, unlink as unlink5 } from "fs";
121709
+ import { join as join20 } from "path";
121710
+ import { readFileSync as readFileSync7, unlink as unlink6 } from "fs";
120370
121711
  class AudioRecorder {
120371
121712
  currentProcess = null;
120372
121713
  async record(options = {}) {
@@ -120376,7 +121717,7 @@ class AudioRecorder {
120376
121717
  const duration = options.durationSeconds ?? 5;
120377
121718
  const sampleRate = options.sampleRate ?? 16000;
120378
121719
  const channels = options.channels ?? 1;
120379
- const output = join18(tmpdir4(), `assistants-record-${Date.now()}.wav`);
121720
+ const output = join20(tmpdir4(), `assistants-record-${Date.now()}.wav`);
120380
121721
  const recorder = this.resolveRecorder(sampleRate, channels, duration, output);
120381
121722
  if (!recorder) {
120382
121723
  throw new Error("No supported audio recorder found. Install sox or ffmpeg.");
@@ -120397,7 +121738,7 @@ class AudioRecorder {
120397
121738
  });
120398
121739
  });
120399
121740
  const data = readFileSync7(output);
120400
- unlink5(output, () => {});
121741
+ unlink6(output, () => {});
120401
121742
  return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
120402
121743
  }
120403
121744
  stop() {
@@ -120569,14 +121910,14 @@ class VoiceManager {
120569
121910
  // packages/core/src/identity/assistant-manager.ts
120570
121911
  init_src();
120571
121912
  import { existsSync as existsSync12 } from "fs";
120572
- import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile7, rm as rm2 } from "fs/promises";
120573
- import { join as join20 } from "path";
121913
+ import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile9, rm as rm2 } from "fs/promises";
121914
+ import { join as join22 } from "path";
120574
121915
 
120575
121916
  // packages/core/src/identity/identity-manager.ts
120576
121917
  init_src();
120577
121918
  import { existsSync as existsSync11 } from "fs";
120578
- import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile6, rm } from "fs/promises";
120579
- import { join as join19 } from "path";
121919
+ import { mkdir as mkdir6, readFile as readFile8, writeFile as writeFile8, rm } from "fs/promises";
121920
+ import { join as join21 } from "path";
120580
121921
  var DEFAULT_PROFILE = {
120581
121922
  displayName: "Assistant",
120582
121923
  timezone: "UTC",
@@ -120606,22 +121947,22 @@ class IdentityManager {
120606
121947
  this.basePath = basePath;
120607
121948
  }
120608
121949
  get identitiesRoot() {
120609
- return join19(this.basePath, "assistants", this.assistantId, "identities");
121950
+ return join21(this.basePath, "assistants", this.assistantId, "identities");
120610
121951
  }
120611
121952
  get indexPath() {
120612
- return join19(this.identitiesRoot, "index.json");
121953
+ return join21(this.identitiesRoot, "index.json");
120613
121954
  }
120614
121955
  get activePath() {
120615
- return join19(this.identitiesRoot, "active.json");
121956
+ return join21(this.identitiesRoot, "active.json");
120616
121957
  }
120617
121958
  identityPath(id) {
120618
- return join19(this.identitiesRoot, `${id}.json`);
121959
+ return join21(this.identitiesRoot, `${id}.json`);
120619
121960
  }
120620
121961
  assistantConfigPath() {
120621
- return join19(this.basePath, "assistants", this.assistantId, "config.json");
121962
+ return join21(this.basePath, "assistants", this.assistantId, "config.json");
120622
121963
  }
120623
121964
  async initialize() {
120624
- await mkdir4(this.identitiesRoot, { recursive: true });
121965
+ await mkdir6(this.identitiesRoot, { recursive: true });
120625
121966
  const index = await this.readIndex();
120626
121967
  for (const id of index.identities) {
120627
121968
  const identity = await this.readIdentity(id);
@@ -120728,7 +122069,7 @@ class IdentityManager {
120728
122069
  return { identities: [] };
120729
122070
  }
120730
122071
  try {
120731
- const raw = await readFile7(this.indexPath, "utf-8");
122072
+ const raw = await readFile8(this.indexPath, "utf-8");
120732
122073
  const data = JSON.parse(raw);
120733
122074
  return { identities: Array.isArray(data.identities) ? data.identities : [] };
120734
122075
  } catch {
@@ -120740,33 +122081,33 @@ class IdentityManager {
120740
122081
  if (!index.identities.includes(id)) {
120741
122082
  index.identities.push(id);
120742
122083
  }
120743
- await writeFile6(this.indexPath, JSON.stringify(index, null, 2));
122084
+ await writeFile8(this.indexPath, JSON.stringify(index, null, 2));
120744
122085
  }
120745
122086
  async removeFromIndex(id) {
120746
122087
  const index = await this.readIndex();
120747
122088
  index.identities = index.identities.filter((identityId) => identityId !== id);
120748
- await writeFile6(this.indexPath, JSON.stringify(index, null, 2));
122089
+ await writeFile8(this.indexPath, JSON.stringify(index, null, 2));
120749
122090
  }
120750
122091
  async readIdentity(id) {
120751
122092
  const path2 = this.identityPath(id);
120752
122093
  if (!existsSync11(path2))
120753
122094
  return null;
120754
122095
  try {
120755
- const raw = await readFile7(path2, "utf-8");
122096
+ const raw = await readFile8(path2, "utf-8");
120756
122097
  return JSON.parse(raw);
120757
122098
  } catch {
120758
122099
  return null;
120759
122100
  }
120760
122101
  }
120761
122102
  async persistIdentity(identity) {
120762
- await mkdir4(this.identitiesRoot, { recursive: true });
120763
- await writeFile6(this.identityPath(identity.id), JSON.stringify(identity, null, 2));
122103
+ await mkdir6(this.identitiesRoot, { recursive: true });
122104
+ await writeFile8(this.identityPath(identity.id), JSON.stringify(identity, null, 2));
120764
122105
  }
120765
122106
  async readActive() {
120766
122107
  if (!existsSync11(this.activePath))
120767
122108
  return null;
120768
122109
  try {
120769
- const raw = await readFile7(this.activePath, "utf-8");
122110
+ const raw = await readFile8(this.activePath, "utf-8");
120770
122111
  const data = JSON.parse(raw);
120771
122112
  return data.id || null;
120772
122113
  } catch {
@@ -120775,13 +122116,13 @@ class IdentityManager {
120775
122116
  }
120776
122117
  async setActive(id) {
120777
122118
  this.activeId = id;
120778
- await writeFile6(this.activePath, JSON.stringify({ id }, null, 2));
122119
+ await writeFile8(this.activePath, JSON.stringify({ id }, null, 2));
120779
122120
  }
120780
122121
  async loadAssistant() {
120781
122122
  if (!existsSync11(this.assistantConfigPath()))
120782
122123
  return null;
120783
122124
  try {
120784
- const raw = await readFile7(this.assistantConfigPath(), "utf-8");
122125
+ const raw = await readFile8(this.assistantConfigPath(), "utf-8");
120785
122126
  return JSON.parse(raw);
120786
122127
  } catch {
120787
122128
  return null;
@@ -120802,19 +122143,19 @@ class AssistantManager {
120802
122143
  this.basePath = basePath;
120803
122144
  }
120804
122145
  get assistantsRoot() {
120805
- return join20(this.basePath, "assistants");
122146
+ return join22(this.basePath, "assistants");
120806
122147
  }
120807
122148
  get indexPath() {
120808
- return join20(this.assistantsRoot, "index.json");
122149
+ return join22(this.assistantsRoot, "index.json");
120809
122150
  }
120810
122151
  get activePath() {
120811
- return join20(this.basePath, "active.json");
122152
+ return join22(this.basePath, "active.json");
120812
122153
  }
120813
122154
  assistantConfigPath(id) {
120814
- return join20(this.assistantsRoot, id, "config.json");
122155
+ return join22(this.assistantsRoot, id, "config.json");
120815
122156
  }
120816
122157
  async initialize() {
120817
- await mkdir5(this.assistantsRoot, { recursive: true });
122158
+ await mkdir7(this.assistantsRoot, { recursive: true });
120818
122159
  const index = await this.readIndex();
120819
122160
  for (const id of index.assistants) {
120820
122161
  const assistant = await this.readAssistant(id);
@@ -120865,7 +122206,7 @@ class AssistantManager {
120865
122206
  if (!this.assistants.has(id)) {
120866
122207
  throw new Error(`Assistant ${id} not found`);
120867
122208
  }
120868
- await rm2(join20(this.assistantsRoot, id), { recursive: true, force: true });
122209
+ await rm2(join22(this.assistantsRoot, id), { recursive: true, force: true });
120869
122210
  this.assistants.delete(id);
120870
122211
  await this.removeFromIndex(id);
120871
122212
  if (this.activeId === id) {
@@ -120900,7 +122241,7 @@ class AssistantManager {
120900
122241
  return { assistants: [] };
120901
122242
  }
120902
122243
  try {
120903
- const raw = await readFile8(this.indexPath, "utf-8");
122244
+ const raw = await readFile9(this.indexPath, "utf-8");
120904
122245
  const data = JSON.parse(raw);
120905
122246
  return { assistants: Array.isArray(data.assistants) ? data.assistants : [] };
120906
122247
  } catch {
@@ -120912,34 +122253,34 @@ class AssistantManager {
120912
122253
  if (!index.assistants.includes(id)) {
120913
122254
  index.assistants.push(id);
120914
122255
  }
120915
- await writeFile7(this.indexPath, JSON.stringify(index, null, 2));
122256
+ await writeFile9(this.indexPath, JSON.stringify(index, null, 2));
120916
122257
  }
120917
122258
  async removeFromIndex(id) {
120918
122259
  const index = await this.readIndex();
120919
122260
  index.assistants = index.assistants.filter((assistantId) => assistantId !== id);
120920
- await writeFile7(this.indexPath, JSON.stringify(index, null, 2));
122261
+ await writeFile9(this.indexPath, JSON.stringify(index, null, 2));
120921
122262
  }
120922
122263
  async readAssistant(id) {
120923
122264
  const configPath = this.assistantConfigPath(id);
120924
122265
  if (!existsSync12(configPath))
120925
122266
  return null;
120926
122267
  try {
120927
- const raw = await readFile8(configPath, "utf-8");
122268
+ const raw = await readFile9(configPath, "utf-8");
120928
122269
  return JSON.parse(raw);
120929
122270
  } catch {
120930
122271
  return null;
120931
122272
  }
120932
122273
  }
120933
122274
  async persistAssistant(assistant) {
120934
- const dir = join20(this.assistantsRoot, assistant.id);
120935
- await mkdir5(dir, { recursive: true });
120936
- await writeFile7(this.assistantConfigPath(assistant.id), JSON.stringify(assistant, null, 2));
122275
+ const dir = join22(this.assistantsRoot, assistant.id);
122276
+ await mkdir7(dir, { recursive: true });
122277
+ await writeFile9(this.assistantConfigPath(assistant.id), JSON.stringify(assistant, null, 2));
120937
122278
  }
120938
122279
  async readActive() {
120939
122280
  if (!existsSync12(this.activePath))
120940
122281
  return null;
120941
122282
  try {
120942
- const raw = await readFile8(this.activePath, "utf-8");
122283
+ const raw = await readFile9(this.activePath, "utf-8");
120943
122284
  const data = JSON.parse(raw);
120944
122285
  return data.id || null;
120945
122286
  } catch {
@@ -120948,11 +122289,11 @@ class AssistantManager {
120948
122289
  }
120949
122290
  async setActive(id) {
120950
122291
  this.activeId = id;
120951
- await writeFile7(this.activePath, JSON.stringify({ id }, null, 2));
122292
+ await writeFile9(this.activePath, JSON.stringify({ id }, null, 2));
120952
122293
  }
120953
122294
  }
120954
122295
  // packages/core/src/inbox/inbox-manager.ts
120955
- import { join as join23 } from "path";
122296
+ import { join as join25 } from "path";
120956
122297
 
120957
122298
  // node_modules/.pnpm/@aws-sdk+middleware-expect-continue@3.972.3/node_modules/@aws-sdk/middleware-expect-continue/dist-es/index.js
120958
122299
  var import_protocol_http = __toESM(require_dist_cjs2(), 1);
@@ -129810,8 +131151,8 @@ class S3InboxClient {
129810
131151
  }
129811
131152
 
129812
131153
  // packages/core/src/inbox/storage/local-cache.ts
129813
- import { join as join22 } from "path";
129814
- import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile9, rm as rm3, readdir as readdir3, stat } from "fs/promises";
131154
+ import { join as join24 } from "path";
131155
+ import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile11, rm as rm3, readdir as readdir4, stat as stat2 } from "fs/promises";
129815
131156
 
129816
131157
  class LocalInboxCache {
129817
131158
  agentId;
@@ -129821,18 +131162,18 @@ class LocalInboxCache {
129821
131162
  constructor(options) {
129822
131163
  this.agentId = options.agentId;
129823
131164
  this.basePath = options.basePath;
129824
- this.cacheDir = join22(this.basePath, this.agentId);
131165
+ this.cacheDir = join24(this.basePath, this.agentId);
129825
131166
  }
129826
131167
  async ensureDirectories() {
129827
- await mkdir6(join22(this.cacheDir, "emails"), { recursive: true });
129828
- await mkdir6(join22(this.cacheDir, "attachments"), { recursive: true });
131168
+ await mkdir8(join24(this.cacheDir, "emails"), { recursive: true });
131169
+ await mkdir8(join24(this.cacheDir, "attachments"), { recursive: true });
129829
131170
  }
129830
131171
  async loadIndex() {
129831
131172
  if (this.index)
129832
131173
  return this.index;
129833
131174
  try {
129834
- const indexPath = join22(this.cacheDir, "index.json");
129835
- const content = await readFile10(indexPath, "utf-8");
131175
+ const indexPath = join24(this.cacheDir, "index.json");
131176
+ const content = await readFile11(indexPath, "utf-8");
129836
131177
  this.index = JSON.parse(content);
129837
131178
  return this.index;
129838
131179
  } catch {
@@ -129844,13 +131185,13 @@ class LocalInboxCache {
129844
131185
  if (!this.index)
129845
131186
  return;
129846
131187
  await this.ensureDirectories();
129847
- const indexPath = join22(this.cacheDir, "index.json");
129848
- await writeFile9(indexPath, JSON.stringify(this.index, null, 2));
131188
+ const indexPath = join24(this.cacheDir, "index.json");
131189
+ await writeFile11(indexPath, JSON.stringify(this.index, null, 2));
129849
131190
  }
129850
131191
  async saveEmail(email) {
129851
131192
  await this.ensureDirectories();
129852
- const emailPath = join22(this.cacheDir, "emails", `${email.id}.json`);
129853
- await writeFile9(emailPath, JSON.stringify(email, null, 2));
131193
+ const emailPath = join24(this.cacheDir, "emails", `${email.id}.json`);
131194
+ await writeFile11(emailPath, JSON.stringify(email, null, 2));
129854
131195
  const index = await this.loadIndex();
129855
131196
  const existingIdx = index.emails.findIndex((e3) => e3.id === email.id);
129856
131197
  const entry = {
@@ -129873,8 +131214,8 @@ class LocalInboxCache {
129873
131214
  }
129874
131215
  async loadEmail(id) {
129875
131216
  try {
129876
- const emailPath = join22(this.cacheDir, "emails", `${id}.json`);
129877
- const content = await readFile10(emailPath, "utf-8");
131217
+ const emailPath = join24(this.cacheDir, "emails", `${id}.json`);
131218
+ const content = await readFile11(emailPath, "utf-8");
129878
131219
  return JSON.parse(content);
129879
131220
  } catch {
129880
131221
  return null;
@@ -129925,16 +131266,16 @@ class LocalInboxCache {
129925
131266
  return new Set(index.emails.map((e3) => e3.id));
129926
131267
  }
129927
131268
  async saveAttachment(emailId, filename, content) {
129928
- const attachmentDir = join22(this.cacheDir, "attachments", emailId);
129929
- await mkdir6(attachmentDir, { recursive: true });
129930
- const attachmentPath = join22(attachmentDir, filename);
129931
- await writeFile9(attachmentPath, content);
131269
+ const attachmentDir = join24(this.cacheDir, "attachments", emailId);
131270
+ await mkdir8(attachmentDir, { recursive: true });
131271
+ const attachmentPath = join24(attachmentDir, filename);
131272
+ await writeFile11(attachmentPath, content);
129932
131273
  return attachmentPath;
129933
131274
  }
129934
131275
  async getAttachmentPath(emailId, filename) {
129935
131276
  try {
129936
- const attachmentPath = join22(this.cacheDir, "attachments", emailId, filename);
129937
- await stat(attachmentPath);
131277
+ const attachmentPath = join24(this.cacheDir, "attachments", emailId, filename);
131278
+ await stat2(attachmentPath);
129938
131279
  return attachmentPath;
129939
131280
  } catch {
129940
131281
  return null;
@@ -129962,10 +131303,10 @@ class LocalInboxCache {
129962
131303
  for (const id of removed) {
129963
131304
  index.emails = index.emails.filter((e3) => e3.id !== id);
129964
131305
  try {
129965
- await rm3(join22(this.cacheDir, "emails", `${id}.json`));
131306
+ await rm3(join24(this.cacheDir, "emails", `${id}.json`));
129966
131307
  } catch {}
129967
131308
  try {
129968
- await rm3(join22(this.cacheDir, "attachments", id), { recursive: true });
131309
+ await rm3(join24(this.cacheDir, "attachments", id), { recursive: true });
129969
131310
  } catch {}
129970
131311
  }
129971
131312
  if (removed.length > 0) {
@@ -129976,20 +131317,20 @@ class LocalInboxCache {
129976
131317
  async getCacheSize() {
129977
131318
  let totalSize = 0;
129978
131319
  try {
129979
- const emailsDir = join22(this.cacheDir, "emails");
129980
- const files = await readdir3(emailsDir);
131320
+ const emailsDir = join24(this.cacheDir, "emails");
131321
+ const files = await readdir4(emailsDir);
129981
131322
  for (const file of files) {
129982
- const fileStat = await stat(join22(emailsDir, file));
131323
+ const fileStat = await stat2(join24(emailsDir, file));
129983
131324
  totalSize += fileStat.size;
129984
131325
  }
129985
131326
  } catch {}
129986
131327
  try {
129987
- const attachmentsDir = join22(this.cacheDir, "attachments");
129988
- const dirs = await readdir3(attachmentsDir);
131328
+ const attachmentsDir = join24(this.cacheDir, "attachments");
131329
+ const dirs = await readdir4(attachmentsDir);
129989
131330
  for (const dir of dirs) {
129990
- const files = await readdir3(join22(attachmentsDir, dir));
131331
+ const files = await readdir4(join24(attachmentsDir, dir));
129991
131332
  for (const file of files) {
129992
- const fileStat = await stat(join22(attachmentsDir, dir, file));
131333
+ const fileStat = await stat2(join24(attachmentsDir, dir, file));
129993
131334
  totalSize += fileStat.size;
129994
131335
  }
129995
131336
  }
@@ -132576,7 +133917,7 @@ class InboxManager {
132576
133917
  }
132577
133918
  }
132578
133919
  function createInboxManager(agentId, agentName, config, configDir) {
132579
- const basePath = join23(configDir, "inbox");
133920
+ const basePath = join25(configDir, "inbox");
132580
133921
  return new InboxManager({
132581
133922
  agentId,
132582
133923
  agentName,
@@ -134741,10 +136082,12 @@ class AgentLoop {
134741
136082
  identityManager = null;
134742
136083
  inboxManager = null;
134743
136084
  walletManager = null;
136085
+ jobManager = null;
134744
136086
  identityContext = null;
134745
136087
  projectContext = null;
134746
136088
  activeProjectId = null;
134747
136089
  assistantId = null;
136090
+ askUserHandler = null;
134748
136091
  onChunk;
134749
136092
  onToolStart;
134750
136093
  onToolEnd;
@@ -134823,6 +136166,9 @@ class AgentLoop {
134823
136166
  FilesystemTools.registerAll(this.toolRegistry, this.sessionId);
134824
136167
  WebTools.registerAll(this.toolRegistry);
134825
136168
  ImageTools.registerAll(this.toolRegistry);
136169
+ this.toolRegistry.register(SkillTool.tool, SkillTool.executor);
136170
+ const askUserTool = createAskUserTool(() => this.askUserHandler);
136171
+ this.toolRegistry.register(askUserTool.tool, askUserTool.executor);
134826
136172
  this.toolRegistry.register(FeedbackTool.tool, FeedbackTool.executor);
134827
136173
  this.toolRegistry.register(SchedulerTool.tool, SchedulerTool.executor);
134828
136174
  if (this.config?.inbox?.enabled) {
@@ -134838,6 +136184,22 @@ class AgentLoop {
134838
136184
  this.walletManager = createWalletManager(agentId, this.config.wallet);
134839
136185
  registerWalletTools(this.toolRegistry, () => this.walletManager);
134840
136186
  }
136187
+ if (this.config?.jobs?.enabled !== false) {
136188
+ this.jobManager = new JobManager(this.config?.jobs || {}, this.sessionId);
136189
+ this.jobManager.onJobComplete((event) => {
136190
+ const statusEmoji = event.status === "completed" ? "\u2713" : event.status === "failed" ? "\u2717" : "\u26A0";
136191
+ const message = `
136192
+ [Job ${event.status}] ${event.connector} (${event.jobId}): ${event.summary}
136193
+ `;
136194
+ this.emit({ type: "text", content: message });
136195
+ });
136196
+ const jobTools = createJobTools(() => this.jobManager);
136197
+ for (const { tool, executor } of jobTools) {
136198
+ this.toolRegistry.register(tool, executor);
136199
+ }
136200
+ this.connectorBridge.setJobManagerGetter(() => this.jobManager);
136201
+ this.jobManager.cleanup().catch(() => {});
136202
+ }
134841
136203
  this.connectorBridge.registerAll(this.toolRegistry);
134842
136204
  this.builtinCommands.registerAll(this.commandLoader);
134843
136205
  this.hookLoader.load(hooksConfig);
@@ -135311,6 +136673,9 @@ class AgentLoop {
135311
136673
  this.identityContext = await this.identityManager.buildSystemPromptContext();
135312
136674
  }
135313
136675
  },
136676
+ refreshSkills: async () => {
136677
+ await this.skillLoader.loadAll(this.cwd);
136678
+ },
135314
136679
  switchAssistant: async (assistantId) => {
135315
136680
  await this.switchAssistant(assistantId);
135316
136681
  },
@@ -135530,6 +136895,9 @@ ${content.trim()}`);
135530
136895
  }
135531
136896
  this.contextManager?.refreshState(this.context.getMessages());
135532
136897
  }
136898
+ setAskUserHandler(handler) {
136899
+ this.askUserHandler = handler;
136900
+ }
135533
136901
  clearConversation() {
135534
136902
  this.resetContext();
135535
136903
  }
@@ -135541,7 +136909,7 @@ ${content.trim()}`);
135541
136909
  const heartbeatConfig = this.buildHeartbeatConfig(this.config);
135542
136910
  if (!heartbeatConfig)
135543
136911
  return;
135544
- const statePath = join24(getConfigDir(), "state", `${this.sessionId}.json`);
136912
+ const statePath = join26(getConfigDir(), "state", `${this.sessionId}.json`);
135545
136913
  this.heartbeatManager = new HeartbeatManager(heartbeatConfig);
135546
136914
  this.heartbeatPersistence = new StatePersistence(statePath);
135547
136915
  this.heartbeatRecovery = new RecoveryManager(this.heartbeatPersistence, heartbeatConfig.persistPath, heartbeatConfig.staleThresholdMs, {
@@ -135590,7 +136958,7 @@ ${content.trim()}`);
135590
136958
  async startEnergySystem() {
135591
136959
  if (!this.config || this.config.energy?.enabled === false)
135592
136960
  return;
135593
- const statePath = join24(getConfigDir(), "energy", "state.json");
136961
+ const statePath = join26(getConfigDir(), "energy", "state.json");
135594
136962
  this.energyManager = new EnergyManager(this.config.energy, new EnergyStorage(statePath));
135595
136963
  await this.energyManager.initialize();
135596
136964
  this.refreshEnergyEffects();
@@ -135841,7 +137209,7 @@ ${this.identityContext}`);
135841
137209
  return null;
135842
137210
  const intervalMs = Math.max(1000, config.heartbeat?.intervalMs ?? 15000);
135843
137211
  const staleThresholdMs = Math.max(intervalMs * 2, config.heartbeat?.staleThresholdMs ?? 120000);
135844
- const persistPath = config.heartbeat?.persistPath ?? join24(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
137212
+ const persistPath = config.heartbeat?.persistPath ?? join26(getConfigDir(), "heartbeats", `${this.sessionId}.json`);
135845
137213
  return {
135846
137214
  intervalMs,
135847
137215
  staleThresholdMs,
@@ -135909,12 +137277,19 @@ ${this.identityContext}`);
135909
137277
  const allowed = this.getEffectiveAllowedTools();
135910
137278
  if (!allowed)
135911
137279
  return tools;
135912
- return tools.filter((tool) => allowed.has(tool.name.toLowerCase()));
137280
+ return tools.filter((tool) => {
137281
+ const name2 = tool.name.toLowerCase();
137282
+ if (name2 === "ask_user")
137283
+ return true;
137284
+ return allowed.has(name2);
137285
+ });
135913
137286
  }
135914
137287
  isToolAllowed(name2) {
135915
137288
  const allowed = this.getEffectiveAllowedTools();
135916
137289
  if (!allowed)
135917
137290
  return true;
137291
+ if (name2.toLowerCase() === "ask_user")
137292
+ return true;
135918
137293
  return allowed.has(name2.toLowerCase());
135919
137294
  }
135920
137295
  }
@@ -135938,17 +137313,17 @@ init_src();
135938
137313
 
135939
137314
  // packages/core/src/logger.ts
135940
137315
  import { existsSync as existsSync13, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
135941
- import { join as join25 } from "path";
137316
+ import { join as join27 } from "path";
135942
137317
  class Logger {
135943
137318
  logDir;
135944
137319
  logFile;
135945
137320
  sessionId;
135946
137321
  constructor(sessionId, basePath) {
135947
137322
  this.sessionId = sessionId;
135948
- this.logDir = join25(basePath || getConfigDir(), "logs");
137323
+ this.logDir = join27(basePath || getConfigDir(), "logs");
135949
137324
  this.ensureDir(this.logDir);
135950
137325
  const date = new Date().toISOString().split("T")[0];
135951
- this.logFile = join25(this.logDir, `${date}.log`);
137326
+ this.logFile = join27(this.logDir, `${date}.log`);
135952
137327
  }
135953
137328
  ensureDir(dir) {
135954
137329
  if (!existsSync13(dir)) {
@@ -135992,9 +137367,9 @@ class SessionStorage {
135992
137367
  constructor(sessionId, basePath, assistantId) {
135993
137368
  this.sessionId = sessionId;
135994
137369
  const root = basePath || getConfigDir();
135995
- this.sessionsDir = assistantId ? join25(root, "assistants", assistantId, "sessions") : join25(root, "sessions");
137370
+ this.sessionsDir = assistantId ? join27(root, "assistants", assistantId, "sessions") : join27(root, "sessions");
135996
137371
  this.ensureDir(this.sessionsDir);
135997
- this.sessionFile = join25(this.sessionsDir, `${sessionId}.json`);
137372
+ this.sessionFile = join27(this.sessionsDir, `${sessionId}.json`);
135998
137373
  }
135999
137374
  ensureDir(dir) {
136000
137375
  if (!existsSync13(dir)) {
@@ -136020,7 +137395,7 @@ class SessionStorage {
136020
137395
  }
136021
137396
  static getActiveAssistantId() {
136022
137397
  try {
136023
- const activePath = join25(getConfigDir(), "active.json");
137398
+ const activePath = join27(getConfigDir(), "active.json");
136024
137399
  if (!existsSync13(activePath))
136025
137400
  return null;
136026
137401
  const raw = readFileSync9(activePath, "utf-8");
@@ -136034,12 +137409,12 @@ class SessionStorage {
136034
137409
  const root = getConfigDir();
136035
137410
  const resolvedId = assistantId ?? SessionStorage.getActiveAssistantId();
136036
137411
  if (resolvedId) {
136037
- const assistantDir = join25(root, "assistants", resolvedId, "sessions");
137412
+ const assistantDir = join27(root, "assistants", resolvedId, "sessions");
136038
137413
  if (existsSync13(assistantDir)) {
136039
137414
  return assistantDir;
136040
137415
  }
136041
137416
  }
136042
- return join25(root, "sessions");
137417
+ return join27(root, "sessions");
136043
137418
  }
136044
137419
  static listSessions(assistantId) {
136045
137420
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
@@ -136051,8 +137426,8 @@ class SessionStorage {
136051
137426
  if (!file.endsWith(".json"))
136052
137427
  continue;
136053
137428
  try {
136054
- const filePath = join25(sessionsDir, file);
136055
- const stat2 = Bun.file(filePath);
137429
+ const filePath = join27(sessionsDir, file);
137430
+ const stat3 = Bun.file(filePath);
136056
137431
  const content = JSON.parse(readFileSync9(filePath, "utf-8"));
136057
137432
  sessions.push({
136058
137433
  id: file.replace(".json", ""),
@@ -136071,7 +137446,7 @@ class SessionStorage {
136071
137446
  }
136072
137447
  static loadSession(sessionId, assistantId) {
136073
137448
  const sessionsDir = SessionStorage.resolveSessionsDir(assistantId);
136074
- const sessionFile = join25(sessionsDir, `${sessionId}.json`);
137449
+ const sessionFile = join27(sessionsDir, `${sessionId}.json`);
136075
137450
  try {
136076
137451
  if (!existsSync13(sessionFile))
136077
137452
  return null;
@@ -136085,15 +137460,15 @@ function initAssistantsDir() {
136085
137460
  const baseDir = getConfigDir();
136086
137461
  const dirs = [
136087
137462
  baseDir,
136088
- join25(baseDir, "logs"),
136089
- join25(baseDir, "assistants"),
136090
- join25(baseDir, "shared", "skills"),
136091
- join25(baseDir, "commands"),
136092
- join25(baseDir, "temp"),
136093
- join25(baseDir, "heartbeats"),
136094
- join25(baseDir, "state"),
136095
- join25(baseDir, "energy"),
136096
- join25(baseDir, "migration")
137463
+ join27(baseDir, "logs"),
137464
+ join27(baseDir, "assistants"),
137465
+ join27(baseDir, "shared", "skills"),
137466
+ join27(baseDir, "commands"),
137467
+ join27(baseDir, "temp"),
137468
+ join27(baseDir, "heartbeats"),
137469
+ join27(baseDir, "state"),
137470
+ join27(baseDir, "energy"),
137471
+ join27(baseDir, "migration")
136097
137472
  ];
136098
137473
  for (const dir of dirs) {
136099
137474
  if (!existsSync13(dir)) {
@@ -136255,6 +137630,11 @@ class EmbeddedClient {
136255
137630
  onError(callback) {
136256
137631
  this.errorCallbacks.push(callback);
136257
137632
  }
137633
+ setAskUserHandler(handler) {
137634
+ if (typeof this.agent.setAskUserHandler === "function") {
137635
+ this.agent.setAskUserHandler(handler);
137636
+ }
137637
+ }
136258
137638
  async getTools() {
136259
137639
  if (!this.initialized) {
136260
137640
  await this.initialize();
@@ -136610,6 +137990,7 @@ var COMMANDS = [
136610
137990
  { name: "/cost", description: "show estimated API cost" },
136611
137991
  { name: "/model", description: "show model information" },
136612
137992
  { name: "/skills", description: "list available skills" },
137993
+ { name: "/skill", description: "create or manage skills" },
136613
137994
  { name: "/config", description: "show configuration" },
136614
137995
  { name: "/projects", description: "manage projects in this folder" },
136615
137996
  { name: "/plans", description: "manage project plans" },
@@ -136626,7 +138007,15 @@ var COMMANDS = [
136626
138007
  { name: "/resume", description: "resume a scheduled command" },
136627
138008
  { name: "/exit", description: "exit assistants" }
136628
138009
  ];
136629
- function Input({ onSubmit, isProcessing, queueLength = 0, commands: commands3, skills = [] }) {
138010
+ function Input({
138011
+ onSubmit,
138012
+ isProcessing,
138013
+ queueLength = 0,
138014
+ commands: commands3,
138015
+ skills = [],
138016
+ isAskingUser = false,
138017
+ askPlaceholder
138018
+ }) {
136630
138019
  const [value, setValue] = import_react30.useState("");
136631
138020
  const [selectedIndex, setSelectedIndex] = import_react30.useState(0);
136632
138021
  const allCommands = import_react30.useMemo(() => {
@@ -136641,6 +138030,8 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands: commands3, s
136641
138030
  return merged.sort((a5, b7) => a5.name.localeCompare(b7.name));
136642
138031
  }, [commands3]);
136643
138032
  const autocompleteMode = import_react30.useMemo(() => {
138033
+ if (isAskingUser)
138034
+ return null;
136644
138035
  if (value.startsWith("$") && !value.includes(" ")) {
136645
138036
  return "skill";
136646
138037
  }
@@ -136670,6 +138061,9 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands: commands3, s
136670
138061
  setSelectedIndex((prev) => Math.min(prev, autocompleteItems.length - 1));
136671
138062
  }, [autocompleteItems.length]);
136672
138063
  use_input_default((input, key) => {
138064
+ if (isAskingUser) {
138065
+ return;
138066
+ }
136673
138067
  if (key.tab) {
136674
138068
  if (autocompleteItems.length > 0) {
136675
138069
  const selected = autocompleteItems[selectedIndex] || autocompleteItems[0];
@@ -136735,7 +138129,9 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands: commands3, s
136735
138129
  setSelectedIndex(0);
136736
138130
  };
136737
138131
  let placeholder = "Type a message...";
136738
- if (isProcessing) {
138132
+ if (isAskingUser) {
138133
+ placeholder = askPlaceholder || "Answer the question...";
138134
+ } else if (isProcessing) {
136739
138135
  placeholder = queueLength > 0 ? "Type to queue (Tab) or interrupt (Shift+Enter)..." : "Type to interrupt (Shift+Enter)...";
136740
138136
  }
136741
138137
  const truncateDescription = (desc, maxLen = 60) => {
@@ -136807,7 +138203,7 @@ function Input({ onSubmit, isProcessing, queueLength = 0, commands: commands3, s
136807
138203
  children: "-".repeat(terminalWidth)
136808
138204
  }, undefined, false, undefined, this)
136809
138205
  }, undefined, false, undefined, this),
136810
- isProcessing && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
138206
+ isProcessing && !isAskingUser && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
136811
138207
  marginLeft: 2,
136812
138208
  children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
136813
138209
  dimColor: true,
@@ -138322,6 +139718,8 @@ function getToolDisplayName(toolCall) {
138322
139718
  return "schedule";
138323
139719
  case "submit_feedback":
138324
139720
  return "feedback";
139721
+ case "ask_user":
139722
+ return "ask";
138325
139723
  case "notion":
138326
139724
  case "gmail":
138327
139725
  case "googledrive":
@@ -138356,6 +139754,12 @@ function formatToolCall(toolCall) {
138356
139754
  return formatScheduleCall(input);
138357
139755
  case "submit_feedback":
138358
139756
  return formatFeedbackCall(input);
139757
+ case "ask_user": {
139758
+ const title = String(input.title || "");
139759
+ const question = Array.isArray(input.questions) && input.questions[0]?.question ? String(input.questions[0].question) : "";
139760
+ const label = title || question || "asking user";
139761
+ return `Asking: ${truncate(label, 60)}`;
139762
+ }
138359
139763
  case "notion":
138360
139764
  return `Notion: ${truncate(String(input.command || input.action || ""), 60)}`;
138361
139765
  case "gmail":
@@ -139085,8 +140489,87 @@ function QueueIndicator({
139085
140489
  }, undefined, true, undefined, this);
139086
140490
  }
139087
140491
 
139088
- // packages/terminal/src/components/App.tsx
140492
+ // packages/terminal/src/components/AskUserPanel.tsx
139089
140493
  var jsx_dev_runtime11 = __toESM(require_jsx_dev_runtime(), 1);
140494
+ function AskUserPanel({
140495
+ sessionId,
140496
+ request: request2,
140497
+ question,
140498
+ index,
140499
+ total
140500
+ }) {
140501
+ return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140502
+ flexDirection: "column",
140503
+ borderStyle: "round",
140504
+ borderColor: "cyan",
140505
+ paddingX: 1,
140506
+ marginY: 1,
140507
+ children: [
140508
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140509
+ justifyContent: "space-between",
140510
+ children: [
140511
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140512
+ color: "cyan",
140513
+ bold: true,
140514
+ children: request2.title || "Question"
140515
+ }, undefined, false, undefined, this),
140516
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140517
+ dimColor: true,
140518
+ children: [
140519
+ index + 1,
140520
+ "/",
140521
+ total
140522
+ ]
140523
+ }, undefined, true, undefined, this)
140524
+ ]
140525
+ }, undefined, true, undefined, this),
140526
+ request2.description && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140527
+ marginTop: 1,
140528
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140529
+ dimColor: true,
140530
+ children: request2.description
140531
+ }, undefined, false, undefined, this)
140532
+ }, undefined, false, undefined, this),
140533
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140534
+ marginTop: 1,
140535
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140536
+ children: question.question
140537
+ }, undefined, false, undefined, this)
140538
+ }, undefined, false, undefined, this),
140539
+ question.options && question.options.length > 0 && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140540
+ flexDirection: "column",
140541
+ marginTop: 1,
140542
+ children: question.options.map((opt, idx) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140543
+ dimColor: true,
140544
+ children: [
140545
+ "\u2022 ",
140546
+ opt
140547
+ ]
140548
+ }, `${opt}-${idx}`, true, undefined, this))
140549
+ }, undefined, false, undefined, this),
140550
+ question.multiline && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140551
+ marginTop: 1,
140552
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140553
+ dimColor: true,
140554
+ children: "Multi-line answer allowed. Paste text and press Enter."
140555
+ }, undefined, false, undefined, this)
140556
+ }, undefined, false, undefined, this),
140557
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
140558
+ marginTop: 1,
140559
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
140560
+ dimColor: true,
140561
+ children: [
140562
+ "Session: ",
140563
+ sessionId
140564
+ ]
140565
+ }, undefined, true, undefined, this)
140566
+ }, undefined, false, undefined, this)
140567
+ ]
140568
+ }, undefined, true, undefined, this);
140569
+ }
140570
+
140571
+ // packages/terminal/src/components/App.tsx
140572
+ var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
139090
140573
  var SHOW_ERROR_CODES = process.env.ASSISTANTS_DEBUG === "1";
139091
140574
  function formatElapsedDuration(ms2) {
139092
140575
  const totalSeconds = Math.max(0, Math.floor(ms2 / 1000));
@@ -139122,6 +140605,7 @@ function App2({ cwd: cwd2, version: version2 }) {
139122
140605
  const [voiceState, setVoiceState] = import_react36.useState();
139123
140606
  const [identityInfo, setIdentityInfo] = import_react36.useState();
139124
140607
  const [verboseTools, setVerboseTools] = import_react36.useState(false);
140608
+ const [askUserState, setAskUserState] = import_react36.useState(null);
139125
140609
  const [processingStartTime, setProcessingStartTime] = import_react36.useState();
139126
140610
  const [currentTurnTokens, setCurrentTurnTokens] = import_react36.useState(0);
139127
140611
  const [scrollOffset, setScrollOffset] = import_react36.useState(0);
@@ -139137,12 +140621,60 @@ function App2({ cwd: cwd2, version: version2 }) {
139137
140621
  const isProcessingRef = import_react36.useRef(isProcessing);
139138
140622
  const processingStartTimeRef = import_react36.useRef(processingStartTime);
139139
140623
  const pendingSendsRef = import_react36.useRef([]);
140624
+ const askUserStateRef = import_react36.useRef(null);
139140
140625
  const updateScrollMetrics = import_react36.useCallback((offset) => {
139141
140626
  const currentOffset = typeof offset === "number" ? offset : scrollRef.current?.getScrollOffset() ?? 0;
139142
140627
  const bottomOffset = scrollRef.current?.getBottomOffset() ?? 0;
139143
140628
  setScrollOffset(currentOffset);
139144
140629
  setAutoScroll(currentOffset >= Math.max(0, bottomOffset - 1));
139145
140630
  }, []);
140631
+ const beginAskUser = import_react36.useCallback((sessionId, request2) => {
140632
+ return new Promise((resolve5, reject) => {
140633
+ if (askUserStateRef.current) {
140634
+ reject(new Error("Another interview is already in progress."));
140635
+ return;
140636
+ }
140637
+ const state = {
140638
+ sessionId,
140639
+ request: request2,
140640
+ index: 0,
140641
+ answers: {},
140642
+ resolve: resolve5,
140643
+ reject
140644
+ };
140645
+ askUserStateRef.current = state;
140646
+ setAskUserState(state);
140647
+ });
140648
+ }, []);
140649
+ const cancelAskUser = import_react36.useCallback((reason) => {
140650
+ const current = askUserStateRef.current;
140651
+ if (!current)
140652
+ return;
140653
+ askUserStateRef.current = null;
140654
+ setAskUserState(null);
140655
+ current.reject(new Error(reason));
140656
+ }, []);
140657
+ const submitAskAnswer = import_react36.useCallback((answer) => {
140658
+ setAskUserState((prev) => {
140659
+ if (!prev)
140660
+ return prev;
140661
+ const question = prev.request.questions[prev.index];
140662
+ const answers = { ...prev.answers, [question.id]: answer };
140663
+ const nextIndex = prev.index + 1;
140664
+ if (nextIndex >= prev.request.questions.length) {
140665
+ askUserStateRef.current = null;
140666
+ prev.resolve({ answers });
140667
+ return null;
140668
+ }
140669
+ const nextState = {
140670
+ ...prev,
140671
+ index: nextIndex,
140672
+ answers
140673
+ };
140674
+ askUserStateRef.current = nextState;
140675
+ return nextState;
140676
+ });
140677
+ }, []);
139146
140678
  import_react36.useEffect(() => {
139147
140679
  if (!stdout)
139148
140680
  return;
@@ -139463,6 +140995,7 @@ function App2({ cwd: cwd2, version: version2 }) {
139463
140995
  });
139464
140996
  const session = await registry.createSession(cwd2);
139465
140997
  setActiveSessionId(session.id);
140998
+ session.client.setAskUserHandler((request2) => beginAskUser(session.id, request2));
139466
140999
  await loadSessionMetadata(session);
139467
141000
  setEnergyState(session.client.getEnergyState() ?? undefined);
139468
141001
  setVoiceState(session.client.getVoiceState() ?? undefined);
@@ -139477,7 +141010,7 @@ function App2({ cwd: cwd2, version: version2 }) {
139477
141010
  return () => {
139478
141011
  registry.closeAll();
139479
141012
  };
139480
- }, [cwd2, registry, handleChunk, finalizeResponse, resetTurnState, loadSessionMetadata]);
141013
+ }, [cwd2, registry, handleChunk, finalizeResponse, resetTurnState, loadSessionMetadata, beginAskUser]);
139481
141014
  const processQueue = import_react36.useCallback(async () => {
139482
141015
  const activeSession2 = registryRef.current.getActiveSession();
139483
141016
  if (!activeSession2 || !activeSessionId)
@@ -139536,6 +141069,8 @@ function App2({ cwd: cwd2, version: version2 }) {
139536
141069
  const backgroundProcessingCount = registry.getBackgroundProcessingSessions().length;
139537
141070
  const MAX_QUEUED_PREVIEW = 3;
139538
141071
  const inlineCount = activeInline.length;
141072
+ const activeAskQuestion = askUserState?.request.questions[askUserState.index];
141073
+ const askPlaceholder = activeAskQuestion?.placeholder || activeAskQuestion?.question || "Answer the question...";
139539
141074
  const showWelcome = messages.length === 0 && !isProcessing;
139540
141075
  const renderWidth = columns ? Math.max(1, columns - 2) : undefined;
139541
141076
  const wrapChars = renderWidth ?? MESSAGE_WRAP_CHARS;
@@ -139588,6 +141123,7 @@ function App2({ cwd: cwd2, version: version2 }) {
139588
141123
  try {
139589
141124
  saveCurrentSessionState();
139590
141125
  const newSession = await registry.createSession(cwd2);
141126
+ newSession.client.setAskUserHandler((request2) => beginAskUser(newSession.id, request2));
139591
141127
  await registry.switchSession(newSession.id);
139592
141128
  setActiveSessionId(newSession.id);
139593
141129
  loadSessionState(newSession.id);
@@ -139600,7 +141136,7 @@ function App2({ cwd: cwd2, version: version2 }) {
139600
141136
  } catch (err) {
139601
141137
  setError(err instanceof Error ? err.message : "Failed to create session");
139602
141138
  }
139603
- }, [cwd2, registry, saveCurrentSessionState, loadSessionState]);
141139
+ }, [cwd2, registry, saveCurrentSessionState, loadSessionState, beginAskUser]);
139604
141140
  const getScrollStep = import_react36.useCallback(() => {
139605
141141
  const viewport = scrollRef.current?.getViewportHeight();
139606
141142
  const fallback = Math.max(3, rows - 6);
@@ -139615,6 +141151,9 @@ function App2({ cwd: cwd2, version: version2 }) {
139615
141151
  return;
139616
141152
  }
139617
141153
  if (key.ctrl && input === "c") {
141154
+ if (askUserStateRef.current) {
141155
+ cancelAskUser("Cancelled by user");
141156
+ }
139618
141157
  if (isProcessing && activeSession) {
139619
141158
  activeSession.client.stop();
139620
141159
  const finalized = finalizeResponse("stopped");
@@ -139635,6 +141174,9 @@ function App2({ cwd: cwd2, version: version2 }) {
139635
141174
  return;
139636
141175
  }
139637
141176
  if (key.escape) {
141177
+ if (askUserStateRef.current) {
141178
+ cancelAskUser("Cancelled by user");
141179
+ }
139638
141180
  if (isProcessing && activeSession) {
139639
141181
  activeSession.client.stop();
139640
141182
  const finalized = finalizeResponse("stopped");
@@ -139672,6 +141214,10 @@ function App2({ cwd: cwd2, version: version2 }) {
139672
141214
  setAutoScroll(true);
139673
141215
  }, [activeSessionId]);
139674
141216
  const handleSubmit = import_react36.useCallback(async (input, mode = "normal") => {
141217
+ if (askUserStateRef.current) {
141218
+ submitAskAnswer(input.trim());
141219
+ return;
141220
+ }
139675
141221
  if (!activeSession || !input.trim())
139676
141222
  return;
139677
141223
  const trimmedInput = input.trim();
@@ -139793,24 +141339,25 @@ function App2({ cwd: cwd2, version: version2 }) {
139793
141339
  handleSessionSwitch,
139794
141340
  finalizeResponse,
139795
141341
  resetTurnState,
139796
- activeSessionId
141342
+ activeSessionId,
141343
+ submitAskAnswer
139797
141344
  ]);
139798
141345
  if (isInitializing) {
139799
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
141346
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
139800
141347
  flexDirection: "column",
139801
141348
  padding: 1,
139802
141349
  height: rows,
139803
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Spinner2, {
141350
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Spinner2, {
139804
141351
  label: "Initializing..."
139805
141352
  }, undefined, false, undefined, this)
139806
141353
  }, undefined, false, undefined, this);
139807
141354
  }
139808
141355
  if (showSessionSelector) {
139809
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
141356
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
139810
141357
  flexDirection: "column",
139811
141358
  padding: 1,
139812
141359
  height: rows,
139813
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(SessionSelector, {
141360
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(SessionSelector, {
139814
141361
  sessions,
139815
141362
  activeSessionId,
139816
141363
  onSelect: handleSessionSwitch,
@@ -139824,19 +141371,19 @@ function App2({ cwd: cwd2, version: version2 }) {
139824
141371
  return { toolCall: e6.toolCall, result };
139825
141372
  });
139826
141373
  const isThinking = isProcessing && !currentResponse && !currentToolCall && toolCallEntries.length === 0;
139827
- return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
141374
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
139828
141375
  flexDirection: "column",
139829
141376
  padding: 1,
139830
141377
  height: rows,
139831
141378
  children: [
139832
- showWelcome && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(WelcomeBanner, {
141379
+ showWelcome && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(WelcomeBanner, {
139833
141380
  version: version2 ?? "unknown",
139834
141381
  model: activeSession?.client.getModel() ?? "unknown",
139835
141382
  directory: activeSession?.cwd || cwd2
139836
141383
  }, undefined, false, undefined, this),
139837
- backgroundProcessingCount > 0 && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
141384
+ backgroundProcessingCount > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
139838
141385
  marginBottom: 1,
139839
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141386
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
139840
141387
  color: "yellow",
139841
141388
  children: [
139842
141389
  backgroundProcessingCount,
@@ -139846,8 +141393,8 @@ function App2({ cwd: cwd2, version: version2 }) {
139846
141393
  ]
139847
141394
  }, undefined, true, undefined, this)
139848
141395
  }, undefined, false, undefined, this),
139849
- scrollOffset > 0 && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
139850
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
141396
+ scrollOffset > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
141397
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text, {
139851
141398
  dimColor: true,
139852
141399
  children: [
139853
141400
  "\u2191 ",
@@ -139856,16 +141403,16 @@ function App2({ cwd: cwd2, version: version2 }) {
139856
141403
  ]
139857
141404
  }, undefined, true, undefined, this)
139858
141405
  }, undefined, false, undefined, this),
139859
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
141406
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
139860
141407
  flexGrow: 1,
139861
141408
  flexShrink: 1,
139862
141409
  minHeight: 1,
139863
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ScrollView, {
141410
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ScrollView, {
139864
141411
  ref: scrollRef,
139865
141412
  onScroll: (offset) => updateScrollMetrics(offset),
139866
141413
  onContentHeightChange: () => updateScrollMetrics(),
139867
141414
  onViewportSizeChange: () => updateScrollMetrics(),
139868
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Messages3, {
141415
+ children: /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Messages3, {
139869
141416
  messages: displayMessages,
139870
141417
  currentResponse: undefined,
139871
141418
  streamingMessages,
@@ -139877,28 +141424,37 @@ function App2({ cwd: cwd2, version: version2 }) {
139877
141424
  }, activeSessionId || "default", false, undefined, this)
139878
141425
  }, undefined, false, undefined, this)
139879
141426
  }, undefined, false, undefined, this),
139880
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(QueueIndicator, {
141427
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(QueueIndicator, {
139881
141428
  messages: [...activeInline, ...activeQueue],
139882
141429
  maxPreview: MAX_QUEUED_PREVIEW
139883
141430
  }, undefined, false, undefined, this),
139884
- error2 && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ErrorBanner, {
141431
+ askUserState && activeAskQuestion && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(AskUserPanel, {
141432
+ sessionId: askUserState.sessionId,
141433
+ request: askUserState.request,
141434
+ question: activeAskQuestion,
141435
+ index: askUserState.index,
141436
+ total: askUserState.request.questions.length
141437
+ }, undefined, false, undefined, this),
141438
+ error2 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ErrorBanner, {
139885
141439
  error: error2,
139886
141440
  showErrorCodes: SHOW_ERROR_CODES
139887
141441
  }, undefined, false, undefined, this),
139888
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(ProcessingIndicator, {
141442
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(ProcessingIndicator, {
139889
141443
  isProcessing,
139890
141444
  startTime: processingStartTime,
139891
141445
  tokenCount: currentTurnTokens,
139892
141446
  isThinking
139893
141447
  }, undefined, false, undefined, this),
139894
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Input, {
141448
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Input, {
139895
141449
  onSubmit: handleSubmit,
139896
141450
  isProcessing,
139897
141451
  queueLength: activeQueue.length + inlineCount,
139898
141452
  commands: commands3,
139899
- skills
141453
+ skills,
141454
+ isAskingUser: Boolean(activeAskQuestion),
141455
+ askPlaceholder
139900
141456
  }, undefined, false, undefined, this),
139901
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Status, {
141457
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Status, {
139902
141458
  isProcessing,
139903
141459
  cwd: activeSession?.cwd || cwd2,
139904
141460
  queueLength: activeQueue.length + inlineCount,
@@ -140091,7 +141647,7 @@ function formatStreamEvent(chunk) {
140091
141647
  }
140092
141648
 
140093
141649
  // packages/terminal/src/index.tsx
140094
- var jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
141650
+ var jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
140095
141651
  var VERSION3 = "0.6.37";
140096
141652
  process.env.ASSISTANTS_VERSION ??= VERSION3;
140097
141653
  function parseArgs(argv) {
@@ -140239,7 +141795,7 @@ if (options.print !== null) {
140239
141795
  process.exit(1);
140240
141796
  });
140241
141797
  } else {
140242
- const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime12.jsxDEV(App2, {
141798
+ const { waitUntilExit } = render_default(/* @__PURE__ */ jsx_dev_runtime13.jsxDEV(App2, {
140243
141799
  cwd: options.cwd,
140244
141800
  version: VERSION3
140245
141801
  }, undefined, false, undefined, this));
@@ -140248,4 +141804,4 @@ if (options.print !== null) {
140248
141804
  });
140249
141805
  }
140250
141806
 
140251
- //# debugId=8049D7C06414401364756E2164756E21
141807
+ //# debugId=26B340BF6D9D424564756E2164756E21