cli-jaw 2.17.26 → 2.17.27

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.
@@ -1,6 +1,8 @@
1
- import { loadHeartbeatFile, saveHeartbeatFile, isHeartbeatDestination, isHeartbeatMentionWatch } from '../core/config.js';
1
+ import { loadHeartbeatFile, saveHeartbeatFile, isHeartbeatDestination, isHeartbeatMentionWatch, settings } from '../core/config.js';
2
2
  import { startHeartbeat } from '../memory/heartbeat.js';
3
3
  import { validateHeartbeatScheduleInput } from '../memory/heartbeat-schedule.js';
4
+ import { approveLegacyFreshStart, quarantineState, detectLegacyMentionWatch } from '../memory/legacy-mention-watch-quarantine.js';
5
+ import { verifiedSlackWorkspace } from '../slack/verified-workspace.js';
4
6
  import { getEmployees } from '../core/db.js';
5
7
  import { stripUndefined } from '../core/strip-undefined.js';
6
8
  /** Resolve the destination a PUT should persist.
@@ -81,6 +83,7 @@ export function registerHeartbeatRoutes(app, requireAuth) {
81
83
  const existingById = new Map(loadHeartbeatFile().jobs.filter(job => job.id).map(job => [job.id, job]));
82
84
  const employeeNames = new Set(getEmployees.all().map(employee => employee.name));
83
85
  const idPrefix = `hb_${Date.now()}`;
86
+ const seenIds = new Set();
84
87
  for (const [index, rawJob] of data.jobs.entries()) {
85
88
  const job = (rawJob && typeof rawJob === 'object') ? rawJob : {};
86
89
  const scheduleResult = validateHeartbeatScheduleInput(job["schedule"]);
@@ -97,6 +100,15 @@ export function registerHeartbeatRoutes(app, requireAuth) {
97
100
  });
98
101
  return;
99
102
  }
103
+ // Two jobs under one id would share a mention-watch ledger namespace,
104
+ // so the second one inherits the first one's cursor and skips every
105
+ // message below it. Rejected rather than de-duplicated: silently
106
+ // dropping one of them loses a job the operator just asked to save.
107
+ if (seenIds.has(jobId)) {
108
+ res.status(400).json({ error: 'duplicate heartbeat job id', index, jobId });
109
+ return;
110
+ }
111
+ seenIds.add(jobId);
100
112
  const existing = existingById.get(jobId);
101
113
  const runnerResult = normalizeHeartbeatPutRunnerFields(job, existing, employeeNames);
102
114
  if (!runnerResult.ok) {
@@ -129,5 +141,146 @@ export function registerHeartbeatRoutes(app, requireAuth) {
129
141
  startHeartbeat();
130
142
  res.json(payload);
131
143
  });
144
+ // Read the hold, so an operator can tell a held job from a broken one. The
145
+ // scheduler refuses a quarantined job on every tick and says why in the log,
146
+ // but a log line is not something the dashboard can show.
147
+ //
148
+ // Detection runs here rather than being assumed from startup. `startHeartbeat`
149
+ // is called after the server is already listening, so a request arriving in
150
+ // that window would find v1 rows with no marker yet and be told the job is
151
+ // fine. It is INSERT OR IGNORE, so calling it again is free.
152
+ app.get('/api/heartbeat/:jobId/mention-watch-hold', requireAuth, (req, res) => {
153
+ const jobId = String(req.params['jobId'] ?? '').trim();
154
+ if (!jobId) {
155
+ res.status(400).json({ error: 'jobId required' });
156
+ return;
157
+ }
158
+ detectLegacyMentionWatch(Date.now());
159
+ const state = quarantineState(jobId);
160
+ res.json({ jobId, held: state?.status === 'pending', state });
161
+ });
162
+ // Release a held job with a NEW floor.
163
+ //
164
+ // A plain `enabled: true` PUT is deliberately not accepted as the approval:
165
+ // every shipped UI rebuilds a job from a fixed field set and omits
166
+ // `mentionWatch` entirely, so reading a save click as consent would let any
167
+ // unrelated edit lift the hold and replay the backlog.
168
+ //
169
+ // The order below is the crash-safety argument and is not interchangeable.
170
+ // `since` lives in heartbeat.json and the hold lives in SQLite, so the two
171
+ // cannot share a transaction. Writing the file first means a crash before the
172
+ // database step leaves the new floor saved and the job still held, which a
173
+ // retry fixes. The reverse lifts the hold while the OLD floor is still live.
174
+ app.post('/api/heartbeat/:jobId/mention-watch-fresh-start', requireAuth, async (req, res) => {
175
+ const jobId = String(req.params['jobId'] ?? '').trim();
176
+ if (!jobId) {
177
+ res.status(400).json({ error: 'jobId required' });
178
+ return;
179
+ }
180
+ const since = String(req.body?.['since'] ?? '').trim();
181
+ // An empty floor is what makes this dangerous: the watch would walk
182
+ // backward through all reachable history and answer it again.
183
+ if (!since) {
184
+ res.status(400).json({ error: 'since required to restart a held mention watch', jobId });
185
+ return;
186
+ }
187
+ // The ONE await comes first, before anything is read.
188
+ //
189
+ // Verification needs the network, and every decision after it reads state
190
+ // that a concurrent request can change. Snapshotting the hold and the file
191
+ // and THEN awaiting is what lets a losing approval resume with a stale
192
+ // whole-file copy and write its floor over the winner's — the DB tells it
193
+ // 'conflict' only after the file damage is already done. Doing the await
194
+ // up front leaves everything below it synchronous, so Node's single thread
195
+ // is the mutual exclusion.
196
+ //
197
+ // The workspace belongs in the record because the ledger is keyed by it: an
198
+ // approval granted while the token pointed at workspace A is otherwise
199
+ // indistinguishable from one granted under B, and the v2 rows the job goes
200
+ // on to write land in a namespace the operator was not looking at. Failing
201
+ // verification is a refusal, not a default.
202
+ const readBotToken = () => String(settings['slack']?.['botToken'] ?? '').trim();
203
+ const token = readBotToken();
204
+ const verified = token ? await verifiedSlackWorkspace(token) : null;
205
+ if (!verified) {
206
+ res.status(409).json({
207
+ error: 'cannot verify the Slack workspace for this token; approving would record a guessed namespace',
208
+ jobId,
209
+ });
210
+ return;
211
+ }
212
+ // A settings write during that await can have swapped the token. The
213
+ // verified team id then describes the OLD credential while the job the
214
+ // approval is about to release runs under the new one — the recorded
215
+ // provenance and the namespace actually written would disagree, which is
216
+ // the exact confusion recording the workspace exists to prevent.
217
+ if (readBotToken() !== token) {
218
+ res.status(409).json({
219
+ error: 'the Slack bot token changed while verifying; retry so the approval records the workspace it will actually run under',
220
+ jobId,
221
+ });
222
+ return;
223
+ }
224
+ // Everything from here down runs without yielding.
225
+ //
226
+ // Same startup-window reason as the GET: without this, a legacy job whose
227
+ // marker has not been written yet reads as 'never held' and 404s.
228
+ detectLegacyMentionWatch(Date.now());
229
+ const state = quarantineState(jobId);
230
+ if (!state) {
231
+ res.status(404).json({ error: 'no mention-watch hold for this job', jobId });
232
+ return;
233
+ }
234
+ const file = loadHeartbeatFile();
235
+ const job = file.jobs.find(candidate => candidate.id === jobId);
236
+ if (!job?.mentionWatch) {
237
+ res.status(400).json({ error: 'job has no mention watch to restart', jobId });
238
+ return;
239
+ }
240
+ const resolution = JSON.stringify({ since, userId: job.mentionWatch.userId, workspaceId: verified.teamId });
241
+ // Already resolved with the same decision is the caller retrying after a
242
+ // lost response; a different one is a second, conflicting decision.
243
+ if (state.status === 'resolved') {
244
+ const outcome = approveLegacyFreshStart(jobId, resolution, Date.now());
245
+ if (outcome === 'conflict') {
246
+ res.status(409).json({ error: 'this hold was already cleared with a different floor', jobId, state });
247
+ return;
248
+ }
249
+ // The recorded decision matching is not enough. The floor lives in the
250
+ // file, and an edit after the approval can have moved it — reporting
251
+ // this floor as in force while another one governs the scan is the one
252
+ // wrong answer available here. Rewriting silently is worse: it would
253
+ // undo a later deliberate change.
254
+ if ((job.mentionWatch.since ?? '') !== since) {
255
+ res.status(409).json({
256
+ error: 'the persisted floor no longer matches this approval',
257
+ jobId,
258
+ approvedSince: since,
259
+ persistedSince: job.mentionWatch.since ?? null,
260
+ });
261
+ return;
262
+ }
263
+ res.json({ jobId, outcome, since });
264
+ return;
265
+ }
266
+ const updated = file.jobs.map(candidate => (candidate.id === jobId
267
+ ? { ...candidate, mentionWatch: { ...job.mentionWatch, since } }
268
+ : candidate));
269
+ // Step 1: the file, and only past this line is the new floor durable.
270
+ saveHeartbeatFile({ jobs: updated });
271
+ // Step 2: claim the hold, then archive and delete the v1 rows, as one unit.
272
+ const outcome = approveLegacyFreshStart(jobId, resolution, Date.now());
273
+ if (outcome === 'conflict') {
274
+ res.status(409).json({ error: 'this hold was already cleared with a different floor', jobId, state });
275
+ return;
276
+ }
277
+ if (outcome === 'not-pending') {
278
+ res.status(409).json({ error: 'the hold changed while restarting; retry', jobId });
279
+ return;
280
+ }
281
+ // Step 3: only now can the job be scheduled again.
282
+ startHeartbeat();
283
+ res.json({ jobId, outcome, since });
284
+ });
132
285
  }
133
286
  //# sourceMappingURL=heartbeat.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../../src/routes/heartbeat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../../src/routes/heartbeat.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ import { abortableDelay } from '../messaging/outbound-lifecycle.js';
7
7
  import { chunkSlackMessage, toMrkdwn } from './format.js';
8
8
  import { MAX_INLINE_RATE_LIMIT_MS, classifySendFailure, retryAfterMs } from '../messaging/retry.js';
9
9
  import { redactOutboundPayload } from '../messaging/redact.js';
10
+ import { log } from '../core/logger.js';
10
11
  export function invalidateSlackSendClient() {
11
12
  // no-op: the token is read fresh from settings on every call.
12
13
  // Present so runtime-settings.ts can invalidate symmetrically with Discord.
@@ -107,6 +108,25 @@ export async function sendSlackText(token, target, text, options = {}) {
107
108
  return slackFailure(describeSlackError(result.error, result.data), result.status, result.retryAfterMs, result.grantedScopes);
108
109
  }
109
110
  }
111
+ // Recorded HERE, not only at `sendChannelOutput`, because most Slack posts
112
+ // never pass through that choke point: the dispatch settle path, the queued
113
+ // reply, the recovered-queue forwarder, and the generic forwarder all call
114
+ // this transport directly. Instrumenting only the choke point left those
115
+ // invisible — which is precisely the set a 'did one turn answer twice' audit
116
+ // needs, so a census over the other record could look complete while missing
117
+ // them.
118
+ //
119
+ // `chunks` matters: one logical answer over the Slack length limit becomes
120
+ // several posts, and counting posts instead of sends would read a long answer
121
+ // as a duplicate.
122
+ //
123
+ // No body, same reason as elsewhere: destination and shape, never the words.
124
+ log.event('slack.post', {
125
+ target: target.targetId,
126
+ ...(target.threadId ? { threaded: true } : {}),
127
+ chunks: chunks.length,
128
+ result: 'ok',
129
+ });
110
130
  // exactOptionalPropertyTypes: omit the key rather than sending undefined.
111
131
  return firstTs ? { ok: true, ts: firstTs } : { ok: true };
112
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"send-only-client.js","sourceRoot":"","sources":["../../../src/slack/send-only-client.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,+EAA+E;AAC/E,iFAAiF;AAEjF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,YAAY,EAAmB,MAAM,UAAU,CAAC;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAM/D,MAAM,UAAU,yBAAyB;IACrC,8DAA8D;IAC9D,4EAA4E;AAChF,CAAC;AAED,MAAM,UAAU,kBAAkB;IAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,EAAE,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,yBAAyB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACnF,OAAO,EAAE,KAAK,EAAE,CAAC;AACrB,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACvC,KAAa,EACb,MAAc,EACd,SAAsB;IAEtB,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAClF,MAAM,MAAM,GAAG,MAAM,QAAQ,CACzB,KAAK,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CACjF,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QAC3B,uDAAuD;QACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,CAAC,MAAM,CAAC,KAAK,IAAI,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IAC9G,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAC/B,KAAa,EACb,MAAoB,EACpB,IAAY,EACZ,UAA8E,EAAE;IAEhF,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,4EAA4E;IAC5E,0EAA0E;IAC1E,8CAA8C;IAC9C,IAAI,OAA2B,CAAC;IAChC,yEAAyE;IACzE,6EAA6E;IAC7E,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,MAAM,QAAQ,GAAG;QACb,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxD,CAAC;IACF,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,kEAAkE;QAClE,oEAAoE;QACpE,sEAAsE;QACtE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC1B,OAAO,YAAY,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CACzB,KAAK,EACL,kBAAkB,EAClB;YACI,OAAO,EAAE,MAAM,CAAC,QAAQ;YACxB,IAAI,EAAE,KAAK;YACX,qEAAqE;YACrE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/D,EACD,QAAQ,CACX,CAAC;QACF,IAAI,MAAM,CAAC,EAAE,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE;YAAE,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACb,mEAAmE;YACnE,8DAA8D;YAC9D,qEAAqE;YACrE,IAAI,MAAM,CAAC,KAAK,KAAK,oBAAoB,EAAE,CAAC;gBACxC,OAAO,YAAY,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,UAAU,GAAG,mBAAmB,CAAC;gBACnC,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,YAAY,EAAE,MAAM,CAAC,YAAY;aACpC,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,IAAI,YAAY,CAAC;gBAC7C,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,YAAY,EAAE,MAAM,CAAC,YAAY;aACpC,CAAC,CAAC;YACH,IAAI,UAAU,KAAK,YAAY,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,wBAAwB,EAAE,CAAC;gBAC9E,8DAA8D;gBAC9D,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC3C,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;oBAC1B,OAAO,YAAY,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;gBACnD,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAC1B,KAAK,EACL,kBAAkB,EAClB;oBACI,OAAO,EAAE,MAAM,CAAC,QAAQ;oBACxB,IAAI,EAAE,KAAK;oBACX,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC1D,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC/D,EACD,QAAQ,CACX,CAAC;gBACF,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;oBACb,6DAA6D;oBAC7D,8DAA8D;oBAC9D,IAAI,KAAK,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE;wBAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/D,SAAS;gBACb,CAAC;gBACD,IAAI,OAAO,CAAC,KAAK,KAAK,oBAAoB,EAAE,CAAC;oBACzC,OAAO,YAAY,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;gBACnD,CAAC;gBACD,OAAO,YAAY,CAAC,kBAAkB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;YACtI,CAAC;YACD,OAAO,YAAY,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACjI,CAAC;IACL,CAAC;IACD,0EAA0E;IAC1E,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"send-only-client.js","sourceRoot":"","sources":["../../../src/slack/send-only-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ // ─── Verified Slack workspace ────────────────────────
2
+ // The mention-watch ledger is keyed by (workspace, user), so a wrong workspace id
3
+ // hands one person cursor to another. That makes the SOURCE of the id part of the
4
+ // correctness argument.
5
+ //
6
+ // `settings.slack.teamId` is not that source. It is written only when empty and
7
+ // never re-checked against the token, so a token pointing at workspace B while the
8
+ // setting still says A yields rows filed under A.
9
+ //
10
+ // `initSlack` is not the entry point either: with no app token it returns before
11
+ // calling `auth.test` at all, and mention watch needs only a bot token. Depending
12
+ // on it would fail-close every outbound-only install that works today.
13
+ //
14
+ // So this asks Slack directly, once per token, and caches the answer against the
15
+ // token that produced it. A token change invalidates the cache by construction.
16
+ import { slackApi } from './api.js';
17
+ let cached = null;
18
+ /** Test seam. Never called in production paths. */
19
+ export function resetVerifiedSlackWorkspace() {
20
+ cached = null;
21
+ }
22
+ /**
23
+ * The team id Slack itself reports for this token, or null when it cannot be
24
+ * established.
25
+ *
26
+ * Null is a refusal, not a default: a caller that keys durable state on this must
27
+ * skip the work rather than guess. One failed lookup skips one tick, which is
28
+ * recoverable; a guessed key writes into someone else ledger, which is not.
29
+ */
30
+ export async function verifiedSlackWorkspace(token, opts = {}) {
31
+ const trimmed = token.trim();
32
+ if (!trimmed)
33
+ return null;
34
+ if (cached && cached.token === trimmed) {
35
+ return { teamId: cached.teamId, userId: cached.userId };
36
+ }
37
+ const auth = await slackApi(trimmed, 'auth.test', {}, opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {});
38
+ const teamId = String(auth.data?.team_id ?? '').trim();
39
+ if (!auth.ok || !teamId)
40
+ return null;
41
+ const userId = String(auth.data?.user_id ?? '').trim() || null;
42
+ cached = { token: trimmed, teamId, userId };
43
+ return { teamId, userId };
44
+ }
45
+ //# sourceMappingURL=verified-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verified-workspace.js","sourceRoot":"","sources":["../../../src/slack/verified-workspace.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,kFAAkF;AAClF,kFAAkF;AAClF,wBAAwB;AACxB,EAAE;AACF,gFAAgF;AAChF,mFAAmF;AACnF,kDAAkD;AAClD,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,uEAAuE;AACvE,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAEhF,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAKpC,IAAI,MAAM,GAAoB,IAAI,CAAC;AAEnC,mDAAmD;AACnD,MAAM,UAAU,2BAA2B;IACvC,MAAM,GAAG,IAAI,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CACxC,KAAa,EACb,OAA+C,EAAE;IAEjD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;QACrC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC5D,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CACvB,OAAO,EACP,WAAW,EACX,EAAE,EACF,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CACtD,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;IAC/D,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-jaw",
3
- "version": "2.17.26",
3
+ "version": "2.17.27",
4
4
  "description": "Personal AI assistant powered by Pi, Antigravity, AI-E, Claude, Claude E, Codex, Codex App, Cursor, Grok, Kiro, OpenCode, and Copilot — Web, Terminal, Telegram, and Discord interfaces with 107 built-in skills",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -0,0 +1,55 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/vendor-render-kIxHrxDV.css"])))=>i.map(i=>d[i]);
