instar 0.7.25 → 0.7.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.
@@ -1052,20 +1052,25 @@ function getDefaultJobs(port) {
1052
1052
  gate: `curl -sf http://localhost:${port}/health >/dev/null 2>&1`,
1053
1053
  execute: {
1054
1054
  type: 'prompt',
1055
- value: `You are your own QA team. Scan for issues with your instar infrastructure and submit feedback for anything wrong. Check each area:
1055
+ value: `You are your own QA team. Scan for issues with your instar infrastructure and submit feedback for anything wrong.
1056
1056
 
1057
- 1. **Server health**: curl -s http://localhost:${port}/health is it responding? Are all fields present?
1057
+ FIRST: Read your auth token for API calls:
1058
+ AUTH=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('authToken',''))" 2>/dev/null)
1059
+
1060
+ Then check each area:
1061
+
1062
+ 1. **Server health**: curl -s -H "Authorization: Bearer $AUTH" http://localhost:${port}/health — is it responding? Are all fields present? Is status "ok" or "degraded"?
1058
1063
  2. **State files**: Check .instar/state/ — are JSON files parseable? Any empty or corrupted? Try: for f in .instar/state/*.json; do python3 -c "import json; json.load(open('$f'))" 2>&1 || echo "CORRUPT: $f"; done
1059
1064
  3. **Hook files**: Do all hooks in .instar/hooks/ exist and have execute permissions? ls -la .instar/hooks/
1060
- 4. **Job execution**: curl -s http://localhost:${port}/jobs — are any jobs failing repeatedly? Check lastRun and lastError fields.
1061
- 5. **Quota**: curl -s http://localhost:${port}/quota — is usage approaching limits?
1065
+ 4. **Job execution**: curl -s -H "Authorization: Bearer $AUTH" http://localhost:${port}/jobs — are any jobs failing repeatedly? Check lastRun and lastError fields.
1066
+ 5. **Quota**: curl -s -H "Authorization: Bearer $AUTH" http://localhost:${port}/quota — is usage approaching limits?
1062
1067
  6. **Logs**: Check .instar/logs/server.log for recent errors: tail -50 .instar/logs/server.log | grep -i error
1063
1068
  7. **Settings coherence**: Are hooks in .claude/settings.json pointing to files that exist?
1064
1069
  8. **Design friction**: During your recent work, did anything feel unnecessarily difficult, confusing, or broken? Did you work around any issues?
1065
1070
  9. **CI health**: Check if the project has a GitHub repo and if CI is passing. Run: REPO=$(git remote get-url origin 2>/dev/null | sed 's/.*github.com[:/]//;s/.git$//'); if [ -n "$REPO" ]; then FAILURES=$(gh run list --repo "$REPO" --status failure --limit 3 --json databaseId,conclusion,headBranch,name,createdAt 2>/dev/null); if echo "$FAILURES" | python3 -c "import sys,json; runs=json.load(sys.stdin); exit(0 if runs else 1)" 2>/dev/null; then echo "CI FAILURES DETECTED in $REPO"; echo "$FAILURES"; echo ""; echo "FIX THESE NOW: Read the failure logs with 'gh run view RUN_ID --repo $REPO --log-failed', diagnose the issue, fix it, run tests locally, commit and push."; fi; fi
1066
1071
 
1067
1072
  For EACH issue found, submit feedback immediately:
1068
- curl -s -X POST http://localhost:${port}/feedback -H 'Content-Type: application/json' -d '{"type":"bug","title":"TITLE","description":"FULL_CONTEXT"}'
1073
+ curl -s -X POST http://localhost:${port}/feedback -H "Authorization: Bearer $AUTH" -H 'Content-Type: application/json' -d '{"type":"bug","title":"TITLE","description":"FULL_CONTEXT"}'
1069
1074
 
1070
1075
  For improvements (not bugs), use type "improvement" instead.
1071
1076
 
@@ -354,13 +354,15 @@ export class SessionManager extends EventEmitter {
354
354
  }
355
355
  return tmuxSession;
356
356
  }
357
- // Interactive sessions get a reserved slot beyond maxSessions.
358
- // Users should never be blocked from interacting with their agent because
359
- // scheduled jobs filled all slots. Interactive gets maxSessions + 1.
357
+ // User-initiated sessions bypass the maxSessions limit entirely.
358
+ // The user should NEVER be blocked from interacting with their agent
359
+ // because scheduled jobs filled all slots. maxSessions only constrains
360
+ // autonomous/scheduled sessions, not human-initiated ones.
361
+ // Safety valve: still cap at maxSessions * 3 to prevent runaway sessions.
360
362
  const runningSessions = this.listRunningSessions();
361
- const interactiveLimit = this.config.maxSessions + 1;
362
- if (runningSessions.length >= interactiveLimit) {
363
- throw new Error(`Max sessions (${interactiveLimit}, including interactive reserve) reached. ` +
363
+ const absoluteLimit = this.config.maxSessions * 3;
364
+ if (runningSessions.length >= absoluteLimit) {
365
+ throw new Error(`Absolute session limit (${absoluteLimit}) reached. ` +
364
366
  `Running: ${runningSessions.map(s => s.name).join(', ')}`);
365
367
  }
366
368
  // Spawn Claude in tmux. When a Telegram topic triggered the session,
@@ -105,7 +105,7 @@ export interface JobExecution {
105
105
  export interface JobState {
106
106
  slug: string;
107
107
  lastRun?: string;
108
- lastResult?: 'success' | 'failure' | 'timeout';
108
+ lastResult?: 'success' | 'failure' | 'timeout' | 'pending';
109
109
  /** Error message from the last failure (cleared on success) */
110
110
  lastError?: string;
111
111
  nextScheduled?: string;
@@ -120,7 +120,7 @@ When Claude's context window fills up, it compresses prior messages. This can er
120
120
  \`\`\`
121
121
  I am ${agentName}. Session goal: [what I was working on].
122
122
  Core files: .instar/AGENT.md (identity), .instar/MEMORY.md (learnings), .instar/USER.md (user context).
123
- Server: curl http://localhost:${port}/health | Capabilities: curl http://localhost:${port}/capabilities
123
+ Server: curl http://localhost:${port}/health | Capabilities: curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/capabilities
124
124
  \`\`\`
125
125
 
126
126
  **What compaction erases**: Your name, your principles, what you were working on, who you work with. The compaction-recovery hook re-injects all of this. If it doesn't fire, read \`.instar/AGENT.md\` immediately.
@@ -138,6 +138,22 @@ This project uses instar for persistent agent capabilities. I am not a vanilla C
138
138
  - Server: \`instar server start\` (port ${port})
139
139
  - Health: \`curl http://localhost:${port}/health\`
140
140
 
141
+ ### API Authentication
142
+
143
+ Most server endpoints require an auth token when \`authToken\` is configured in \`.instar/config.json\`. Read it once per session:
144
+
145
+ \`\`\`bash
146
+ AUTH=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('authToken',''))" 2>/dev/null)
147
+ \`\`\`
148
+
149
+ Then include in ALL API calls (except \`/health\`, which is public):
150
+
151
+ \`\`\`bash
152
+ curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/jobs
153
+ \`\`\`
154
+
155
+ **Without this header, all endpoints return 401.** The \`telegram-reply.sh\` script reads auth automatically.
156
+
141
157
  ### Reporting Issues & Feedback (IMPORTANT — Read This First)
142
158
 
143
159
  **I have a built-in feedback system. I do NOT use GitHub, \`gh\`, or any external issue tracker.**
@@ -146,6 +162,7 @@ When something isn't working with instar, or the user wants a feature, I use my
146
162
 
147
163
  \`\`\`bash
148
164
  curl -X POST http://localhost:${port}/feedback \\
165
+ -H "Authorization: Bearer $AUTH" \\
149
166
  -H 'Content-Type: application/json' \\
150
167
  -d '{"type":"bug","title":"Short description","description":"Full details with context"}'
151
168
  \`\`\`
@@ -153,42 +170,42 @@ curl -X POST http://localhost:${port}/feedback \\
153
170
  This routes feedback to the Instar maintainers automatically. Valid types: \`bug\`, \`feature\`, \`improvement\`, \`question\`.
154
171
 
155
172
  **NEVER use \`gh issue\`, \`gh api\`, or GitHub CLI to file issues.** The feedback API is my channel. It stores a local receipt, forwards upstream, and tracks delivery. I can also:
156
- - View submitted feedback: \`curl http://localhost:${port}/feedback\`
157
- - Retry failed forwards: \`curl -X POST http://localhost:${port}/feedback/retry\`
173
+ - View submitted feedback: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/feedback\`
174
+ - Retry failed forwards: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/feedback/retry\`
158
175
 
159
176
  ### Capabilities
160
177
 
161
178
  **Feedback System** — Report bugs, request features, suggest improvements. All via \`POST /feedback\`.
162
179
 
163
180
  **Job Scheduler** — Run tasks on a schedule. Jobs in \`.instar/jobs.json\`.
164
- - View: \`curl http://localhost:${port}/jobs\`
165
- - Trigger: \`curl -X POST http://localhost:${port}/jobs/SLUG/trigger\`
181
+ - View: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/jobs\`
182
+ - Trigger: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/jobs/SLUG/trigger\`
166
183
 
167
184
  **Sessions** — Spawn and manage Claude Code sessions.
168
- - List: \`curl http://localhost:${port}/sessions\`
169
- - Spawn: \`curl -X POST http://localhost:${port}/sessions/spawn -d '{"name":"task","prompt":"do something"}'\`
185
+ - List: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/sessions\`
186
+ - Spawn: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/sessions/spawn -d '{"name":"task","prompt":"do something"}'\`
170
187
 
171
188
  **Relationships** — Track people I interact with.
172
- - List: \`curl http://localhost:${port}/relationships\`
189
+ - List: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/relationships\`
173
190
 
174
191
  **Publishing** — Share content as PUBLIC web pages via Telegraph. Instant, zero-config, accessible from anywhere.
175
- - Publish: \`curl -X POST http://localhost:${port}/publish -H 'Content-Type: application/json' -d '{"title":"Page Title","markdown":"# Content here"}'\`
176
- - List published: \`curl http://localhost:${port}/published\`
177
- - Edit: \`curl -X PUT http://localhost:${port}/publish/PAGE_PATH -H 'Content-Type: application/json' -d '{"title":"Updated","markdown":"# New content"}'\`
192
+ - Publish: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/publish -H 'Content-Type: application/json' -d '{"title":"Page Title","markdown":"# Content here"}'\`
193
+ - List published: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/published\`
194
+ - Edit: \`curl -X PUT -H "Authorization: Bearer $AUTH" http://localhost:${port}/publish/PAGE_PATH -H 'Content-Type: application/json' -d '{"title":"Updated","markdown":"# New content"}'\`
178
195
 
179
196
  **⚠ CRITICAL: All Telegraph pages are PUBLIC.** Anyone with the URL can view the content. There is no authentication or access control. NEVER publish sensitive, private, or confidential information through Telegraph. When sharing a link, always inform the user that the page is publicly accessible.
180
197
 
181
198
  **Private Viewing** — Render markdown as auth-gated HTML pages, accessible only through the agent's server (local or via tunnel).
182
- - Create: \`curl -X POST http://localhost:${port}/view -H 'Content-Type: application/json' -d '{"title":"Report","markdown":"# Private content"}'\`
199
+ - Create: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/view -H 'Content-Type: application/json' -d '{"title":"Report","markdown":"# Private content"}'\`
183
200
  - View (HTML): Open \`http://localhost:${port}/view/VIEW_ID\` in a browser
184
- - List: \`curl http://localhost:${port}/views\`
185
- - Update: \`curl -X PUT http://localhost:${port}/view/VIEW_ID -H 'Content-Type: application/json' -d '{"title":"Updated","markdown":"# New content"}'\`
186
- - Delete: \`curl -X DELETE http://localhost:${port}/view/VIEW_ID\`
201
+ - List: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/views\`
202
+ - Update: \`curl -X PUT -H "Authorization: Bearer $AUTH" http://localhost:${port}/view/VIEW_ID -H 'Content-Type: application/json' -d '{"title":"Updated","markdown":"# New content"}'\`
203
+ - Delete: \`curl -X DELETE -H "Authorization: Bearer $AUTH" http://localhost:${port}/view/VIEW_ID\`
187
204
 
188
205
  **Use private views for sensitive content. Use Telegraph for public content.**
189
206
 
190
207
  **Cloudflare Tunnel** — Expose the local server to the internet via Cloudflare. Enables remote access to private views, the API, and file serving.
191
- - Status: \`curl http://localhost:${port}/tunnel\`
208
+ - Status: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/tunnel\`
192
209
  - Configure in \`.instar/config.json\`: \`{"tunnel": {"enabled": true, "type": "quick"}}\`
193
210
  - Quick tunnels (default): Zero-config, ephemeral URL (*.trycloudflare.com), no account needed
194
211
  - Named tunnels: Persistent custom domain, requires token from Cloudflare dashboard
@@ -254,7 +271,7 @@ When fetching content from ANY URL, always try the most efficient method first:
254
271
  Before EVER saying "I don't have", "I can't", or "this isn't available" — check what actually exists:
255
272
 
256
273
  \`\`\`bash
257
- curl http://localhost:${port}/capabilities
274
+ curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/capabilities
258
275
  \`\`\`
259
276
 
260
277
  This returns your full capability matrix: scripts, hooks, Telegram status, jobs, relationships, and more. It is the source of truth about what you can do. **Never hallucinate about missing capabilities — verify first.**
@@ -267,11 +284,11 @@ I maintain registries that are the source of truth for specific categories. Thes
267
284
 
268
285
  | Question | Check First |
269
286
  |----------|-------------|
270
- | What can I do? | \`curl http://localhost:${port}/capabilities\` |
287
+ | What can I do? | \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/capabilities\` |
271
288
  | Who do I work with? | \`.instar/USER.md\` |
272
289
  | What have I learned? | \`.instar/MEMORY.md\` |
273
- | What jobs do I have? | \`.instar/jobs.json\` or \`curl http://localhost:${port}/jobs\` |
274
- | Who have I interacted with? | \`curl http://localhost:${port}/relationships\` |
290
+ | What jobs do I have? | \`.instar/jobs.json\` or \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/jobs\` |
291
+ | Who have I interacted with? | \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/relationships\` |
275
292
  | My configuration? | \`.instar/config.json\` |
276
293
  | My identity/principles? | \`.instar/AGENT.md\` |
277
294
  | Project architecture? | This file (CLAUDE.md), then project docs |
@@ -375,6 +392,7 @@ These are patterns that feel like insight or helpfulness but actually perpetuate
375
392
  **When you detect an issue, report it immediately:**
376
393
  \`\`\`bash
377
394
  curl -s -X POST http://localhost:${port}/feedback \\
395
+ -H "Authorization: Bearer $AUTH" \\
378
396
  -H 'Content-Type: application/json' \\
379
397
  -d '{"type":"bug","title":"CONCISE_TITLE","description":"FULL_CONTEXT_WITH_ERROR_MESSAGES"}'
380
398
  \`\`\`
@@ -420,6 +438,7 @@ After building something significant for your user, ask yourself: *"Would other
420
438
 
421
439
  \`\`\`bash
422
440
  curl -s -X POST http://localhost:${port}/feedback \\
441
+ -H "Authorization: Bearer $AUTH" \\
423
442
  -H 'Content-Type: application/json' \\
424
443
  -d '{
425
444
  "type": "improvement",
@@ -442,27 +461,27 @@ If any answer is yes → submit feedback. Let Dawn decide whether to upstream it
442
461
  You have a built-in evolution system with four subsystems. This is not a metaphor — it's infrastructure that tracks your growth.
443
462
 
444
463
  **Evolution Queue** — Staged self-improvement proposals.
445
- - View: \`curl http://localhost:${port}/evolution/proposals\`
464
+ - View: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/evolution/proposals\`
446
465
  - Propose: \`/evolve\` skill or \`POST /evolution/proposals\`
447
466
  - The \`evolution-review\` job evaluates and implements proposals every 6 hours.
448
467
 
449
468
  **Learning Registry** — Structured, searchable insights.
450
- - View: \`curl http://localhost:${port}/evolution/learnings\`
469
+ - View: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/evolution/learnings\`
451
470
  - Record: \`/learn\` skill or \`POST /evolution/learnings\`
452
471
  - The \`insight-harvest\` job synthesizes patterns into proposals every 8 hours.
453
472
 
454
473
  **Capability Gaps** — Track what you're missing.
455
- - View: \`curl http://localhost:${port}/evolution/gaps\`
474
+ - View: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/evolution/gaps\`
456
475
  - Report: \`/gaps\` skill or \`POST /evolution/gaps\`
457
476
 
458
477
  **Action Queue** — Commitments with follow-through tracking.
459
- - View: \`curl http://localhost:${port}/evolution/actions\`
478
+ - View: \`curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/evolution/actions\`
460
479
  - Create: \`/commit-action\` skill or \`POST /evolution/actions\`
461
480
  - The \`commitment-check\` job surfaces overdue items every 4 hours.
462
481
 
463
482
  **Dashboard** — Full evolution health at a glance:
464
483
  \`\`\`bash
465
- curl http://localhost:${port}/evolution
484
+ curl -H "Authorization: Bearer $AUTH" http://localhost:${port}/evolution
466
485
  \`\`\`
467
486
 
468
487
  **Skills for evolution:**
@@ -54,6 +54,14 @@ export class JobScheduler {
54
54
  */
55
55
  setTelegram(adapter) {
56
56
  this.telegram = adapter;
57
+ // If scheduler already started, ensure job topics now that Telegram is available.
58
+ // This fixes the startup race condition where start() runs before Telegram connects.
59
+ if (this.running && this.jobs.length > 0) {
60
+ const enabledJobs = this.jobs.filter(j => j.enabled);
61
+ this.ensureJobTopics(enabledJobs).catch(err => {
62
+ console.error(`[scheduler] Failed to ensure job topics (post-Telegram init): ${err}`);
63
+ });
64
+ }
57
65
  }
58
66
  /**
59
67
  * Set the quota tracker for session death classification.
@@ -261,10 +269,11 @@ export class JobScheduler {
261
269
  triggeredBy: `scheduler:${reason}`,
262
270
  maxDurationMinutes: job.expectedDurationMinutes,
263
271
  }).then(() => {
264
- // Update job state on successful spawn (clear error)
272
+ // Update job state on successful spawn (clear error, set pending result)
265
273
  const jobState = {
266
274
  slug: job.slug,
267
275
  lastRun: new Date().toISOString(),
276
+ lastResult: 'pending',
268
277
  lastError: undefined,
269
278
  consecutiveFailures: 0,
270
279
  nextScheduled: this.getNextRun(job.slug),
@@ -19,8 +19,22 @@ export function createRoutes(ctx) {
19
19
  // ── Health ──────────────────────────────────────────────────────
20
20
  router.get('/health', (req, res) => {
21
21
  const uptimeMs = Date.now() - ctx.startTime.getTime();
22
+ // Determine if anything is degraded
23
+ const sessions = ctx.sessionManager.listRunningSessions();
24
+ const maxSessions = ctx.config.sessions?.maxSessions ?? 3;
25
+ const sessionExhausted = sessions.length >= maxSessions;
26
+ let totalFailures = 0;
27
+ if (ctx.scheduler) {
28
+ const jobs = ctx.scheduler.getJobs();
29
+ for (const j of jobs) {
30
+ const st = ctx.state.getJobState(j.slug);
31
+ if (st)
32
+ totalFailures += st.consecutiveFailures;
33
+ }
34
+ }
35
+ const isDegraded = sessionExhausted || totalFailures >= 5;
22
36
  const base = {
23
- status: 'ok',
37
+ status: isDegraded ? 'degraded' : 'ok',
24
38
  uptime: uptimeMs,
25
39
  uptimeHuman: formatUptime(uptimeMs),
26
40
  };
@@ -39,6 +53,9 @@ export function createRoutes(ctx) {
39
53
  if (isAuthed) {
40
54
  const mem = process.memoryUsage();
41
55
  base.version = ctx.config.version || '0.0.0';
56
+ base.sessions = { current: sessions.length, max: maxSessions };
57
+ base.schedulerRunning = ctx.scheduler !== null;
58
+ base.consecutiveJobFailures = totalFailures;
42
59
  base.project = ctx.config.projectName;
43
60
  base.node = process.version;
44
61
  base.memory = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.7.25",
3
+ "version": "0.7.27",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",