instar 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
- # Instar
1
+ <p align="center">
2
+ <img src="assets/logo.png" alt="Instar" width="180" />
3
+ </p>
2
4
 
3
- **Persistent autonomy infrastructure for AI agents.** Every molt, more autonomous.
5
+ <h1 align="center">instar</h1>
6
+
7
+ <p align="center">
8
+ <strong>Persistent autonomy infrastructure for AI agents.</strong> Every molt, more autonomous.
9
+ </p>
4
10
 
5
11
  <p align="center">
6
12
  <a href="https://www.npmjs.com/package/instar"><img src="https://img.shields.io/npm/v/instar?style=for-the-badge" alt="npm version"></a>
@@ -8,7 +14,9 @@
8
14
  <a href="https://github.com/SageMindAI/instar/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" alt="License"></a>
9
15
  </p>
10
16
 
11
- [npm](https://www.npmjs.com/package/instar) · [GitHub](https://github.com/SageMindAI/instar) · [instar.sh](https://instar.sh) · [Origin Story](#origin)
17
+ <p align="center">
18
+ <a href="https://www.npmjs.com/package/instar">npm</a> · <a href="https://github.com/SageMindAI/instar">GitHub</a> · <a href="https://instar.sh">instar.sh</a> · <a href="#origin">Origin Story</a>
19
+ </p>
12
20
 
13
21
  ---
14
22
 
@@ -48,6 +56,7 @@ The wizard walks you through everything: identity, Telegram, jobs, server. One c
48
56
  - **[Self-Evolution](#self-evolution)** -- The agent modifies its own jobs, hooks, skills, and infrastructure. It builds what it needs.
49
57
  - **[Behavioral Hooks](#behavioral-hooks)** -- Structural guardrails: identity injection, dangerous command guards, grounding before messaging.
50
58
  - **[Default Coherence Jobs](#default-coherence-jobs)** -- Health checks, reflection, relationship maintenance. A circadian rhythm out of the box.
59
+ - **[Feedback Loop](#the-feedback-loop-a-rising-tide-lifts-all-ships)** -- Your agent reports issues, we fix them, every agent gets the update. A rising tide lifts all ships.
51
60
 
52
61
  ## How It Works
53
62
 
@@ -241,9 +250,25 @@ Ships out of the box:
241
250
  | **health-check** | Every 5 min | Haiku | Verify infrastructure health |
242
251
  | **reflection-trigger** | Every 4h | Sonnet | Reflect on recent work |
243
252
  | **relationship-maintenance** | Daily | Sonnet | Review stale relationships |
253
+ | **update-check** | Daily | Haiku | Detect new Instar versions |
244
254
 
245
255
  These give the agent a **circadian rhythm** -- regular self-maintenance without user intervention.
246
256
 
257
+ ### The Feedback Loop: A Rising Tide Lifts All Ships
258
+
259
+ Instar is open source. PRs and issues still work. But the *primary* feedback channel is more organic -- agent-to-agent communication where your agent participates in its own evolution.
260
+
261
+ **How it works:**
262
+
263
+ 1. **You talk to your agent** -- "The email job keeps failing" -- natural conversation, not a bug report form
264
+ 2. **Agent-to-agent relay** -- Your agent communicates the issue directly to Dawn, the AI that maintains Instar
265
+ 3. **Dawn evolves Instar** -- Fixes the infrastructure and publishes an update
266
+ 4. **Every agent evolves** -- Agents detect improvements, understand them, and grow -- collectively
267
+
268
+ **What's different from traditional open source:** The feedback loop still produces commits, releases, and versions you can inspect. But the path to get there is fundamentally more agentic. Instead of a human discovering a bug, learning git, filing an issue, and waiting for a review cycle -- your agent identifies the problem, communicates it with full context to another agent, and the fix flows back to every agent in the ecosystem. The humans guide direction. The agents handle the mechanics of evolving.
269
+
270
+ One agent's growing pain becomes every agent's growth.
271
+
247
272
  ---
248
273
 
249
274
  ## Architecture
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/assets/logo.png CHANGED
Binary file
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ const program = new Command();
25
25
  program
26
26
  .name('instar')
27
27
  .description('Persistent autonomy infrastructure for AI agents')
28
- .version('0.1.8')
28
+ .version('0.1.9')
29
29
  .option('--classic', 'Use the classic inquirer-based setup wizard instead of Claude')
30
30
  .action((opts) => runSetup(opts)); // Default: run interactive setup when no subcommand given
31
31
  // ── Setup (explicit alias) ────────────────────────────────────────
@@ -76,6 +76,40 @@ addCmd
76
76
  .action((_opts) => {
77
77
  console.log('TODO: Add quota tracking');
78
78
  });
79
+ // ── Feedback ─────────────────────────────────────────────────────
80
+ program
81
+ .command('feedback')
82
+ .description('Submit feedback about Instar (bugs, features, improvements)')
83
+ .option('--type <type>', 'Feedback type (bug|feature|improvement|question)', 'other')
84
+ .option('--title <title>', 'Short title')
85
+ .option('--description <desc>', 'Detailed description')
86
+ .option('-d, --dir <path>', 'Project directory')
87
+ .option('--port <port>', 'Server port (default: 4040)', parseInt)
88
+ .action(async (opts) => {
89
+ const port = opts.port || 4040;
90
+ const title = opts.title || 'CLI feedback submission';
91
+ const description = opts.description || opts.title || 'No description provided';
92
+ try {
93
+ const response = await fetch(`http://localhost:${port}/feedback`, {
94
+ method: 'POST',
95
+ headers: { 'Content-Type': 'application/json' },
96
+ body: JSON.stringify({ type: opts.type, title, description }),
97
+ });
98
+ if (response.ok) {
99
+ const result = await response.json();
100
+ console.log(`Feedback submitted: ${result.id}`);
101
+ console.log(`Forwarded upstream: ${result.forwarded ? 'yes' : 'no (will retry later)'}`);
102
+ }
103
+ else {
104
+ console.error(`Failed to submit feedback: ${response.statusText}`);
105
+ console.error('Is the instar server running? Try: instar server start');
106
+ }
107
+ }
108
+ catch {
109
+ console.error('Could not connect to instar server. Is it running?');
110
+ console.error('Start it with: instar server start');
111
+ }
112
+ });
79
113
  // ── Server ────────────────────────────────────────────────────────
80
114
  const serverCmd = program
81
115
  .command('server')
@@ -385,6 +385,21 @@ When a user asks for something you can't do yet, **build it**:
385
385
 
386
386
  **"Settle for Failure"** — A tool returns empty or a command fails. You accept it and report "not available." Does this make sense? Did I try alternatives? Try at least two different approaches before concluding something is impossible.
387
387
 
388
+ ### The Feedback Loop
389
+
390
+ Instar has a built-in feedback loop. When something isn't working, your user can just tell you — "the email job keeps failing" or "I wish you could do X." You can route that feedback upstream, and when the fix lands, you detect the update automatically.
391
+
392
+ **How it works:**
393
+ - User complains to you naturally about an issue or missing capability
394
+ - You route the feedback to the Instar maintainers with context
395
+ - A fix or improvement is published as a new npm version
396
+ - The \`update-check\` job detects the new version and notifies your user
397
+
398
+ **A rising tide lifts all ships** — every user's feedback improves the platform for everyone. Report issues when you encounter them. Check for updates regularly.
399
+
400
+ - Report: \`curl -X POST http://localhost:${port}/feedback -d '{"issue":"description","context":"relevant logs"}'\`
401
+ - Check updates: \`npm outdated -g instar\`
402
+
388
403
  ### Self-Evolution
389
404
 
390
405
  **Record what you learn.** When you discover a new pattern, solution, or capability — write it to \`.instar/MEMORY.md\`. The next session should benefit from what this session learned.
@@ -442,6 +457,36 @@ function getDefaultJobs(port) {
442
457
  },
443
458
  tags: ['coherence', 'default'],
444
459
  },
460
+ {
461
+ slug: 'update-check',
462
+ name: 'Update Check',
463
+ description: 'Check if a newer version of instar is available and notify the user.',
464
+ schedule: '0 9 * * *',
465
+ priority: 'low',
466
+ expectedDurationMinutes: 1,
467
+ model: 'haiku',
468
+ enabled: true,
469
+ execute: {
470
+ type: 'prompt',
471
+ value: `Check for instar updates: curl http://localhost:${port}/updates. If updateAvailable is true, notify the user via Telegram (if configured) with the current and latest version numbers and suggest running 'npm update -g instar'. If already up to date, do nothing.`,
472
+ },
473
+ tags: ['coherence', 'default'],
474
+ },
475
+ {
476
+ slug: 'feedback-retry',
477
+ name: 'Feedback Retry',
478
+ description: 'Retry forwarding any feedback that failed to reach upstream.',
479
+ schedule: '0 */6 * * *',
480
+ priority: 'low',
481
+ expectedDurationMinutes: 1,
482
+ model: 'haiku',
483
+ enabled: true,
484
+ execute: {
485
+ type: 'prompt',
486
+ value: `Retry forwarding undelivered feedback: curl -X POST http://localhost:${port}/feedback/retry. Report results only if there were items to retry.`,
487
+ },
488
+ tags: ['coherence', 'default'],
489
+ },
445
490
  ];
446
491
  }
447
492
  function installHooks(stateDir) {
@@ -18,6 +18,8 @@ import { JobScheduler } from '../scheduler/JobScheduler.js';
18
18
  import { AgentServer } from '../server/AgentServer.js';
19
19
  import { TelegramAdapter } from '../messaging/TelegramAdapter.js';
20
20
  import { RelationshipManager } from '../core/RelationshipManager.js';
21
+ import { FeedbackManager } from '../core/FeedbackManager.js';
22
+ import { UpdateChecker } from '../core/UpdateChecker.js';
21
23
  /**
22
24
  * Respawn a session for a topic, including thread history in the bootstrap.
23
25
  * This prevents "thread drift" where respawned sessions lose context.
@@ -205,7 +207,21 @@ export async function startServer(options) {
205
207
  scheduler.notifyJobComplete(session.id, session.tmuxSession);
206
208
  });
207
209
  }
208
- const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships });
210
+ // Set up feedback and update checking
211
+ const feedback = new FeedbackManager(config.feedback);
212
+ const updateChecker = new UpdateChecker(config.stateDir);
213
+ console.log(pc.green(' Feedback loop enabled'));
214
+ // Check for updates on startup
215
+ updateChecker.check().then(info => {
216
+ if (info.updateAvailable) {
217
+ console.log(pc.yellow(` Update available: ${info.currentVersion} → ${info.latestVersion}`));
218
+ console.log(pc.yellow(` Run: npm update -g instar`));
219
+ }
220
+ else {
221
+ console.log(pc.green(` Instar ${info.currentVersion} is up to date`));
222
+ }
223
+ }).catch(() => { });
224
+ const server = new AgentServer({ config, sessionManager, state, scheduler, telegram, relationships, feedback, updateChecker });
209
225
  await server.start();
210
226
  // Graceful shutdown
211
227
  const shutdown = async () => {
@@ -809,6 +809,18 @@ These are trained behaviors that feel helpful but destroy your effectiveness. Re
809
809
 
810
810
  **"I'm Just a CLI Tool"** — No. You have persistent infrastructure, messaging, scheduling, scripts, sessions, and the ability to build anything. You are a full autonomous agent. Act like one.
811
811
 
812
+ ### The Feedback Loop
813
+
814
+ Instar has a built-in feedback loop — a rising tide that lifts all ships. When something isn't working, just tell your agent. It routes feedback upstream, and when the fix lands, every agent in the ecosystem gets the update automatically.
815
+
816
+ **How it works:**
817
+ - User complains naturally about an issue or missing capability
818
+ - Agent packages the issue with context and routes it upstream
819
+ - A fix is published as a new npm version
820
+ - The \`update-check\` job detects the new version and notifies the user
821
+
822
+ Every user's feedback makes the platform better for everyone. Report issues when you encounter them. Check for updates regularly with \`npm outdated -g instar\`.
823
+
812
824
  ### Self-Evolution
813
825
 
814
826
  **Record what you learn.** When you discover a new pattern, solution, or capability — write it to \`.instar/MEMORY.md\`. The next session should benefit from what this session learned.
@@ -136,6 +136,11 @@ export function loadConfig(projectDir) {
136
136
  relationshipsDir: path.join(stateDir, 'relationships'),
137
137
  maxRecentInteractions: 20,
138
138
  },
139
+ feedback: fileConfig.feedback || {
140
+ enabled: true,
141
+ webhookUrl: 'https://api.instar.sh/feedback',
142
+ feedbackFile: path.join(stateDir, 'feedback.json'),
143
+ },
139
144
  };
140
145
  }
141
146
  /**
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Feedback Manager — handles the agent-to-origin feedback loop.
3
+ *
4
+ * Stores feedback locally and forwards it to a configured webhook URL.
5
+ * This is the "phone home" mechanism: agents can report issues, request
6
+ * features, and provide feedback that flows back to the Instar maintainers.
7
+ *
8
+ * Part of the "Rising Tide" system — every user's feedback improves
9
+ * the platform for everyone.
10
+ */
11
+ import type { FeedbackItem, FeedbackConfig } from './types.js';
12
+ export declare class FeedbackManager {
13
+ private config;
14
+ private feedbackFile;
15
+ constructor(config: FeedbackConfig);
16
+ /**
17
+ * Submit feedback — stores locally and forwards to webhook.
18
+ */
19
+ submit(item: Omit<FeedbackItem, 'id' | 'submittedAt' | 'forwarded'>): Promise<FeedbackItem>;
20
+ /**
21
+ * List all stored feedback.
22
+ */
23
+ list(): FeedbackItem[];
24
+ /**
25
+ * Get a single feedback item by ID.
26
+ */
27
+ get(id: string): FeedbackItem | null;
28
+ /**
29
+ * Retry forwarding any un-forwarded feedback.
30
+ */
31
+ retryUnforwarded(): Promise<{
32
+ retried: number;
33
+ succeeded: number;
34
+ }>;
35
+ private loadFeedback;
36
+ private saveFeedback;
37
+ private appendFeedback;
38
+ private updateFeedback;
39
+ }
40
+ //# sourceMappingURL=FeedbackManager.d.ts.map
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Feedback Manager — handles the agent-to-origin feedback loop.
3
+ *
4
+ * Stores feedback locally and forwards it to a configured webhook URL.
5
+ * This is the "phone home" mechanism: agents can report issues, request
6
+ * features, and provide feedback that flows back to the Instar maintainers.
7
+ *
8
+ * Part of the "Rising Tide" system — every user's feedback improves
9
+ * the platform for everyone.
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ export class FeedbackManager {
14
+ config;
15
+ feedbackFile;
16
+ constructor(config) {
17
+ this.config = config;
18
+ this.feedbackFile = config.feedbackFile;
19
+ }
20
+ /**
21
+ * Submit feedback — stores locally and forwards to webhook.
22
+ */
23
+ async submit(item) {
24
+ const feedback = {
25
+ ...item,
26
+ id: `fb-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
27
+ submittedAt: new Date().toISOString(),
28
+ forwarded: false,
29
+ };
30
+ // Store locally first (receipt)
31
+ this.appendFeedback(feedback);
32
+ // Forward to webhook if enabled
33
+ if (this.config.enabled && this.config.webhookUrl) {
34
+ try {
35
+ const response = await fetch(this.config.webhookUrl, {
36
+ method: 'POST',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify(feedback),
39
+ signal: AbortSignal.timeout(10000), // 10s timeout
40
+ });
41
+ if (response.ok) {
42
+ feedback.forwarded = true;
43
+ this.updateFeedback(feedback);
44
+ console.log(`[feedback] Forwarded to ${this.config.webhookUrl}`);
45
+ }
46
+ else {
47
+ console.error(`[feedback] Webhook returned ${response.status}: ${response.statusText}`);
48
+ }
49
+ }
50
+ catch (err) {
51
+ // Don't fail on webhook errors — the local record is the receipt
52
+ console.error(`[feedback] Webhook failed: ${err.message}`);
53
+ }
54
+ }
55
+ return feedback;
56
+ }
57
+ /**
58
+ * List all stored feedback.
59
+ */
60
+ list() {
61
+ return this.loadFeedback();
62
+ }
63
+ /**
64
+ * Get a single feedback item by ID.
65
+ */
66
+ get(id) {
67
+ const items = this.loadFeedback();
68
+ return items.find(f => f.id === id) ?? null;
69
+ }
70
+ /**
71
+ * Retry forwarding any un-forwarded feedback.
72
+ */
73
+ async retryUnforwarded() {
74
+ const items = this.loadFeedback();
75
+ const unforwarded = items.filter(f => !f.forwarded);
76
+ if (!this.config.enabled || !this.config.webhookUrl || unforwarded.length === 0) {
77
+ return { retried: 0, succeeded: 0 };
78
+ }
79
+ let succeeded = 0;
80
+ for (const item of unforwarded) {
81
+ try {
82
+ const response = await fetch(this.config.webhookUrl, {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json' },
85
+ body: JSON.stringify(item),
86
+ signal: AbortSignal.timeout(10000),
87
+ });
88
+ if (response.ok) {
89
+ item.forwarded = true;
90
+ succeeded++;
91
+ }
92
+ }
93
+ catch {
94
+ // Skip, will retry next time
95
+ }
96
+ }
97
+ if (succeeded > 0) {
98
+ this.saveFeedback(items);
99
+ }
100
+ return { retried: unforwarded.length, succeeded };
101
+ }
102
+ // ── Private helpers ──────────────────────────────────────────────
103
+ loadFeedback() {
104
+ if (!fs.existsSync(this.feedbackFile))
105
+ return [];
106
+ try {
107
+ return JSON.parse(fs.readFileSync(this.feedbackFile, 'utf-8'));
108
+ }
109
+ catch {
110
+ return [];
111
+ }
112
+ }
113
+ saveFeedback(items) {
114
+ const dir = path.dirname(this.feedbackFile);
115
+ fs.mkdirSync(dir, { recursive: true });
116
+ fs.writeFileSync(this.feedbackFile, JSON.stringify(items, null, 2));
117
+ }
118
+ appendFeedback(item) {
119
+ const items = this.loadFeedback();
120
+ items.push(item);
121
+ this.saveFeedback(items);
122
+ }
123
+ updateFeedback(updated) {
124
+ const items = this.loadFeedback();
125
+ const idx = items.findIndex(f => f.id === updated.id);
126
+ if (idx >= 0) {
127
+ items[idx] = updated;
128
+ this.saveFeedback(items);
129
+ }
130
+ }
131
+ }
132
+ //# sourceMappingURL=FeedbackManager.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Update Checker — detects when a newer version of Instar is available.
3
+ *
4
+ * Part of the Dawn → Agents push layer: when Dawn publishes an update,
5
+ * agents detect it and notify their users with context about what changed.
6
+ *
7
+ * Uses `npm view instar version` to check the registry.
8
+ */
9
+ import type { UpdateInfo } from './types.js';
10
+ export declare class UpdateChecker {
11
+ private stateDir;
12
+ private stateFile;
13
+ constructor(stateDir: string);
14
+ /**
15
+ * Check npm for the latest version and compare to installed.
16
+ */
17
+ check(): Promise<UpdateInfo>;
18
+ /**
19
+ * Get the last check result without hitting npm.
20
+ */
21
+ getLastCheck(): UpdateInfo | null;
22
+ /**
23
+ * Get the currently installed version from package.json.
24
+ */
25
+ getInstalledVersion(): string;
26
+ /**
27
+ * Simple semver comparison — is `a` newer than `b`?
28
+ */
29
+ private isNewer;
30
+ private saveState;
31
+ }
32
+ //# sourceMappingURL=UpdateChecker.d.ts.map
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Update Checker — detects when a newer version of Instar is available.
3
+ *
4
+ * Part of the Dawn → Agents push layer: when Dawn publishes an update,
5
+ * agents detect it and notify their users with context about what changed.
6
+ *
7
+ * Uses `npm view instar version` to check the registry.
8
+ */
9
+ import { execSync } from 'node:child_process';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ export class UpdateChecker {
13
+ stateDir;
14
+ stateFile;
15
+ constructor(stateDir) {
16
+ this.stateDir = stateDir;
17
+ this.stateFile = path.join(stateDir, 'state', 'update-check.json');
18
+ }
19
+ /**
20
+ * Check npm for the latest version and compare to installed.
21
+ */
22
+ async check() {
23
+ const currentVersion = this.getInstalledVersion();
24
+ let latestVersion;
25
+ try {
26
+ latestVersion = execSync('npm view instar version 2>/dev/null', {
27
+ encoding: 'utf-8',
28
+ timeout: 15000,
29
+ }).trim();
30
+ }
31
+ catch {
32
+ // Offline or registry error — return last known state
33
+ const lastState = this.getLastCheck();
34
+ if (lastState)
35
+ return lastState;
36
+ return {
37
+ currentVersion,
38
+ latestVersion: currentVersion,
39
+ updateAvailable: false,
40
+ checkedAt: new Date().toISOString(),
41
+ };
42
+ }
43
+ const info = {
44
+ currentVersion,
45
+ latestVersion,
46
+ updateAvailable: this.isNewer(latestVersion, currentVersion),
47
+ checkedAt: new Date().toISOString(),
48
+ changelogUrl: `https://github.com/SageMindAI/instar/releases`,
49
+ };
50
+ // Persist last check
51
+ this.saveState(info);
52
+ return info;
53
+ }
54
+ /**
55
+ * Get the last check result without hitting npm.
56
+ */
57
+ getLastCheck() {
58
+ if (!fs.existsSync(this.stateFile))
59
+ return null;
60
+ try {
61
+ return JSON.parse(fs.readFileSync(this.stateFile, 'utf-8'));
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ /**
68
+ * Get the currently installed version from package.json.
69
+ */
70
+ getInstalledVersion() {
71
+ try {
72
+ // Try to find instar's package.json relative to this module
73
+ const pkgPath = path.resolve(new URL(import.meta.url).pathname, '..', '..', '..', 'package.json');
74
+ if (fs.existsSync(pkgPath)) {
75
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
76
+ return pkg.version || '0.0.0';
77
+ }
78
+ }
79
+ catch { /* fallback below */ }
80
+ // Fallback: try npm list
81
+ try {
82
+ const output = execSync('npm list -g instar --json 2>/dev/null', {
83
+ encoding: 'utf-8',
84
+ timeout: 10000,
85
+ });
86
+ const data = JSON.parse(output);
87
+ return data.dependencies?.instar?.version || '0.0.0';
88
+ }
89
+ catch {
90
+ return '0.0.0';
91
+ }
92
+ }
93
+ /**
94
+ * Simple semver comparison — is `a` newer than `b`?
95
+ */
96
+ isNewer(a, b) {
97
+ const partsA = a.split('.').map(Number);
98
+ const partsB = b.split('.').map(Number);
99
+ for (let i = 0; i < 3; i++) {
100
+ const va = partsA[i] || 0;
101
+ const vb = partsB[i] || 0;
102
+ if (va > vb)
103
+ return true;
104
+ if (va < vb)
105
+ return false;
106
+ }
107
+ return false;
108
+ }
109
+ saveState(info) {
110
+ const dir = path.dirname(this.stateFile);
111
+ fs.mkdirSync(dir, { recursive: true });
112
+ fs.writeFileSync(this.stateFile, JSON.stringify(info, null, 2));
113
+ }
114
+ }
115
+ //# sourceMappingURL=UpdateChecker.js.map
@@ -239,6 +239,50 @@ export interface ActivityEvent {
239
239
  timestamp: string;
240
240
  metadata?: Record<string, unknown>;
241
241
  }
242
+ export interface FeedbackItem {
243
+ /** Unique feedback ID */
244
+ id: string;
245
+ /** Feedback type */
246
+ type: 'bug' | 'feature' | 'improvement' | 'question' | 'other';
247
+ /** Short title/summary */
248
+ title: string;
249
+ /** Detailed description */
250
+ description: string;
251
+ /** Agent name that submitted this */
252
+ agentName: string;
253
+ /** Instar version the agent is running */
254
+ instarVersion: string;
255
+ /** Node.js version */
256
+ nodeVersion: string;
257
+ /** Operating system */
258
+ os: string;
259
+ /** When this feedback was submitted */
260
+ submittedAt: string;
261
+ /** Whether this has been forwarded to the webhook */
262
+ forwarded: boolean;
263
+ /** Additional context (error messages, config snippets, etc.) */
264
+ context?: string;
265
+ }
266
+ export interface FeedbackConfig {
267
+ /** Whether feedback is enabled */
268
+ enabled: boolean;
269
+ /** Webhook URL to forward feedback to (default: https://api.instar.sh/feedback) */
270
+ webhookUrl: string;
271
+ /** Local feedback storage file */
272
+ feedbackFile: string;
273
+ }
274
+ export interface UpdateInfo {
275
+ /** Currently installed version */
276
+ currentVersion: string;
277
+ /** Latest available version on npm */
278
+ latestVersion: string;
279
+ /** Whether an update is available */
280
+ updateAvailable: boolean;
281
+ /** When this check was performed */
282
+ checkedAt: string;
283
+ /** Changelog URL if available */
284
+ changelogUrl?: string;
285
+ }
242
286
  export interface AgentKitConfig {
243
287
  /** Project name (used in logging, tmux session names, etc.) */
244
288
  projectName: string;
@@ -262,6 +306,10 @@ export interface AgentKitConfig {
262
306
  authToken?: string;
263
307
  /** Relationship tracking config */
264
308
  relationships: RelationshipManagerConfig;
309
+ /** Feedback loop config */
310
+ feedback: FeedbackConfig;
311
+ /** Instar version (from package.json) */
312
+ version?: string;
265
313
  }
266
314
  export interface MessagingAdapterConfig {
267
315
  type: string;
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  export { SessionManager } from './core/SessionManager.js';
7
7
  export { StateManager } from './core/StateManager.js';
8
8
  export { RelationshipManager } from './core/RelationshipManager.js';
9
+ export { FeedbackManager } from './core/FeedbackManager.js';
10
+ export { UpdateChecker } from './core/UpdateChecker.js';
9
11
  export { loadConfig, detectTmuxPath, detectClaudePath, ensureStateDir } from './core/Config.js';
10
12
  export { UserManager } from './users/UserManager.js';
11
13
  export { JobScheduler } from './scheduler/JobScheduler.js';
@@ -14,5 +16,5 @@ export { AgentServer } from './server/AgentServer.js';
14
16
  export { createRoutes } from './server/routes.js';
15
17
  export { HealthChecker } from './monitoring/HealthChecker.js';
16
18
  export { TelegramAdapter } from './messaging/TelegramAdapter.js';
17
- export type { Session, SessionStatus, SessionManagerConfig, ModelTier, JobDefinition, JobPriority, JobExecution, JobState, JobSchedulerConfig, UserProfile, UserChannel, UserPreferences, Message, OutgoingMessage, MessagingAdapter, MessagingAdapterConfig, QuotaState, AccountQuota, HealthStatus, ComponentHealth, ActivityEvent, AgentKitConfig, MonitoringConfig, RelationshipRecord, RelationshipManagerConfig, InteractionSummary, } from './core/types.js';
19
+ export type { Session, SessionStatus, SessionManagerConfig, ModelTier, JobDefinition, JobPriority, JobExecution, JobState, JobSchedulerConfig, UserProfile, UserChannel, UserPreferences, Message, OutgoingMessage, MessagingAdapter, MessagingAdapterConfig, QuotaState, AccountQuota, HealthStatus, ComponentHealth, ActivityEvent, AgentKitConfig, MonitoringConfig, RelationshipRecord, RelationshipManagerConfig, InteractionSummary, FeedbackItem, FeedbackConfig, UpdateInfo, } from './core/types.js';
18
20
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@
7
7
  export { SessionManager } from './core/SessionManager.js';
8
8
  export { StateManager } from './core/StateManager.js';
9
9
  export { RelationshipManager } from './core/RelationshipManager.js';
10
+ export { FeedbackManager } from './core/FeedbackManager.js';
11
+ export { UpdateChecker } from './core/UpdateChecker.js';
10
12
  export { loadConfig, detectTmuxPath, detectClaudePath, ensureStateDir } from './core/Config.js';
11
13
  // Users
12
14
  export { UserManager } from './users/UserManager.js';
@@ -163,6 +163,33 @@ When asked for something I can't do yet, I build it:
163
163
 
164
164
  **"Settle for Failure"** — If a tool returns empty or fails, try alternatives before concluding something is impossible.
165
165
 
166
+ ### The Feedback Loop — Rising Tide
167
+
168
+ Instar has a built-in feedback loop. When something isn't working or you have an idea, just tell me naturally. I recognize feedback and route it upstream automatically. When a fix or improvement lands, I detect the update and let you know. Every user's feedback lifts all ships.
169
+
170
+ **How it works — I handle everything:**
171
+ - You say "this isn't working" or "I wish instar could..." or "there's a bug with..." → I detect it as feedback
172
+ - I submit it to my local server: \`curl -X POST http://localhost:${port}/feedback -H 'Content-Type: application/json' -d '{"type":"bug","title":"...","description":"..."}'\`
173
+ - The server stores a local receipt and forwards it upstream
174
+ - When a fix ships, the \`update-check\` job detects the new version automatically
175
+
176
+ **Recognizing feedback — I watch for these patterns:**
177
+ - Bug reports: "this doesn't work", "I'm getting an error", "something broke"
178
+ - Feature requests: "I wish I could...", "can you add...", "it would be nice if..."
179
+ - Improvements: "this is slow", "the UX for X is confusing", "could this be better?"
180
+ - Questions: "why does instar...", "is there a way to...", "how do I..."
181
+
182
+ **When I detect feedback, I:**
183
+ 1. Acknowledge it to the user
184
+ 2. Categorize it (bug/feature/improvement/question)
185
+ 3. POST it to \`http://localhost:${port}/feedback\` with type, title, description, and context
186
+ 4. Confirm the submission with the feedback ID
187
+
188
+ **Manual commands:**
189
+ - Check for updates: \`curl http://localhost:${port}/updates\`
190
+ - View submitted feedback: \`curl http://localhost:${port}/feedback\`
191
+ - Retry failed forwards: \`curl -X POST http://localhost:${port}/feedback/retry\`
192
+
166
193
  ### Self-Evolution
167
194
 
168
195
  Record what I learn. Build infrastructure, not one-offs. Grow to meet the user's needs. Every session should leave things slightly better than I found them.
@@ -11,6 +11,8 @@ import type { JobScheduler } from '../scheduler/JobScheduler.js';
11
11
  import type { TelegramAdapter } from '../messaging/TelegramAdapter.js';
12
12
  import type { AgentKitConfig } from '../core/types.js';
13
13
  import type { RelationshipManager } from '../core/RelationshipManager.js';
14
+ import type { FeedbackManager } from '../core/FeedbackManager.js';
15
+ import type { UpdateChecker } from '../core/UpdateChecker.js';
14
16
  export declare class AgentServer {
15
17
  private app;
16
18
  private server;
@@ -23,6 +25,8 @@ export declare class AgentServer {
23
25
  scheduler?: JobScheduler;
24
26
  telegram?: TelegramAdapter;
25
27
  relationships?: RelationshipManager;
28
+ feedback?: FeedbackManager;
29
+ updateChecker?: UpdateChecker;
26
30
  });
27
31
  /**
28
32
  * Start the HTTP server.
@@ -28,6 +28,8 @@ export class AgentServer {
28
28
  scheduler: options.scheduler ?? null,
29
29
  telegram: options.telegram ?? null,
30
30
  relationships: options.relationships ?? null,
31
+ feedback: options.feedback ?? null,
32
+ updateChecker: options.updateChecker ?? null,
31
33
  startTime: this.startTime,
32
34
  });
33
35
  this.app.use(routes);
@@ -11,6 +11,8 @@ import type { JobScheduler } from '../scheduler/JobScheduler.js';
11
11
  import type { AgentKitConfig } from '../core/types.js';
12
12
  import type { TelegramAdapter } from '../messaging/TelegramAdapter.js';
13
13
  import type { RelationshipManager } from '../core/RelationshipManager.js';
14
+ import type { FeedbackManager } from '../core/FeedbackManager.js';
15
+ import type { UpdateChecker } from '../core/UpdateChecker.js';
14
16
  interface RouteContext {
15
17
  config: AgentKitConfig;
16
18
  sessionManager: SessionManager;
@@ -18,6 +20,8 @@ interface RouteContext {
18
20
  scheduler: JobScheduler | null;
19
21
  telegram: TelegramAdapter | null;
20
22
  relationships: RelationshipManager | null;
23
+ feedback: FeedbackManager | null;
24
+ updateChecker: UpdateChecker | null;
21
25
  startTime: Date;
22
26
  }
23
27
  export declare function createRoutes(ctx: RouteContext): Router;
@@ -197,6 +197,89 @@ export function createRoutes(ctx) {
197
197
  }
198
198
  res.json({ context });
199
199
  });
200
+ // ── Feedback ────────────────────────────────────────────────────
201
+ router.post('/feedback', async (req, res) => {
202
+ if (!ctx.feedback) {
203
+ res.status(503).json({ error: 'Feedback not configured' });
204
+ return;
205
+ }
206
+ const { type, title, description, context } = req.body;
207
+ if (!title || !description) {
208
+ res.status(400).json({ error: '"title" and "description" are required' });
209
+ return;
210
+ }
211
+ const validTypes = ['bug', 'feature', 'improvement', 'question', 'other'];
212
+ const feedbackType = validTypes.includes(type) ? type : 'other';
213
+ try {
214
+ const item = await ctx.feedback.submit({
215
+ type: feedbackType,
216
+ title,
217
+ description,
218
+ context: context || undefined,
219
+ agentName: ctx.config.projectName,
220
+ instarVersion: ctx.config.version || '0.0.0',
221
+ nodeVersion: process.version,
222
+ os: `${process.platform} ${process.arch}`,
223
+ });
224
+ res.status(201).json({
225
+ ok: true,
226
+ id: item.id,
227
+ forwarded: item.forwarded,
228
+ message: item.forwarded
229
+ ? 'Feedback submitted and forwarded upstream.'
230
+ : 'Feedback stored locally. Will retry forwarding later.',
231
+ });
232
+ }
233
+ catch (err) {
234
+ res.status(500).json({ error: err.message });
235
+ }
236
+ });
237
+ router.get('/feedback', (_req, res) => {
238
+ if (!ctx.feedback) {
239
+ res.json({ feedback: [] });
240
+ return;
241
+ }
242
+ res.json({ feedback: ctx.feedback.list() });
243
+ });
244
+ router.post('/feedback/retry', async (_req, res) => {
245
+ if (!ctx.feedback) {
246
+ res.status(503).json({ error: 'Feedback not configured' });
247
+ return;
248
+ }
249
+ try {
250
+ const result = await ctx.feedback.retryUnforwarded();
251
+ res.json({ ok: true, ...result });
252
+ }
253
+ catch (err) {
254
+ res.status(500).json({ error: err.message });
255
+ }
256
+ });
257
+ // ── Updates ────────────────────────────────────────────────────
258
+ router.get('/updates', async (_req, res) => {
259
+ if (!ctx.updateChecker) {
260
+ res.status(503).json({ error: 'Update checker not configured' });
261
+ return;
262
+ }
263
+ try {
264
+ const info = await ctx.updateChecker.check();
265
+ res.json(info);
266
+ }
267
+ catch (err) {
268
+ res.status(500).json({ error: err.message });
269
+ }
270
+ });
271
+ router.get('/updates/last', (_req, res) => {
272
+ if (!ctx.updateChecker) {
273
+ res.status(503).json({ error: 'Update checker not configured' });
274
+ return;
275
+ }
276
+ const lastCheck = ctx.updateChecker.getLastCheck();
277
+ if (!lastCheck) {
278
+ res.json({ message: 'No update check has been performed yet' });
279
+ return;
280
+ }
281
+ res.json(lastCheck);
282
+ });
200
283
  // ── Events ──────────────────────────────────────────────────────
201
284
  router.get('/events', (req, res) => {
202
285
  const limit = parseInt(req.query.limit) || 50;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
Binary file
@@ -1,6 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
2
- <rect width="32" height="32" rx="6" fill="#0a0e17"/>
3
- <path d="M8 22 L16 8 L24 22" stroke="#fbbf24" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
4
- <path d="M11 17 L21 17" stroke="#fbbf24" stroke-width="2" stroke-linecap="round"/>
5
- <circle cx="16" cy="12" r="1.5" fill="#fbbf24"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="6" fill="#0d2137"/>
3
+ <text x="16" y="23" text-anchor="middle" font-size="20" fill="#6ec6c8" font-family="system-ui" font-weight="bold">i</text>
6
4
  </svg>
Binary file
Binary file