2
+ import{n as e,r as t}from"./vendor-render-BpGO1zp3.js";import{t as n}from"./state-DxpgDKMO.js";import{a as r,i,n as a,r as o,t as s}from"./api-tVoG39dL.js";import{a as c,c as l,d as u,f as ee,i as te,l as ne,r as re,s as ie,t as ae,u as oe}from"./sidebar-FbI4udzs.js";import{t as d}from"./preload-helper-DFYRRz9w.js";import{A as se,C as ce,D as le,E as ue,F as de,I as fe,L as pe,M as f,O as p,R as m,S as me,b as he,f as ge,h as _e,l as ve,m as h,n as ye,p as be,u as xe,v as Se,y as Ce}from"./render-Cd4frZ2U.js";import{_ as we,d as Te,f as g,n as Ee,u as De}from"./iframe-renderer-LZp7Nqm1.js";import{O as Oe,T as ke,_,t as Ae,v}from"./message-history-Bp_sUDSx.js";import{n as je,s as y}from"./idb-cache-lOntwU3S.js";import{c as Me,f as Ne,o as Pe}from"./ui-Cd8etFwN.js";import{t as Fe}from"./locale-DC-ZsWOf.js";import{a as Ie,c as Le,i as Re,n as ze,o as Be,r as Ve,s as He,t as Ue}from"./slash-commands-FoQuZXga.js";import{i as We,o as Ge}from"./constants-DK-21dMG.js";import{A as Ke,C as qe,D as Je,E as Ye,F as Xe,I as Ze,L as Qe,M as $e,N as et,O as tt,P as nt,R as rt,S as b,T as it,a as at,c as ot,d as x,g as st,h as ct,i as lt,j as ut,k as dt,m as ft,o as pt,r as mt,s as ht,t as gt,u as S,v as _t,w as C,x as vt,y as yt,z as bt}from"./settings-core-C5qo8giT.js";import{t as xt}from"./provider-icons-CXA2mnPO.js";import{a as St,i as Ct,t as wt}from"./skills-DeG9lWF2.js";import{a as Tt,c as Et,d as Dt,f as Ot,g as kt,h as At,i as jt,l as Mt,m as Nt,n as Pt,o as Ft,p as It,r as Lt,s as Rt,t as w,u as zt}from"./settings-a0blK1fr.js";import{a as Bt,c as T,d as Vt,f as Ht,i as Ut,l as Wt,m as Gt,n as Kt,o as qt,p as Jt,r as Yt,s as Xt,t as Zt,u as E}from"./memory-V5caYbpS.js";import{n as Qt}from"./bgtask-badge-CT6s6rND.js";var $t=new Set([`.png`,`.jpg`,`.jpeg`,`.gif`,`.webp`,`.svg`]),en=new Set([`.mp4`,`.webm`,`.mov`,`.ogg`]);function tn(e){let t=String(e||``).split(/[\\/]/).pop()||``,n=t.lastIndexOf(`.`),r=n>0?t.slice(n).toLowerCase():``;return $t.has(r)?`image`:en.has(r)?`video`:`file`}var D=[],nn=8e3;function rn(e){return/^\/compact(?:\s|$)/i.test(String(e||``).trim())?300*1e3:1e4}var an={image:`chat.file.sentImage`,video:`chat.file.sentVideo`,file:`chat.file.sent`};function O(e){return m(an[tn(e)],{path:e})}function on(e,t=``){let n=e.map(O).join(`
3
+ `);return t?`${n}${m(`chat.file.sentWithMsg`,{text:t})}`:n}function sn(e,t){let n=t.map(O).join(`
4
+ `);return n?`${e}\n\n${n}`:e}function cn(e){let t=me();return t?new Promise(n=>{let r=`preview-send-${Date.now()}-${Math.random().toString(36).slice(2)}`,i=!1,a=()=>{window.removeEventListener(`message`,s),window.clearTimeout(c)},o=e=>{i||(i=!0,a(),n(e))},s=e=>{if(e.source!==window.parent||!he(e.origin))return;let t=e.data;!t||t.type!==`jaw-preview-send-result`||t.requestId!==r||o({ok:!!t.ok,status:Number.isInteger(t.status)?t.status:t.ok?200:502,data:t.data||(t.error?{error:t.error}:{}),viaRelay:!0})},c=window.setTimeout(()=>o(null),nn);window.addEventListener(`message`,s);try{window.parent.postMessage(h({type:`jaw-preview-send-message`,requestId:r,prompt:e}),t)}catch{o(null)}}):Promise.resolve(null)}async function ln(e){let t=await cn(e);if(t?.ok)return t;try{let t=await fetch(`${s}/api/message`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(h({prompt:e}))}),n=await t.json().catch(()=>({}));return{ok:t.ok,status:t.status,data:n}}catch(e){return{ok:!1,status:0,data:{error:e.message}}}}async function un(e){let t,n,i=rn(e);if(typeof AbortSignal?.timeout==`function`)t=AbortSignal.timeout(i);else{let e=new AbortController;t=e.signal,n=setTimeout(()=>e.abort(),i)}let a=Fe(),o=await r(),c=await fetch(`${s}/api/command`,{method:`POST`,headers:{"Content-Type":`application/json`,"Accept-Language":a,...o?{Authorization:`Bearer ${o}`}:{}},body:JSON.stringify(h({text:e,locale:a})),signal:t});n&&clearTimeout(n);let l=await c.json().catch(()=>({}));return{ok:c.ok,status:c.status,result:l}}async function dn(e,t,n){let r=t.result;if(r?.code===`not_command`){if(n){await n();return}_(`user`,e),y({role:`user`,content:e,timestamp:Date.now()}),await i(`/api/message`,`POST`,h({prompt:e}));return}if(!t.ok&&!r?.text)throw Error(`HTTP ${t.status}`);if(r?.code===`clear_screen`){ye(),we().clear();let e=document.getElementById(`chatMessages`);e&&(e.innerHTML=``)}(r?.text||r?.recovery)&&v(fn(r),``,r.type),r?.steerPrompt&&await i(`/api/message`,`POST`,h({prompt:r.steerPrompt}))}function fn(e){let t=e.recovery;if(!t?.originalText)return e.text?f(e.text):``;let n=(t.suggestedCommands||[]).slice(0,5),r=n.length?`<div class="cmd-recovery-suggestions">${n.map(e=>`<code>${f(e)}</code>`).join(` `)}</div>`:``,i=f(t.originalText);return`${e.text?`<div>${f(e.text)}</div>`:``}
5
+ <div class="cmd-recovery" data-cmd-recovery>
6
+ <div class="cmd-recovery-label">${f(m(`cmd.recovery.originalPrompt`))}</div>
7
+ <pre class="cmd-recovery-text">${i}</pre>
8
+ ${r}
9
+ <div class="cmd-recovery-actions">
10
+ <button type="button" class="cmd-recovery-btn" data-cmd-recovery-action="reinsert" data-cmd-text="${i}">${f(m(`cmd.artifact.action.reinsert`))}</button>
11
+ <button type="button" class="cmd-recovery-btn" data-cmd-recovery-action="copy" data-cmd-text="${i}">${f(m(`cmd.artifact.action.copy`))}</button>
12
+ </div>
13
+ </div>`}document.addEventListener(`click`,async e=>{let t=e.target?.closest(`[data-cmd-recovery-action]`);if(!t)return;let n=t.dataset.cmdRecoveryAction||``,r=t.dataset.cmdText||``;if(r){if(n===`reinsert`){let e=document.getElementById(`chatInput`);if(!e)return;e.value=r,e.focus(),e.selectionStart=e.selectionEnd=e.value.length,e.dispatchEvent(new Event(`input`,{bubbles:!0}));return}n===`copy`&&await le(r)}});var k=!1;async function A(e=`enter`){let t=document.getElementById(`chatInput`),r=document.getElementById(`btnSend`);if(!t||!r)return;let a=t.value.trim();if(!ve(e===`button`&&r.classList.contains(`stop-mode`)?``:a)){be();return}let s=e===`button`;if(r.classList.contains(`stop-mode`)&&s){o(`/api/stop`,`POST`,h({}));return}if(k||!a&&!n.attachedFiles.length)return;ne(),k=!0;let c=r,u=c.disabled;c.disabled=!0;try{await ft();let e=a.slice(1).trim().split(/\s+/)[0]||``,r=e.includes(`/`)||e.includes(`\\`),o=a.startsWith(`/`)&&!r;if(o&&!n.attachedFiles.length){if(t.value=``,M(),Ue(),He(a))return;try{await dn(a,await un(a))}catch(e){v(m(`chat.cmd.fail`,{msg:e.message}),``,`error`)}finally{l(`command`).catch(()=>{})}return}if(n.attachedFiles.length){let e=`📎 [${n.attachedFiles.map(e=>e.name).join(`, `)}] ${a}`;_(`user`,e),y({role:`user`,content:e,timestamp:Date.now()}),t.value=``,M();try{let e=await Promise.all(n.attachedFiles.map(e=>mn(e))),t=on(e,a);if(j(),o){Ue();try{let n=sn(a,e);await dn(n,await un(n),async()=>{await i(`/api/message`,`POST`,h({prompt:t}))})}catch(e){v(m(`chat.cmd.fail`,{msg:e.message}),``,`error`)}finally{l(`command`).catch(()=>{})}return}await i(`/api/message`,`POST`,h({prompt:t}))}catch(e){v(m(`chat.file.uploadFail`,{msg:e.message})),j()}}else{t.value=``,M();let e=await ln(a),n=e.data;if(e.status===409&&n.error===`duplicate`)return;if(!e.ok){v(`${p.error} ${f(n.error||m(`chat.requestFail`,{status:e.status}))}`,``,`error`);return}if(n.queued){let{updateQueueBadge:e}=await d(async()=>{let{updateQueueBadge:e}=await import(`./ui-yrn3s72A.js`);return{updateQueueBadge:e}},__vite__mapDeps([0]));e(n.pending||1)}else e.viaRelay?(y({role:`user`,content:a,timestamp:Date.now()}),n.continued&&v(m(`chat.continue`)),g(!0)):n.noPendingContinue?(_(`user`,a),y({role:`user`,content:a,timestamp:Date.now()}),g(!0)):n.continued?(_(`user`,a),y({role:`user`,content:a,timestamp:Date.now()}),v(m(`chat.continue`)),g(!0)):(_(`user`,a),y({role:`user`,content:a,timestamp:Date.now()}),g(!0))}}finally{k=!1,c.disabled=u}}function pn(e){e.key===`Enter`&&!e.shiftKey&&!e.isComposing&&(e.preventDefault(),A(`enter`))}async function mn(e){let t=await fetch(`${s}/api/upload`,{method:`POST`,headers:{"X-Filename":encodeURIComponent(e.name)},body:e});if(!t.ok)throw Error(`upload failed`);return(await t.json()).path}function hn(e){for(let t of e)n.attachedFiles.some(e=>e.name===t.name)||n.attachedFiles.push(t);_n(),document.getElementById(`chatInput`)?.focus()}function gn(e){n.attachedFiles.splice(e,1),_n()}function j(){D.forEach(e=>URL.revokeObjectURL(e)),D=[],n.attachedFiles=[],_n();let e=document.getElementById(`fileInput`);e&&(e.value=``)}function _n(){let e=document.getElementById(`filePreview`),t=document.getElementById(`filePreviewList`);if(e){if(D.forEach(e=>URL.revokeObjectURL(e)),D=[],!n.attachedFiles.length){e.classList.remove(`visible`),t&&(t.innerHTML=``);return}e.classList.add(`visible`),t&&(t.innerHTML=n.attachedFiles.map((e,t)=>{let n=(e.size/1024).toFixed(1),r=e.type.startsWith(`image/`),i=``;if(r){let t=URL.createObjectURL(e);D.push(t),i=`<img src="${t}" class="file-chip-thumb" alt="">`}return`<div class="file-chip">
14
+ ${i}
15
+ <span class="file-chip-name">${p.paperclip} ${f(e.name)} (${n}KB)</span>
16
+ <button class="file-chip-remove" data-file-idx="${t}" title="Remove">${p.close}</button>
17
+ </div>`}).join(``))}}async function vn(){ye(),we().clear();let e=document.getElementById(`chatMessages`);e&&(e.innerHTML=``);let{cleanupToolActivity:t}=await d(async()=>{let{cleanupToolActivity:e}=await import(`./ui-yrn3s72A.js`);return{cleanupToolActivity:e}},__vite__mapDeps([0]));t(),je().catch(()=>{}),ne()}var yn=0;function bn(e){if(yn)return;let t=De();yn=requestAnimationFrame(()=>{if(yn=0,e.style.height=`auto`,e.style.height=e.scrollHeight+`px`,t){let e=document.getElementById(`chatMessages`);e&&(e.scrollTop=e.scrollHeight,Te())}})}function xn(){let e=document.getElementById(`chatInput`);e&&e.addEventListener(`input`,()=>bn(e))}function M(){let e=document.getElementById(`chatInput`),t=De();if(e&&(e.style.height=`auto`),t){let e=document.getElementById(`chatMessages`);e&&(e.scrollTop=e.scrollHeight,Te())}}function Sn(){let e=document.querySelector(`.chat-area`),t=document.getElementById(`dragOverlay`);if(!e||!t)return;let n=0;e.addEventListener(`dragenter`,e=>{e.preventDefault(),n++,t.classList.add(`visible`)}),e.addEventListener(`dragleave`,e=>{e.preventDefault(),n--,n<=0&&(n=0,t.classList.remove(`visible`))}),e.addEventListener(`dragover`,e=>e.preventDefault()),e.addEventListener(`drop`,e=>{e.preventDefault(),n=0,t.classList.remove(`visible`);let r=[...e.dataTransfer?.files||[]];r.length&&hn(r)}),document.getElementById(`fileInput`)?.addEventListener(`change`,e=>{let t=e.target,n=[...t.files||[]];n.length&&hn(n),t.value=``}),document.addEventListener(`paste`,e=>{let t=e.clipboardData?.items;if(!t)return;let n=[];for(let e of t){if(e.kind!==`file`)continue;let t=e.getAsFile();if(t)if(!t.name||t.name===`image.png`){let e=new Date().toISOString().replace(/[:.]/g,`-`),r=t.type.split(`/`)[1]||`png`,i=new File([t],`pasted-${e}.${r}`,{type:t.type});n.push(i)}else n.push(t)}n.length&&(e.preventDefault(),hn(n))})}async function Cn(e,t,r){if(!ve()){be();return}let a=document.getElementById(`chatInput`),o=a?.value.trim()||``,c=[...n.attachedFiles],l=[m(`chat.voice.label`)];c.length&&l.push(`📎 [${c.map(e=>e.name).join(`, `)}]`),o&&l.push(o),_(`user`,l.join(` `)),y({role:`user`,content:l.join(` `),timestamp:Date.now()}),a&&o&&(a.value=``,M()),c.length&&j();try{let n=await fetch(`${s}/api/voice`,{method:`POST`,headers:{"Content-Type":r,"X-Voice-Ext":t,"X-STT-Only":`true`},body:e});if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error||`HTTP ${n.status}`)}let a=await n.json().catch(()=>null);if(!a?.text)throw Error(`Empty STT result`);v(`${p.mic} STT (${f(a.engine||``)}, ${a.elapsed?.toFixed(1)}s): "${f(a.text.slice(0,100))}"`,``,`info`);let l=[];c.length&&(l=await Promise.all(c.map(e=>mn(e))));let u=[];for(let e of l)u.push(O(e));u.push(`🎤 ${a.text}`),o&&u.push(o),await i(`/api/message`,`POST`,h({prompt:u.join(`
18
+ `)}))}catch(e){v(m(`voice.sttFail`,{msg:e.message}),``,`error`)}}var wn={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6},Tn={jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12};function En(e){let t=typeof e==`string`?e.trim():``;if(t)try{return new Intl.DateTimeFormat(`en-US`,{timeZone:t}).format(new Date),t}catch{return}}function Dn(e){let t=e&&typeof e==`object`?e:{},n=t.kind,r=typeof t.timeZone==`string`?t.timeZone.trim():``,i=En(t.timeZone);if(r&&!i)return{ok:!1,code:`invalid_timezone`,error:`invalid timeZone "${r}"`};if(n===`cron`){let e=typeof t.cron==`string`?t.cron.trim().replace(/\s+/g,` `):``;if(!e)return{ok:!1,code:`invalid_cron`,error:`cron expression required`};let n=On(e);return n?{ok:!1,code:`invalid_cron`,error:n}:{ok:!0,schedule:i?{kind:`cron`,cron:e,timeZone:i}:{kind:`cron`,cron:e}}}if(n==null||n===`every`){let e=typeof t.minutes==`number`?t.minutes:Number(t.minutes);if(!Number.isInteger(e)||e<1)return{ok:!1,code:`invalid_minutes`,error:`minutes must be an integer >= 1`};let n=Math.max(1,Math.floor(e));return{ok:!0,schedule:i?{kind:`every`,minutes:n,timeZone:i}:{kind:`every`,minutes:n}}}return{ok:!1,code:`invalid_kind`,error:`invalid heartbeat schedule kind "${String(n)}"`}}function On(e){try{let[t,n,r,i,a]=kn(e);return N(t,{min:0,max:59}),N(n,{min:0,max:23}),N(r,{min:1,max:31}),N(i,{min:1,max:12,aliases:Tn}),N(a,{min:0,max:7,aliases:wn,normalize:Nn}),null}catch(e){return e.message}}function kn(e){let t=String(e||``).trim().replace(/\s+/g,` `).split(` `);if(t.length!==5)throw Error(`cron must have 5 fields, got ${t.length}`);return t}function N(e,t){for(let n of e.split(`,`))An(n.trim(),t)}function An(e,t){if(!e)throw Error(`empty cron segment`);let n=e;if(e.includes(`/`)){let[t,r,...i]=e.split(`/`);if(!t||!r||i.length>0)throw Error(`invalid cron step segment "${e}"`);n=t,Mn(r,`invalid cron step "${r}"`)}if(n!==`*`){if(n.includes(`-`)){let[e,r,...i]=n.split(`-`);if(!e||!r||i.length>0||jn(e,t)>jn(r,t))throw Error(`invalid cron range "${n}"`);return}jn(n,t)}}function jn(e,t){let n=e.trim().toLowerCase(),r=t.aliases?.[n]??Number(n);if(!Number.isInteger(r))throw Error(`invalid cron value "${e}"`);let i=t.normalize?t.normalize(r):r;if(!Pn(i,t.min,t.max))throw Error(`cron value "${e}" out of range ${t.min}-${t.max}`);return i}function Mn(e,t){let n=Number(e);if(!Number.isInteger(n)||n<=0)throw Error(t);return n}function Nn(e){return e===7?0:e}function Pn(e,t,n){return e>=t&&e<=n}async function Fn(){n.heartbeatJobs=((await a(`/api/heartbeat`))?.jobs||[]).map(I),n.heartbeatErrors=L(n.heartbeatJobs),P(),document.getElementById(`heartbeatModal`)?.classList.add(`open`)}function In(e){e&&e.target!==e.currentTarget||document.getElementById(`heartbeatModal`)?.classList.remove(`open`)}function P(){let e=document.getElementById(`hbJobsList`);if(!e)return;let t=n.heartbeatJobs.map(I);n.heartbeatJobs=t,n.heartbeatErrors=L(t),t.length===0?e.innerHTML=`<p style="color:var(--text-dim);font-size:12px;text-align:center">${m(`hb.empty`)}</p>`:e.innerHTML=t.map((e,t)=>{let r=Hn(e.schedule),i=r.kind===`cron`,a=n.heartbeatErrors[e.id],o=a||Yn(r),s=a?`hb-schedule-meta hb-error`:`hb-schedule-meta hb-help`,c=i?`<input type="text" value="${f(r.cron)}" placeholder="${f(m(`hb.cronPlaceholder`))}"
19
+ data-hb-cron="${t}">`:`<input type="number" value="${r.minutes}" min="1" data-hb-minutes="${t}">`,l=i?`<span class="hb-chip">${f(m(`hb.cronLabel`))}</span>`:`<span class="hb-chip">${f(m(`hb.minutesLabel`))}</span>`;return`
20
+ <div class="hb-job-card">
21
+ <div class="hb-job-header">
22
+ <input type="text" value="${f(String(e.name||``))}" placeholder="${f(m(`hb.name`))}"
23
+ data-hb-name="${t}">
24
+ <button class="hb-toggle ${e.enabled?`on`:`off`}"
25
+ data-hb-toggle="${t}" aria-label="${f(String(e.name||`job`)+` toggle`)}"></button>
26
+ <button class="hb-del" data-hb-remove="${t}">${p.close}</button>
27
+ </div>
28
+ <div class="hb-job-schedule">
29
+ <select data-hb-kind="${t}">
30
+ <option value="every"${i?``:` selected`}>${f(m(`hb.kindEvery`))}</option>
31
+ <option value="cron"${i?` selected`:``}>${f(m(`hb.kindCron`))}</option>
32
+ </select>
33
+ ${c}
34
+ ${l}
35
+ <input type="text" value="${f(r.timeZone||``)}" placeholder="${f(Kn())}"
36
+ data-hb-timezone="${t}">
37
+ </div>
38
+ <p class="${s}">${f(o)}</p>
39
+ <textarea class="hb-prompt" rows="2" placeholder="${f(m(`hb.prompt`))}"
40
+ data-hb-prompt="${t}">${f(String(e.prompt||``))}</textarea>
41
+ </div>
42
+ `}).join(``);let r=t.filter(e=>e.enabled).length,i=document.getElementById(`hbSidebarBtn`);i&&(i.innerHTML=`${p.heartPulse} Heartbeat (${r})`)}var Ln=0;function Rn(){n.heartbeatJobs.push({id:`hb_${Date.now()}_${Ln++}`,name:``,enabled:!0,schedule:Wn({kind:`every`,minutes:5}),prompt:``}),P(),F()}function zn(e){n.heartbeatJobs.splice(e,1),P(),F()}function Bn(e){let t=n.heartbeatJobs[e];t&&(t.enabled=!t.enabled,P(),F())}async function F(){let e=n.heartbeatJobs.map(I);if(n.heartbeatJobs=e,n.heartbeatErrors=L(e),Object.keys(n.heartbeatErrors).length>0){P();return}let t=await i(`/api/heartbeat`,`PUT`,{jobs:e});t?.jobs&&(n.heartbeatJobs=t.jobs.map(I),n.heartbeatErrors=L(n.heartbeatJobs),P())}async function Vn(){try{let e=((await a(`/api/heartbeat`))?.jobs||[]).map(I).filter(e=>e.enabled).length,t=document.getElementById(`hbSidebarBtn`);t&&(t.innerHTML=`${p.heartPulse} Heartbeat (${e})`)}catch{}}function I(e){return{id:String(e.id||`hb_${Date.now()}_${Ln++}`),name:String(e.name||``),enabled:e.enabled!==!1,schedule:Hn(e.schedule),prompt:String(e.prompt||``)}}function Hn(e){let t=Un(e?.timeZone);if(e?.kind===`cron`){let n=typeof e.cron==`string`?e.cron.trim().replace(/\s+/g,` `):`0 9 * * *`;return t?{kind:`cron`,cron:n,timeZone:t}:{kind:`cron`,cron:n}}let n=typeof e?.minutes==`number`&&Number.isFinite(e.minutes)&&e.minutes>0?Math.max(1,Math.floor(e.minutes)):5;return t?{kind:`every`,minutes:n,timeZone:t}:{kind:`every`,minutes:n}}function Un(e){return(typeof e==`string`?e.trim():``)||void 0}function Wn(e){let t=Gn();return t?{...e,timeZone:t}:e}function Gn(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||void 0}catch{return}}function Kn(){let e=Gn();return e?`${m(`hb.timezoneAuto`)} ${e}`:m(`hb.timezoneAuto`)}function L(e){let t={};for(let n of e){let e=qn(n);e&&(t[n.id]=e)}return t}function qn(e){let t=Dn(e.schedule);return t.ok?null:Jn(t.code,t.error)}function Jn(e,t){switch(e){case`invalid_cron`:return m(`hb.invalidCron`);case`invalid_timezone`:return m(`hb.invalidTimeZone`);case`invalid_minutes`:return m(`hb.invalidMinutes`);default:return t||m(`hb.invalidSchedule`)}}function Yn(e){let t=e.timeZone||Gn()||`Asia/Seoul`;return e.kind===`cron`?m(`hb.scheduleHintCron`,{cron:`0 9 * * *`,timeZone:t}):m(`hb.scheduleHintEvery`,{minutes:e.minutes,timeZone:t})}var R=`jaw.uiTheme`,z=null;function Xn(e){return e===`dark`||e===`light`||e===`auto`}function Zn(e){return e===`light`||e===`auto`&&typeof window<`u`&&window.matchMedia&&window.matchMedia(`(prefers-color-scheme: light)`).matches?`light`:`dark`}function Qn(n){let r=n===`light`?e:t;z||(z=document.createElement(`style`),z.id=`hljsTheme`,document.head.appendChild(z)),z.textContent=r}function $n(){!localStorage.getItem(R)&&localStorage.getItem(`theme`)&&(localStorage.setItem(R,localStorage.getItem(`theme`)),localStorage.removeItem(`theme`));let e=tr(),t=localStorage.getItem(R),n=window.matchMedia(`(prefers-color-scheme: light)`).matches?`light`:`dark`;B(e||t||n),document.getElementById(`toggleTheme`)?.addEventListener(`click`,er),window.addEventListener(`message`,rr),window.matchMedia(`(prefers-color-scheme: dark)`).addEventListener(`change`,()=>{let e=localStorage.getItem(R);(!e||e===`auto`)&&B(e||`auto`)}),window.addEventListener(`storage`,e=>{e.key===R&&e.newValue&&B(e.newValue)})}function er(){let e=(document.documentElement.getAttribute(`data-theme`)||`dark`)===`dark`?`light`:`dark`;B(e),localStorage.setItem(R,e)}function B(e){let t=Zn(e);document.documentElement.setAttribute(`data-theme`,t);let n=document.getElementById(`toggleTheme`);n&&n.classList.toggle(`is-light`,t===`light`),Qn(t),Ee(),ue()}function tr(){let e=new URLSearchParams(window.location.search).get(`jawTheme`);return Xn(e)?e:null}function nr(e){if(e===window.location.origin)return!0;try{let t=new URL(e).hostname;return t===`localhost`||t===`127.0.0.1`||t===`::1`||t===`[::1]`}catch{return!1}}function rr(e){if(window.parent===window||e.source!==window.parent||!nr(e.origin))return;let t=e.data;!t||t.type!==`jaw-preview-theme-sync`||!Xn(t.theme)||B(t.theme)}var ir=new Set([`j`,`k`,`i`,`n`,`p`]);function ar(e){if(e.altKey&&!e.ctrlKey&&!e.metaKey){let t=e.key.toLowerCase();return(t.length!==1||!ir.has(t))&&e.code?.startsWith(`Key`)&&(t=e.code.slice(3).toLowerCase()),ir.has(t)}return e.ctrlKey&&!e.metaKey&&!e.altKey?e.code===`Backquote`:!!(e.metaKey&&!e.ctrlKey&&!e.altKey&&(!e.shiftKey&&(e.code===`Backquote`||e.code===`KeyB`||e.code===`KeyJ`)||e.shiftKey&&(e.code===`KeyB`||e.code===`KeyD`||e.code===`KeyE`)))}function or(){window.parent!==window&&document.addEventListener(`keydown`,e=>{if(ar(e)){e.preventDefault();try{window.parent.postMessage({type:`jaw-preview-shortcut`,key:e.key,code:e.code,altKey:e.altKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,shiftKey:e.shiftKey},`*`)}catch{}}})}var sr=50,cr=30,lr=80,ur=500,V=null,dr=!1;function fr(){if(dr||!(`ontouchstart`in window))return;dr=!0;let e=document.querySelector(`.chat-area`);e&&(e.addEventListener(`touchstart`,pr,{passive:!0}),e.addEventListener(`touchend`,mr,{passive:!0}))}function pr(e){let t=e.touches[0],n=window.innerWidth,r=null;t.clientX<cr?r=`left`:t.clientX>n-cr&&(r=`right`),V={startX:t.clientX,startY:t.clientY,startTime:Date.now(),isEdge:r}}function mr(e){if(!V)return;let t=e.changedTouches[0],n=t.clientX-V.startX,r=Math.abs(t.clientY-V.startY),i=Date.now()-V.startTime,a=V;V=null,!(r>lr||i>ur||Math.abs(n)<sr)&&(n>0&&a.isEdge===`left`&&re(),n<0&&a.isEdge===`right`&&te())}var H=!1,U=null,W=[],hr=null,gr=null,_r=0,G=!1,K=`stopped`,vr=15e3;function yr(){if(typeof MediaRecorder>`u`)return``;for(let e of[`audio/webm;codecs=opus`,`audio/mp4`,`audio/ogg;codecs=opus`])if(MediaRecorder.isTypeSupported(e))return e;return``}function br(e){let t=e;switch(t.name){case`NotAllowedError`:return m(`voice.micDenied`);case`NotFoundError`:return m(`voice.micNotFound`);case`NotReadableError`:case`AbortError`:return m(`voice.micBusy`);case`TimeoutError`:return Tr();default:return t instanceof TypeError||!navigator.mediaDevices?m(`voice.httpsRequired`):m(`voice.micDenied`)}}function xr(){let e=document;return e.permissionsPolicy||e.featurePolicy||null}function Sr(){try{return xr()?.allowsFeature?.(`microphone`)===!1}catch{return!1}}async function Cr(){try{return navigator.permissions?.query?(await navigator.permissions.query({name:`microphone`})).state:null}catch{return null}}function wr(){let e=new URL(window.location.href),t=new URL(e.href);e.hostname===`127.0.0.1`?t.hostname=`localhost`:e.hostname===`localhost`&&(t.hostname=`127.0.0.1`);let n=t.origin===e.origin?``:t.origin;return{origin:e.origin,alternateOrigin:n}}function Tr(){let e=wr();return e.alternateOrigin?m(`voice.requestTimeoutLoopback`,e):m(`voice.requestTimeout`,e)}function Er(e){let t=e;return t.name===`NotSupportedError`?m(`voice.unsupported`):t.name===`NotReadableError`||t.name===`AbortError`?m(`voice.micBusy`):t instanceof TypeError?m(`voice.httpsRequired`):m(`voice.interrupted`)}function q(e){if(window.parent!==window)try{window.parent.postMessage({type:`jaw-preview-stt-recording`,action:e},`*`)}catch{}}function Dr(){return new DOMException(`Timed out waiting for microphone permission prompt`,`TimeoutError`)}async function Or(){let e=!1,t=null,n=navigator.mediaDevices.getUserMedia({audio:!0}).then(t=>e?(t.getTracks().forEach(e=>e.stop()),null):t,t=>{if(e)return null;throw t}),r=new Promise((n,r)=>{t=setTimeout(()=>{e=!0,r(Dr())},vr)});try{let e=await Promise.race([n,r]);if(!e)throw Dr();return e}finally{t!==null&&clearTimeout(t)}}async function kr(){if(n.isRecording||G)return;if(typeof MediaRecorder>`u`||!navigator.mediaDevices?.getUserMedia){v(m(`voice.unsupported`),``,`error`);return}if(Sr()){v(m(`voice.policyBlocked`),``,`error`);return}if(await Cr()===`denied`){v(m(`voice.permissionDeniedBrowser`,wr()),``,`error`);return}G=!0,H=!1,K=`stopped`,Y(!1),q(`request`);let e=null;try{e=await Or(),hr=e}catch(e){G=!1,Y(!1),q(`failed`),v(br(e),``,`error`);return}try{let t=yr(),r=new MediaRecorder(e,t?{mimeType:t}:{});U=r,W=[],r.ondataavailable=e=>{e.data.size>0&&W.push(e.data)},r.onerror=()=>{H=!0,K=`failed`,n.isRecording=!1,Pr(),Y(!1),r.state===`recording`?r.stop():(W=[],J(),U=null,q(`failed`)),v(m(`voice.interrupted`),``,`error`)},r.onstop=async()=>{let e=K,n=H||e!==`stopped`;if(K=`stopped`,n){W=[],J(),U=null,H=!1,q(e);return}let i=r.mimeType||t||`audio/webm`,a=i.includes(`mp4`)?`.m4a`:i.includes(`ogg`)?`.ogg`:`.webm`,o=new Blob(W,{type:i});if(W=[],J(),U=null,H=!1,q(`stopped`),o.size>20*1024*1024){v(m(`voice.tooLarge`),``,`error`);return}if(o.size<1e3){v(m(`voice.tooShort`),``,`error`);return}await Cn(o,a,i)},r.start(),n.isRecording=!0,G=!1,_r=Date.now(),Y(!0),Nr(),q(`started`)}catch(e){G=!1,n.isRecording=!1,W=[],U=null,J(),Y(!1),q(`failed`),v(Er(e),``,`error`)}}function Ar(){!n.isRecording||!U||(K=`stopped`,U.state===`recording`?U.stop():(J(),U=null,q(`stopped`)),n.isRecording=!1,Pr(),Y(!1))}function jr(){!n.isRecording||!U||(H=!0,K=`cancelled`,U.state===`recording`?U.stop():(W=[],J(),U=null,q(`cancelled`)),n.isRecording=!1,Pr(),Y(!1))}function Mr(){n.isRecording?Ar():kr()}function J(){hr?.getTracks().forEach(e=>e.stop()),hr=null}function Nr(){let e=document.getElementById(`voiceTimer`);e&&(e.style.display=`inline`,gr=setInterval(()=>{let t=Math.floor((Date.now()-_r)/1e3);e.textContent=`${String(Math.floor(t/60)).padStart(2,`0`)}:${String(t%60).padStart(2,`0`)}`},500))}function Pr(){gr&&=(clearInterval(gr),null);let e=document.getElementById(`voiceTimer`);e&&(e.style.display=`none`,e.textContent=`00:00`)}function Y(e){let t=document.getElementById(`btnVoice`),n=document.getElementById(`btnVoiceCancel`),r=G&&!e;t&&(t.classList.toggle(`recording`,e),t.classList.toggle(`arming`,r),t.innerHTML=e?p.stop:p.mic,t.title=m(r?`voice.requesting`:e?`voice.stop`:`voice.start`),t.toggleAttribute(`aria-busy`,r),t instanceof HTMLButtonElement&&(t.disabled=r)),n&&(n.style.display=e?`inline-block`:`none`)}window.parent;var Fr=null,Ir=``,X=null,Lr=!1;function Rr(){let e=document.getElementById(`chatSearchInput`),t=document.getElementById(`chatSearchClose`),n=document.getElementById(`chatSearchResults`),r=document.getElementById(`chatSearchCount`);!e||!t||!n||!r||Lr||(Lr=!0,window.addEventListener(`message`,e=>{e.data?.type===`jaw-preview-search`&&zr()}),e.addEventListener(`input`,()=>{let t=e.value.trim();if(Fr&&clearTimeout(Fr),!t){qr(n,r);return}Fr=setTimeout(()=>void Vr(t,n,r),250)}),e.addEventListener(`keydown`,e=>{if(e.key===`Escape`){Z();return}e.key===`Enter`&&(e.preventDefault(),n.querySelector(`.chat-search-result`)?.click()),e.key===`ArrowDown`&&(e.preventDefault(),Kr(n,1)),e.key===`ArrowUp`&&(e.preventDefault(),Kr(n,-1))}),n.addEventListener(`wheel`,e=>{let t=n,r=t.scrollTop===0&&e.deltaY<0,i=t.scrollTop+t.clientHeight>=t.scrollHeight&&e.deltaY>0;!r&&!i&&e.stopPropagation(),(r||i)&&e.preventDefault()},{passive:!1}),t.addEventListener(`click`,Z))}function zr(){let e=document.getElementById(`chatSearch`),t=document.getElementById(`chatSearchInput`);!e||!t||(e.classList.contains(`open`)?Z():(e.classList.add(`open`),t.focus(),Br()))}function Z(){let e=document.getElementById(`chatSearch`),t=document.getElementById(`chatSearchInput`),n=document.getElementById(`chatSearchResults`),r=document.getElementById(`chatSearchCount`);e&&(e.classList.remove(`open`),t&&(t.value=``),n&&r&&qr(n,r),Ir=``)}function Br(){X=new Map,a(`/api/messages${Ae()}`).then(e=>{e&&(X=new Map,e.forEach((e,t)=>X.set(e.id,t)))}).catch(()=>{})}async function Vr(e,t,n){Ir=e;let r=await a(_e(`/api/messages/search?q=${encodeURIComponent(e)}&limit=20`));if(!(!r||Ir!==e)){if(n.textContent=r.length>0?`${r.length} found`:`no results`,r.length===0){t.classList.remove(`has-results`),t.innerHTML=``;return}t.innerHTML=r.map(t=>Hr(t,e)).join(``),t.classList.add(`has-results`),t.querySelectorAll(`.chat-search-result`).forEach(e=>{e.addEventListener(`click`,()=>{Gr(Number(e.dataset.msgId))})})}}function Hr(e,t){let n=e.role===`assistant`?`agent`:e.role,r=n===`agent`?e.cli||`agent`:`you`,i=e.match_field===`tool_log`,a=i?`<span class="chat-search-result-badge">tool</span>`:``,o=Wr(i?Ur(e.tool_log,t):e.content,t),s=Jr(e.created_at);return`<div class="chat-search-result" data-msg-id="${e.id}" tabindex="-1">
43
+ <div class="chat-search-result-meta">
44
+ <span class="chat-search-result-role ${n}">${f(r)}</span>
45
+ ${a}
46
+ <span>${f(s)}</span>
47
+ </div>
48
+ <div class="chat-search-result-snippet">${o}</div>
49
+ </div>`}function Ur(e,t){if(!e)return``;try{let n=JSON.parse(e),r=t.toLowerCase();for(let e of n){let t=`${e.label||``} ${e.detail||``}`;if(t.toLowerCase().includes(r))return t}}catch{}return e.slice(0,300)}function Wr(e,t){let n=e.toLowerCase().indexOf(t.toLowerCase());if(n===-1)return f(e.slice(0,120));let r=Math.max(0,n-40),i=Math.min(e.length,n+t.length+80),a=f(e.slice(r,n)),o=f(e.slice(n,n+t.length)),s=f(e.slice(n+t.length,i));return`${r>0?`&hellip;`:``}${a}<mark>${o}</mark>${s}${i<e.length?`&hellip;`:``}`}function Gr(e){if(!X)return;let t=X.get(e);t!=null&&(we().scrollToIndex(t,`center`),requestAnimationFrame(()=>{setTimeout(()=>{let e=document.getElementById(`chatMessages`);if(!e)return;e.querySelectorAll(`.msg.search-highlight`).forEach(e=>e.classList.remove(`search-highlight`));let n=e.querySelectorAll(`.msg`);for(let e of n){let n=e.getAttribute(`data-turn-index`);if(n!=null&&Number(n)===t){e.classList.add(`search-highlight`);break}}},150)}))}function Kr(e,t){let n=Array.from(e.querySelectorAll(`.chat-search-result`));if(n.length===0)return;let r=e.querySelector(`.chat-search-result.active`),i=r?n.indexOf(r)+t:0;i<0&&(i=n.length-1),i>=n.length&&(i=0),n.forEach(e=>e.classList.remove(`active`)),n[i].classList.add(`active`),n[i].scrollIntoView({block:`nearest`}),n[i].click()}function qr(e,t){e.classList.remove(`has-results`),e.innerHTML=``,t.textContent=``}function Jr(e){try{let t=new Date(e),n=new Date;return t.toDateString()===n.toDateString()?t.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`}):t.toLocaleDateString([],{month:`short`,day:`numeric`})+` `+t.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`})}catch{return``}}var Yr=!1;function Xr(){if(Yr)return;Yr=!0;let e=document.createElement(`div`);e.id=`mediaLightbox`,e.className=`media-lightbox hidden`,e.innerHTML=`<div class="media-lightbox-backdrop"></div>
50
+ <div class="media-lightbox-content">
51
+ <img class="media-lightbox-img" alt="" />
52
+ <div class="media-lightbox-controls">
53
+ <button class="media-lightbox-open" aria-label="Open in browser">↗ Open</button>
54
+ <button class="media-lightbox-close" aria-label="Close">✕</button>
55
+ </div></div>`,document.body.appendChild(e);let t=``,n=()=>e.classList.add(`hidden`),r=()=>{t&&window.open(t,`_blank`)};e.querySelector(`.media-lightbox-backdrop`).addEventListener(`click`,n),e.querySelector(`.media-lightbox-close`).addEventListener(`click`,n),e.querySelector(`.media-lightbox-open`).addEventListener(`click`,r),e.querySelector(`.media-lightbox-img`).addEventListener(`dblclick`,r),document.addEventListener(`keydown`,t=>{t.key===`Escape`&&!e.classList.contains(`hidden`)&&n()}),document.addEventListener(`click`,n=>{let r=n.target;(r.classList.contains(`chat-inline-img`)||r.closest(`.file-preview-list`)&&r.tagName===`IMG`)&&(t=r.src,e.querySelector(`.media-lightbox-img`).src=t,e.classList.remove(`hidden`))})}var Zr=`jaw:stale-bundle-reload-pending`;function Qr(e){let t=typeof e==`string`?e:e instanceof Error||typeof e?.message==`string`?e.message:``;if(!/(failed to fetch dynamically imported module|error loading dynamically imported module|importing a module script failed|chunkloaderror)/i.test(t))return!1;console.warn(`[stale-bundle] detected dynamic import failure:`,t);try{if(sessionStorage.getItem(Zr)===`1`)return!0;sessionStorage.setItem(Zr,`1`)}catch{}let n=()=>window.location.reload();return`serviceWorker`in navigator?(navigator.serviceWorker.getRegistration().then(e=>e?.update()).catch(()=>{}).finally(n),!0):(n(),!0)}window.addEventListener(`unhandledrejection`,e=>{if(Qr(e.reason)){e.preventDefault();return}console.error(`[unhandled]`,e.reason),e.preventDefault()}),window.addEventListener(`error`,e=>{if(Qr(e.error||e.message)){e.preventDefault();return}console.error(`[error]`,e.message,e.filename,e.lineno)});var{loadEmployees:$r,addEmployee:ei,deleteEmployee:ti,updateEmployee:Q,onEmpCliChange:ni,onEmpRoleChange:ri}=await d(async()=>{let{loadEmployees:e,addEmployee:t,deleteEmployee:n,updateEmployee:r,onEmpCliChange:i,onEmpRoleChange:a}=await import(`./employees-DCju7V1x.js`);return{loadEmployees:e,addEmployee:t,deleteEmployee:n,updateEmployee:r,onEmpCliChange:i,onEmpRoleChange:a}},__vite__mapDeps([0]));function ii(e){if(e===window.location.origin)return!0;try{let t=new URL(e).hostname;return t===`localhost`||t===`127.0.0.1`||t===`::1`||t===`[::1]`}catch{return!1}}function ai(e){let t=(e.ctrlKey||e.metaKey)&&e.shiftKey&&e.code===`Space`,n=e.altKey&&!e.ctrlKey&&!e.metaKey&&!e.shiftKey&&e.code===`KeyM`;return t||n}document.getElementById(`btnSend`)?.addEventListener(`click`,()=>{A(`button`)});var oi=document.getElementById(`chatInput`);oi?.addEventListener(`keydown`,e=>{Ve(e)||pn(e)});var $=0;oi?.addEventListener(`input`,e=>{e.isComposing||($&&cancelAnimationFrame($),$=requestAnimationFrame(()=>{Be(e.target?.value||``),$=0}))}),oi?.addEventListener(`cmd-execute`,()=>{A(`cmd-execute`)}),document.getElementById(`cmdDropdown`)?.addEventListener(`click`,ze),document.addEventListener(`click`,Re),u(),Qt(),document.getElementById(`filePreviewClear`)?.addEventListener(`click`,j),document.getElementById(`filePreviewList`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`[data-file-idx]`);t&&gn(+(t.dataset.fileIdx||`0`))}),document.querySelector(`.btn-attach`)?.addEventListener(`click`,()=>{document.getElementById(`fileInput`)?.click()}),document.getElementById(`btnVoice`)?.addEventListener(`click`,()=>Mr()),document.getElementById(`btnVoiceCancel`)?.addEventListener(`click`,()=>jr()),window.addEventListener(`message`,e=>{if(window.parent===window||e.source!==window.parent||!ii(e.origin))return;let t=e.data;!t||t.type!==`jaw-preview-stt-toggle`||Mr()}),document.getElementById(`memorySidebarBtn`)?.addEventListener(`click`,Ut),document.getElementById(`btnClearChat`)?.addEventListener(`click`,vn),document.getElementById(`hbSidebarBtn`)?.addEventListener(`click`,Fn),document.getElementById(`langSelect`)?.addEventListener(`change`,async e=>{let t=e.target.value;[`ko`,`en`,`zh`,`ja`].includes(t)&&(await pe(t),ie())}),document.querySelector(`.tab-bar`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`.tab-btn`);if(!t)return;let n=[...t.parentElement?.children||[]].filter(e=>e.classList.contains(`tab-btn`)).indexOf(t),r=[`agents`,`skills`,`settings`];r[n]&&Ne(r[n],t)}),document.querySelector(`.sidebar-save-bar .btn-save`)?.addEventListener(`click`,Pe);function si(e){let t=(e instanceof HTMLSelectElement?e:document.getElementById(`selCliProvider`))?.value||``,n=document.getElementById(`providerAiE`);!t||!n||Array.from(n.options).some(e=>e.value===t)&&(n.value=t)}document.getElementById(`selCli`)?.addEventListener(`change`,()=>pt()),document.getElementById(`selCliProvider`)?.addEventListener(`change`,e=>{si(e.target),pt(),S()}),document.getElementById(`selModel`)?.addEventListener(`change`,()=>S()),document.getElementById(`selEffort`)?.addEventListener(`change`,()=>S()),document.getElementById(`flushCli`)?.addEventListener(`change`,()=>ht()),document.getElementById(`flushModel`)?.addEventListener(`change`,()=>ht()),document.querySelector(`[data-action="addEmployee"]`)?.addEventListener(`click`,ei),document.getElementById(`employeesList`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`[data-emp-delete]`);if(t){ti(t.dataset.empDelete||``);return}}),document.getElementById(`employeesList`)?.addEventListener(`change`,e=>{let t=e.target,n=t.closest(`[data-emp-name]`);if(n){Q(n.dataset.empName||``,{name:t.value});return}let r=t.closest(`[data-emp-cli]`);if(r){ni(r.dataset.empCli||``,t.value);return}let i=t.closest(`[data-emp-model]`);if(i){if(t.value===`__custom__`){let e=prompt(m(`model.promptInput`));if(e?.trim()){let n=document.createElement(`option`);n.value=e.trim(),n.textContent=e.trim();let r=t.querySelector(`option[value="__custom__"]`);r&&t.insertBefore(n,r),t.value=e.trim(),Q(i.dataset.empModel||``,{model:e.trim()})}else t.value=`default`}else Q(i.dataset.empModel||``,{model:t.value});return}let a=t.closest(`[data-emp-role]`);if(a){ri(a.dataset.empRole||``,t.value);return}let o=t.closest(`[data-emp-custom]`);if(o){Q(o.dataset.empCustom||``,{role:t.value});return}}),document.getElementById(`skillsList`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`[data-skill-id]`);t&&St(t.dataset.skillId||``,t.dataset.skillEnabled===`true`)}),document.querySelector(`#tabSkills`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`.skill-filter`);t&&wt(t.dataset.filter||`all`,t)}),document.getElementById(`skillSearchInput`)?.addEventListener(`input`,e=>{Ct(e.target.value)}),document.querySelector(`[data-action="openPrompt"]`)?.addEventListener(`click`,Lt),document.getElementById(`tgOff`)?.addEventListener(`click`,()=>Qe(!1)),document.getElementById(`tgOn`)?.addEventListener(`click`,()=>Qe(!0)),document.getElementById(`tgForwardOff`)?.addEventListener(`click`,()=>Ze(!1)),document.getElementById(`tgForwardOn`)?.addEventListener(`click`,()=>Ze(!0)),document.getElementById(`tgMentionOff`)?.addEventListener(`click`,()=>rt(!1)),document.getElementById(`tgMentionOn`)?.addEventListener(`click`,()=>rt(!0)),document.getElementById(`tgToken`)?.addEventListener(`change`,Xe),document.getElementById(`tgChatIds`)?.addEventListener(`change`,Xe),document.getElementById(`chTelegram`)?.addEventListener(`click`,()=>b(`telegram`)),document.getElementById(`chDiscord`)?.addEventListener(`click`,()=>b(`discord`)),document.getElementById(`dcOff`)?.addEventListener(`click`,()=>ut(!1)),document.getElementById(`dcOn`)?.addEventListener(`click`,()=>ut(!0)),document.getElementById(`dcForwardOff`)?.addEventListener(`click`,()=>et(!1)),document.getElementById(`dcForwardOn`)?.addEventListener(`click`,()=>et(!0)),document.getElementById(`dcAllowBotsOff`)?.addEventListener(`click`,()=>$e(!1)),document.getElementById(`dcAllowBotsOn`)?.addEventListener(`click`,()=>$e(!0)),document.getElementById(`dcMentionOff`)?.addEventListener(`click`,()=>nt(!1)),document.getElementById(`dcMentionOn`)?.addEventListener(`click`,()=>nt(!0)),document.getElementById(`dcToken`)?.addEventListener(`change`,Ke),document.getElementById(`dcGuildId`)?.addEventListener(`change`,Ke),document.getElementById(`dcChannelIds`)?.addEventListener(`change`,Ke),document.getElementById(`chSlack`)?.addEventListener(`click`,()=>b(`slack`)),document.getElementById(`slOff`)?.addEventListener(`click`,()=>it(!1)),document.getElementById(`slOn`)?.addEventListener(`click`,()=>it(!0)),document.getElementById(`slForwardOff`)?.addEventListener(`click`,()=>Je(!1)),document.getElementById(`slForwardOn`)?.addEventListener(`click`,()=>Je(!0)),document.getElementById(`slAllowBotsOff`)?.addEventListener(`click`,()=>Ye(!1)),document.getElementById(`slAllowBotsOn`)?.addEventListener(`click`,()=>Ye(!0)),document.getElementById(`slMentionOff`)?.addEventListener(`click`,()=>tt(!1)),document.getElementById(`slMentionOn`)?.addEventListener(`click`,()=>tt(!0)),document.getElementById(`slThreadOff`)?.addEventListener(`click`,()=>dt(!1)),document.getElementById(`slThreadOn`)?.addEventListener(`click`,()=>dt(!0)),document.getElementById(`slBotToken`)?.addEventListener(`change`,C),document.getElementById(`slAppToken`)?.addEventListener(`change`,C),document.getElementById(`slTeamId`)?.addEventListener(`change`,C),document.getElementById(`slChannelIds`)?.addEventListener(`change`,C),document.getElementById(`slAttachPort`)?.addEventListener(`change`,C),bt(),qe(),document.getElementById(`fallbackOrderList`)?.addEventListener(`change`,vt);function ci(e){let t=document.getElementById(`codexFastOn`),n=document.getElementById(`codexFastOff`);t&&n&&(t.classList.toggle(`active`,e),n.classList.toggle(`active`,!e)),x()}document.getElementById(`codexFastOn`)?.addEventListener(`click`,()=>ci(!0)),document.getElementById(`codexFastOff`)?.addEventListener(`click`,()=>ci(!1));function li(e){let t=document.getElementById(`codexCtxOn`),n=document.getElementById(`codexCtxOff`),r=document.getElementById(`codexCtxValues`);t&&n&&(t.classList.toggle(`active`,e),n.classList.toggle(`active`,!e)),r&&(r.style.display=e?``:`none`),x()}document.getElementById(`codexCtxOn`)?.addEventListener(`click`,()=>li(!0)),document.getElementById(`codexCtxOff`)?.addEventListener(`click`,()=>li(!1)),document.getElementById(`codexCtxWindow`)?.addEventListener(`change`,x),document.getElementById(`codexCtxCompact`)?.addEventListener(`change`,x);function ui(e){let t=document.getElementById(`claude1mOn`),n=document.getElementById(`claude1mOff`),r=document.getElementById(`modelClaude`),i=e;if(r){let t=r.value||``;if(e&&!t.endsWith(`[1m]`)){let e=t+`[1m]`;Array.from(r.options).some(t=>t.value===e)?r.value=e:i=!1}else if(!e&&t.endsWith(`[1m]`)){let e=t.replace(/\[1m\]$/,``);Array.from(r.options).some(t=>t.value===e)?r.value=e:i=!0}else i=t.endsWith(`[1m]`)}t&&n&&(t.classList.toggle(`active`,i),n.classList.toggle(`active`,!i)),x()}document.getElementById(`claude1mOn`)?.addEventListener(`click`,()=>ui(!0)),document.getElementById(`claude1mOff`)?.addEventListener(`click`,()=>ui(!1));function di(e){return e.split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``)}function fi(){for(let e of We()){let t=di(e),n=document.getElementById(`model`+t);n&&n.addEventListener(`change`,function(){mt(e,this)});let r=document.getElementById(`customModel`+t);r&&r.addEventListener(`change`,function(){gt(e,this)});let i=document.getElementById(`effort`+t);i&&i.addEventListener(`change`,x)}document.getElementById(`providerAiE`)?.addEventListener(`change`,ot)}document.querySelector(`[data-action="openMcpModal"]`)?.addEventListener(`click`,_t),document.querySelector(`[data-action="syncMcp"]`)?.addEventListener(`click`,yt),document.querySelector(`[data-action="installMcp"]`)?.addEventListener(`click`,st),ct(),document.querySelector(`[data-action="refreshCli"]`)?.addEventListener(`click`,()=>{Ot()?Nt(!0):Mt()}),document.getElementById(`cliStatusInterval`)?.addEventListener(`change`,function(){kt(this.value)}),document.getElementById(`promptModal`)?.addEventListener(`click`,e=>w(e)),document.querySelector(`#promptModal .modal-box`)?.addEventListener(`click`,e=>e.stopPropagation()),document.querySelector(`[data-action="closePrompt"]`)?.addEventListener(`click`,()=>w()),document.querySelector(`[data-action="cancelPrompt"]`)?.addEventListener(`click`,()=>w()),document.querySelector(`[data-action="savePrompt"]`)?.addEventListener(`click`,Tt),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&!n.isRecording&&w()}),document.querySelector(`[data-action="openTemplates"]`)?.addEventListener(`click`,jt),document.querySelector(`[data-action="saveTemplate"]`)?.addEventListener(`click`,Ft),document.querySelector(`[data-action="closeTemplate"]`)?.addEventListener(`click`,()=>Pt()),document.getElementById(`templateModal`)?.addEventListener(`click`,e=>Pt(e)),document.querySelector(`#templateModal .modal-box`)?.addEventListener(`click`,e=>e.stopPropagation()),document.getElementById(`templateBack`)?.addEventListener(`click`,Rt),document.getElementById(`templateDevToggle`)?.addEventListener(`click`,Et);var{openPiSettingsModal:pi,closePiSettingsModal:mi,registerPiProfile:hi,onPiModeChange:gi,syncPiModelDropdown:_i,getCachedPi:vi}=await d(async()=>{let{openPiSettingsModal:e,closePiSettingsModal:t,registerPiProfile:n,onPiModeChange:r,syncPiModelDropdown:i,getCachedPi:a}=await import(`./pi-settings-5HVg1sXJ.js`);return{openPiSettingsModal:e,closePiSettingsModal:t,registerPiProfile:n,onPiModeChange:r,syncPiModelDropdown:i,getCachedPi:a}},[]);document.getElementById(`btnPiSettings`)?.addEventListener(`click`,pi),document.getElementById(`piSettingsModal`)?.addEventListener(`click`,e=>{e.target===e.currentTarget&&mi()}),document.querySelector(`#piSettingsModal .modal-box`)?.addEventListener(`click`,e=>e.stopPropagation()),document.querySelector(`[data-action="closePiSettings"]`)?.addEventListener(`click`,mi),document.getElementById(`btnPiRegister`)?.addEventListener(`click`,()=>void hi()),document.getElementById(`piMode`)?.addEventListener(`change`,gi),document.getElementById(`providerPi`)?.addEventListener(`change`,async()=>{let e=document.getElementById(`providerPi`);if(e?.value){_i(e.value,vi());let{savePerCli:t}=await d(async()=>{let{savePerCli:e}=await import(`./settings-core-Cm-CmVLA.js`);return{savePerCli:e}},__vite__mapDeps([0]));await t()}}),document.getElementById(`heartbeatModal`)?.addEventListener(`click`,e=>In(e)),document.querySelector(`#heartbeatModal .modal-box`)?.addEventListener(`click`,e=>e.stopPropagation()),document.querySelector(`[data-action="closeHeartbeat"]`)?.addEventListener(`click`,()=>In()),document.querySelector(`[data-action="addHeartbeat"]`)?.addEventListener(`click`,Rn),document.getElementById(`hbJobsList`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`[data-hb-toggle]`);if(t){Bn(+(t.dataset.hbToggle||`0`));return}let n=e.target?.closest(`[data-hb-remove]`);if(n){zn(+(n.dataset.hbRemove||`0`));return}}),document.getElementById(`hbJobsList`)?.addEventListener(`change`,e=>{let t=e.target,r=t.closest(`[data-hb-name]`);if(r){n.heartbeatJobs[+(r.dataset.hbName||`0`)].name=t.value,F();return}let i=t.closest(`[data-hb-kind]`);if(i){let e=+(i.dataset.hbKind||`0`),r=n.heartbeatJobs[e]?.schedule,a=typeof r?.timeZone==`string`?r.timeZone:void 0;n.heartbeatJobs[e].schedule=t.value===`cron`?{kind:`cron`,cron:typeof r?.cron==`string`&&r.cron.trim()?r.cron:`0 9 * * *`,...a?{timeZone:a}:{}}:{kind:`every`,minutes:typeof r?.minutes==`number`&&r.minutes>0?Math.floor(r.minutes):5,...a?{timeZone:a}:{}},P(),F();return}let a=t.closest(`[data-hb-minutes]`);if(a){let e=+(a.dataset.hbMinutes||`0`),r=n.heartbeatJobs[e]?.schedule,i=Math.max(1,Math.floor(Number(t.value)||5)),o=typeof r?.timeZone==`string`?r.timeZone:void 0;n.heartbeatJobs[e].schedule={kind:`every`,minutes:i,...o?{timeZone:o}:{}},P(),F();return}let o=t.closest(`[data-hb-cron]`);if(o){let e=+(o.dataset.hbCron||`0`),r=n.heartbeatJobs[e]?.schedule,i=typeof r?.timeZone==`string`?r.timeZone:void 0,a=t.value.trim().replace(/\s+/g,` `);n.heartbeatJobs[e].schedule={kind:`cron`,cron:a,...i?{timeZone:i}:{}},P(),F();return}let s=t.closest(`[data-hb-timezone]`);if(s){let e=+(s.dataset.hbTimezone||`0`),r=n.heartbeatJobs[e]?.schedule;r?.kind===`cron`?n.heartbeatJobs[e].schedule={kind:`cron`,cron:typeof r.cron==`string`&&r.cron.trim()?r.cron:`0 9 * * *`,...t.value.trim()?{timeZone:t.value.trim()}:{}}:n.heartbeatJobs[e].schedule={kind:`every`,minutes:typeof r?.minutes==`number`&&r.minutes>0?Math.floor(r.minutes):5,...t.value.trim()?{timeZone:t.value.trim()}:{}},P(),F();return}let c=t.closest(`[data-hb-prompt]`);if(c){n.heartbeatJobs[+(c.dataset.hbPrompt||`0`)].prompt=t.value,F();return}}),document.getElementById(`memoryModal`)?.addEventListener(`click`,e=>Zt(e)),document.querySelector(`#memoryModal .modal-box`)?.addEventListener(`click`,e=>e.stopPropagation()),document.querySelector(`[data-action="closeMemory"]`)?.addEventListener(`click`,()=>Zt()),document.getElementById(`memTabBtnSettings`)?.addEventListener(`click`,()=>E(`settings`)),document.getElementById(`memTabBtnAdvOps`)?.addEventListener(`click`,()=>E(`status`)),document.getElementById(`memTabBtnFiles`)?.addEventListener(`click`,()=>E(`files`)),document.getElementById(`memOn`)?.addEventListener(`click`,()=>Wt(!0)),document.getElementById(`memOff`)?.addEventListener(`click`,()=>Wt(!1)),document.getElementById(`memFlushEvery`)?.addEventListener(`change`,T),document.getElementById(`memRetention`)?.addEventListener(`change`,T),document.getElementById(`memFlushLang`)?.addEventListener(`change`,T),document.getElementById(`memFlushNowBtn`)?.addEventListener(`click`,Ht),document.getElementById(`advBootstrapBtn`)?.addEventListener(`click`,Xt),document.getElementById(`advReindexBtn`)?.addEventListener(`click`,qt),document.getElementById(`advReimportBtn`)?.addEventListener(`click`,Xt),document.getElementById(`advOpenCorruptedBtn`)?.addEventListener(`click`,Yt),document.getElementById(`advStatusBanner`)?.addEventListener(`click`,e=>{e.target?.id===`advUpgradeSoulBtn`&&Jt(),e.target?.id===`advSynthesizeSoulBtn`&&Vt()}),document.getElementById(`basicMemoryFiles`)?.addEventListener(`click`,e=>{let t=e.target?.closest(`[data-mem-delete]`);if(t){e.stopPropagation(),Kt(t.dataset.memDelete||``);return}let n=e.target?.closest(`[data-mem-view]`);if(n){Gt(n.dataset.memView||``);return}if(e.target?.closest(`[data-mem-back]`)){Ut();return}});async function yi(){se(),xt(),$n(),or(),Se(),Ce(),await fe();let e=await ge({cancelRecording:jr});if(e===`redirect`)return;ee(xe);let t=document.getElementById(`langSelect`);if(t&&(t.value=de()),Le(),e===`hub`){oe(),c();return}if(Rr(),Xr(),document.getElementById(`chatSearchTrigger`)?.addEventListener(`click`,zr),oe(),c(),await Ge(),fi(),Sn(),xn(),await Ie(),await at(),lt(),At(),Dt(),zt(),Ot()&&Nt(),ae(),$r(),Vn(),Bt(),ke(),await Oe(),Me(),fr(),window.location.hash===`#settings`){let e=document.getElementById(`tabBtnSettings`);e&&Ne(`settings`,e)}try{sessionStorage.removeItem(Zr)}catch{}setTimeout(()=>{document.querySelector(`.mermaid-pending, [data-mermaid-code-raw], .diagram-widget-pending`)&&ce()},3e3),`serviceWorker`in navigator&&!It()&&navigator.serviceWorker.register(`/sw.js`).catch(()=>{})}yi().catch(e=>{console.error(`[bootstrap]`,e)}),document.addEventListener(`keydown`,e=>{(e.ctrlKey||e.metaKey)&&e.key===`f`&&(e.preventDefault(),e.stopPropagation(),zr())},!0),document.addEventListener(`keydown`,e=>{if(e.key===`Escape`){if(document.getElementById(`chatSearch`)?.classList.contains(`open`)){e.preventDefault(),Z();return}if(n.isRecording){e.preventDefault(),jr();return}document.querySelectorAll(`.modal-overlay.open`).forEach(e=>{e.classList.remove(`open`)})}ai(e)&&(e.preventDefault(),Mr())}),document.getElementById(`mobileMenuLeft`)?.addEventListener(`click`,re),document.getElementById(`mobileMenuRight`)?.addEventListener(`click`,te);
@@ -31,7 +31,7 @@
31
31
  href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=Outfit:wght@400;500;600;700&display=swap"
