instar 0.26.4 → 0.26.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +61 -7
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +156 -3
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/core/SessionManager.d.ts +31 -0
  6. package/dist/core/SessionManager.d.ts.map +1 -1
  7. package/dist/core/SessionManager.js +164 -20
  8. package/dist/core/SessionManager.js.map +1 -1
  9. package/dist/core/TopicResumeMap.d.ts +5 -0
  10. package/dist/core/TopicResumeMap.d.ts.map +1 -1
  11. package/dist/core/TopicResumeMap.js +7 -0
  12. package/dist/core/TopicResumeMap.js.map +1 -1
  13. package/dist/core/types.d.ts +0 -4
  14. package/dist/core/types.d.ts.map +1 -1
  15. package/dist/core/types.js.map +1 -1
  16. package/dist/messaging/imessage/IMessageAdapter.d.ts +16 -0
  17. package/dist/messaging/imessage/IMessageAdapter.d.ts.map +1 -1
  18. package/dist/messaging/imessage/IMessageAdapter.js +60 -2
  19. package/dist/messaging/imessage/IMessageAdapter.js.map +1 -1
  20. package/dist/messaging/imessage/types.d.ts +11 -0
  21. package/dist/messaging/imessage/types.d.ts.map +1 -1
  22. package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -1
  23. package/dist/messaging/slack/SlackAdapter.js +46 -12
  24. package/dist/messaging/slack/SlackAdapter.js.map +1 -1
  25. package/dist/scaffold/templates.d.ts +1 -1
  26. package/dist/scaffold/templates.d.ts.map +1 -1
  27. package/dist/scaffold/templates.js +32 -1
  28. package/dist/scaffold/templates.js.map +1 -1
  29. package/dist/server/AgentServer.d.ts +1 -0
  30. package/dist/server/AgentServer.d.ts.map +1 -1
  31. package/dist/server/AgentServer.js +1 -0
  32. package/dist/server/AgentServer.js.map +1 -1
  33. package/dist/server/routes.d.ts +1 -0
  34. package/dist/server/routes.d.ts.map +1 -1
  35. package/dist/server/routes.js +155 -0
  36. package/dist/server/routes.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/data/builtin-manifest.json +65 -49
  39. package/src/templates/hooks/intercept-imsg-send.js +68 -0
  40. package/src/templates/scripts/imessage-reply.sh +130 -0
  41. package/upgrades/0.26.5.md +15 -0
  42. package/upgrades/0.26.6.md +23 -0
  43. package/upgrades/NEXT.md +0 -35
  44. /package/.claude/skills/secret-setup/{SKILL.md → skill.md} +0 -0
@@ -1254,6 +1254,22 @@ export function createRoutes(ctx) {
1254
1254
  adapter: !!ctx.telegram,
1255
1255
  bidirectional: hasTelegramConfig && hasTelegramReplyScript && !!ctx.telegram,
1256
1256
  };
1257
+ // iMessage
1258
+ const hasIMessageConfig = ctx.config.messaging?.some(m => m.type === 'imessage' && m.enabled) ?? false;
1259
+ const imessage = {
1260
+ configured: hasIMessageConfig,
1261
+ adapter: !!ctx.imessage,
1262
+ connected: ctx.imessage?.getConnectionInfo().state === 'connected',
1263
+ endpoints: ctx.imessage ? [
1264
+ 'GET /imessage/status',
1265
+ 'POST /imessage/validate-send/:recipient — validate + get send token (Layer 3)',
1266
+ 'POST /imessage/reply/:recipient — confirm delivery with token (called by imessage-reply.sh)',
1267
+ 'GET /imessage/chats',
1268
+ 'GET /imessage/chats/:chatId/history',
1269
+ 'GET /imessage/search?q=...',
1270
+ 'GET /imessage/log-stats',
1271
+ ] : [],
1272
+ };
1257
1273
  // Jobs
1258
1274
  let jobCount = 0;
1259
1275
  let jobSlugs = [];
@@ -1292,6 +1308,7 @@ export function createRoutes(ctx) {
1292
1308
  scripts,
1293
1309
  hooks,
1294
1310
  telegram,
1311
+ imessage,
1295
1312
  scheduler: {
1296
1313
  enabled: ctx.config.scheduler.enabled,
1297
1314
  jobCount,
@@ -3253,6 +3270,144 @@ export function createRoutes(ctx) {
3253
3270
  links: ctx.messageBridge.getLinks(),
3254
3271
  });
3255
3272
  });