32
32
  rel="stylesheet">
33
33
  <!-- Vite handles module bundling in dev (HMR) and production (build) -->
34
- <script type="module" crossorigin src="/dist/assets/app-DhDdCs_s.js"></script>
34
+ <script type="module" crossorigin src="/dist/assets/app-DUwWnKh3.js"></script>
35
35
  <link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-kIxHrxDV.css">
36
36
  <link rel="stylesheet" crossorigin href="/dist/assets/app-DuyLYRC6.css">
37
37
  </head>
@@ -80,9 +80,21 @@ export function renderHeartbeatJobs(): void {
80
80
  if (btn) btn.innerHTML = `${ICONS.heartPulse} Heartbeat (${active})`;
81
81
  }
82
82
 
83
+ /** Ids must be unique across one save.
84
+ *
85
+ * `Date.now()` alone is not enough. `normalizeHeartbeatJob` runs inside a
86
+ * `.map()`, so two jobs missing an id in the same millisecond both become
87
+ * `hb_<same-ms>`; two quick clicks on add-job collide the same way. The server
88
+ * rejects a duplicate id now — two jobs under one id share one mention-watch
89
+ * ledger namespace and the second inherits the first's cursor — so a collision
90
+ * that used to silently merge two jobs would fail the entire save. */
91
+ let idCounter = 0;
92
+
83
93
  export function addHeartbeatJob(): void {
84
94
  (state.heartbeatJobs as HeartbeatJob[]).push({
85
- id: 'hb_' + Date.now(),
95
+ // Same-millisecond collision matters here too: two quick clicks on
96
+ // 'add job' would otherwise produce one id twice.
97
+ id: `hb_${Date.now()}_${idCounter++}`,
86
98
  name: '',
87
99
  enabled: true,
88
100
  schedule: withBrowserTimeZone({ kind: 'every', minutes: 5 }),
@@ -134,7 +146,7 @@ export async function initHeartbeatBadge(): Promise<void> {
134
146
 
135
147
  function normalizeHeartbeatJob(job: HeartbeatJob): HeartbeatJob {
136
148
  return {
137
- id: String(job.id || `hb_${Date.now()}`),
149
+ id: String(job.id || `hb_${Date.now()}_${idCounter++}`),
138
150
  name: String(job.name || ''),
139
151
  enabled: job.enabled !== false,
140
152
  schedule: normalizeSchedule(job.schedule),