3273
+ // ── iMessage ─────────────────────────────────────────────────────
3274
+ router.get('/imessage/status', (_req, res) => {
3275
+ if (!ctx.imessage) {
3276
+ res.status(503).json({ error: 'iMessage not configured' });
3277
+ return;
3278
+ }
3279
+ res.json(ctx.imessage.getConnectionInfo());
3280
+ });
3281
+ // ── Outbound Safety: validate-before-send endpoint ──
3282
+ // Called by imessage-reply.sh BEFORE sending. Issues a single-use token
3283
+ // that binds validation to the actual send (TOCTOU mitigation).
3284
+ router.post('/imessage/validate-send/:recipient', (req, res) => {
3285
+ if (!ctx.imessage) {
3286
+ res.status(503).json({ error: 'iMessage not configured' });
3287
+ return;
3288
+ }
3289
+ const { recipient } = req.params;
3290
+ if (!recipient) {
3291
+ res.status(400).json({ error: 'recipient parameter required' });
3292
+ return;
3293
+ }
3294
+ const result = ctx.imessage.validateSend(decodeURIComponent(recipient));
3295
+ if (!result.allowed) {
3296
+ res.status(403).json({
3297
+ allowed: false,
3298
+ reason: result.reason,
3299
+ });
3300
+ return;
3301
+ }
3302
+ res.json({
3303
+ allowed: true,
3304
+ token: result.token,
3305
+ sendMode: result.sendMode,
3306
+ });
3307
+ });
3308
+ // Reply confirmation endpoint — called by imessage-reply.sh AFTER sending via imsg CLI.
3309
+ // Requires a valid send token from validate-send (TOCTOU mitigation).
3310
+ // Logs the outbound message and clears stall tracking.
3311
+ router.post('/imessage/reply/:recipient', (req, res) => {
3312
+ if (!ctx.imessage) {
3313
+ res.status(503).json({ error: 'iMessage not configured' });
3314
+ return;
3315
+ }
3316
+ const { recipient } = req.params;
3317
+ if (!recipient) {
3318
+ res.status(400).json({ error: 'recipient parameter required' });
3319
+ return;
3320
+ }
3321
+ const decodedRecipient = decodeURIComponent(recipient);
3322
+ // Validate recipient is authorized
3323
+ if (!ctx.imessage.isAuthorized(decodedRecipient)) {
3324
+ res.status(403).json({ error: 'recipient not in authorizedContacts' });
3325
+ return;
3326
+ }
3327
+ const { text, sendToken } = req.body;
3328
+ if (!text || typeof text !== 'string') {
3329
+ res.status(400).json({ error: '"text" field required' });
3330
+ return;
3331
+ }
3332
+ // Validate send token if provided (TOCTOU binding)
3333
+ if (sendToken) {
3334
+ const tokenResult = ctx.imessage.confirmSend(sendToken, decodedRecipient, text);
3335
+ if (!tokenResult.ok) {
3336
+ res.status(403).json({ error: tokenResult.reason });
3337
+ return;
3338
+ }
3339
+ }
3340
+ // Log outbound message
3341
+ ctx.imessage.logOutboundMessage(decodedRecipient, text);
3342
+ // Clear stall tracking for this sender
3343
+ ctx.imessage.clearStallForSender(decodedRecipient);
3344
+ // Also clear in SessionManager if available
3345
+ if (ctx.sessionManager && 'clearIMessageInjectionTracker' in ctx.sessionManager) {
3346
+ ctx.sessionManager.clearIMessageInjectionTracker(decodedRecipient);
3347
+ }
3348
+ res.json({ ok: true, recipient: decodedRecipient, logged: true });
3349
+ });
3350
+ router.get('/imessage/chats', async (req, res) => {
3351
+ if (!ctx.imessage) {
3352
+ res.status(503).json({ error: 'iMessage not configured' });
3353
+ return;
3354
+ }
3355
+ const limit = parseInt(req.query.limit) || 20;
3356
+ try {
3357
+ const chats = await ctx.imessage.listChats(limit);
3358
+ res.json(chats);
3359
+ }
3360
+ catch (err) {
3361
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
3362
+ }
3363
+ });
3364
+ router.get('/imessage/chats/:chatId/history', async (req, res) => {
3365
+ if (!ctx.imessage) {
3366
+ res.status(503).json({ error: 'iMessage not configured' });
3367
+ return;
3368
+ }
3369
+ const { chatId } = req.params;
3370
+ const limit = parseInt(req.query.limit) || 50;
3371
+ try {
3372
+ const history = await ctx.imessage.getChatHistory(chatId, limit);
3373
+ res.json(history);
3374
+ }
3375
+ catch (err) {
3376
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
3377
+ }
3378
+ });
3379
+ router.get('/imessage/log-stats', (_req, res) => {
3380
+ if (!ctx.imessage) {
3381
+ res.status(503).json({ error: 'iMessage not configured' });
3382
+ return;
3383
+ }
3384
+ try {
3385
+ const stats = ctx.imessage.messageLogger.getStats();
3386
+ res.json(stats);
3387
+ }
3388
+ catch (err) {
3389
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
3390
+ }
3391
+ });
3392
+ router.get('/imessage/search', (_req, res) => {
3393
+ if (!ctx.imessage) {
3394
+ res.status(503).json({ error: 'iMessage not configured' });
3395
+ return;
3396
+ }
3397
+ const query = (_req.query.q || '').trim();
3398
+ if (!query) {
3399
+ res.status(400).json({ error: 'q parameter required' });
3400
+ return;
3401
+ }
3402
+ const limit = parseInt(_req.query.limit) || 50;
3403
+ try {
3404
+ const results = ctx.imessage.messageLogger.search({ query, limit });
3405
+ res.json({ results, count: results.length });
3406
+ }
3407
+ catch (err) {
3408
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
3409
+ }
3410
+ });
3256
3411
  // ── Relationships ─────────────────────────────────────────────────
3257
3412
  router.get('/relationships', (req, res) => {
3258
3413
  if (!ctx.relationships) {