instar 0.6.11 → 0.6.13

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.
@@ -222,6 +222,38 @@ export interface ComponentHealth {
222
222
  message?: string;
223
223
  lastCheck: string;
224
224
  }
225
+ /**
226
+ * Optional LLM intelligence for judgment calls.
227
+ *
228
+ * Any module that makes decisions beyond simple lookups can declare
229
+ * `intelligence?: IntelligenceProvider` in its config. This is the
230
+ * structural pattern that prevents defaulting to brittle heuristics.
231
+ *
232
+ * The contract: heuristics narrow candidates, the provider decides.
233
+ * When no provider is configured, modules fall back to heuristic-only
234
+ * behavior — functional but less accurate.
235
+ *
236
+ * Born from the "heuristics are pre-filters, not decision-makers" lesson.
237
+ */
238
+ export interface IntelligenceProvider {
239
+ /**
240
+ * Ask the LLM to evaluate a judgment call.
241
+ * Returns a structured response that the caller parses.
242
+ *
243
+ * @param prompt - The judgment to make, with full context
244
+ * @param options - Optional configuration for this call
245
+ * @returns The LLM's response text
246
+ */
247
+ evaluate(prompt: string, options?: IntelligenceOptions): Promise<string>;
248
+ }
249
+ export interface IntelligenceOptions {
250
+ /** Model tier preference (implementations may override based on availability) */
251
+ model?: 'fast' | 'balanced' | 'capable';
252
+ /** Maximum tokens for the response */
253
+ maxTokens?: number;
254
+ /** Temperature (0-1, lower = more deterministic) */
255
+ temperature?: number;
256
+ }
225
257
  export interface RelationshipRecord {
226
258
  /** Unique identifier for this person */
227
259
  id: string;
@@ -245,6 +277,10 @@ export interface RelationshipRecord {
245
277
  significance: number;
246
278
  /** Brief summary of the relationship arc */
247
279
  arcSummary?: string;
280
+ /** Relationship category (e.g., 'collaborator', 'community_member', 'kindred_ai') */
281
+ category?: string;
282
+ /** Freeform tags for flexible categorization */
283
+ tags?: string[];
248
284
  /** Per-interaction log (last N interactions, kept compact) */
249
285
  recentInteractions: InteractionSummary[];
250
286
  }
@@ -263,6 +299,13 @@ export interface RelationshipManagerConfig {
263
299
  relationshipsDir: string;
264
300
  /** Maximum recent interactions to keep per relationship */
265
301
  maxRecentInteractions: number;
302
+ /**
303
+ * Optional LLM intelligence for judgment calls (identity resolution,
304
+ * duplicate detection, merge decisions). When absent, falls back to
305
+ * string-based heuristics. When present, heuristics narrow candidates
306
+ * and the LLM makes the final call.
307
+ */
308
+ intelligence?: IntelligenceProvider;
266
309
  }
267
310
  export type SkipReason = 'disabled' | 'paused' | 'quota' | 'capacity';
268
311
  export interface SkipEvent {
@@ -363,6 +406,173 @@ export interface UpdateResult {
363
406
  /** Health check result after update */
364
407
  healthCheck?: 'healthy' | 'degraded' | 'unhealthy' | 'skipped';
365
408
  }
409
+ /**
410
+ * Evolution proposal — a staged self-improvement suggestion.
411
+ *
412
+ * Unlike direct self-modification (editing jobs.json, creating skills),
413
+ * proposals are staged for review before implementation. This gives
414
+ * the agent (and optionally the user) a chance to evaluate whether
415
+ * the change is wise before it takes effect.
416
+ *
417
+ * Born from Portal's EVOLUTION_QUEUE pattern (100+ completed proposals).
418
+ */
419
+ export interface EvolutionProposal {
420
+ /** Unique ID (e.g., "EVO-001") */
421
+ id: string;
422
+ /** Short title describing the proposed change */
423
+ title: string;
424
+ /** Where this proposal came from */
425
+ source: string;
426
+ /** Full description of what to change and why */
427
+ description: string;
428
+ /** Category of change */
429
+ type: EvolutionType;
430
+ /** Expected impact if implemented */
431
+ impact: 'high' | 'medium' | 'low';
432
+ /** Estimated effort to implement */
433
+ effort: 'high' | 'medium' | 'low';
434
+ /** Current status */
435
+ status: EvolutionStatus;
436
+ /** Who or what proposed this */
437
+ proposedBy: string;
438
+ /** When proposed */
439
+ proposedAt: string;
440
+ /** When implemented (if status is 'implemented') */
441
+ implementedAt?: string;
442
+ /** Implementation notes */
443
+ resolution?: string;
444
+ /** Tags for categorization */
445
+ tags?: string[];
446
+ }
447
+ export type EvolutionType = 'capability' | 'infrastructure' | 'voice' | 'workflow' | 'philosophy' | 'integration' | 'performance';
448
+ export type EvolutionStatus = 'proposed' | 'approved' | 'in_progress' | 'implemented' | 'rejected' | 'deferred';
449
+ /**
450
+ * Structured learning entry — an insight captured from interaction.
451
+ *
452
+ * Unlike freeform MEMORY.md entries, these are structured, searchable,
453
+ * cross-referenceable, and trackable (applied vs unapplied).
454
+ */
455
+ export interface LearningEntry {
456
+ /** Unique ID (e.g., "LRN-001") */
457
+ id: string;
458
+ /** Short title */
459
+ title: string;
460
+ /** Category of learning */
461
+ category: string;
462
+ /** Full description of the insight */
463
+ description: string;
464
+ /** Where this learning came from */
465
+ source: LearningSource;
466
+ /** Tags for cross-referencing */
467
+ tags: string[];
468
+ /** Has this learning been applied to improve the agent? */
469
+ applied: boolean;
470
+ /** What it was applied to (e.g., "EVO-003", "MEMORY.md") */
471
+ appliedTo?: string;
472
+ /** How relevant this is to agent evolution (freeform) */
473
+ evolutionRelevance?: string;
474
+ }
475
+ export interface LearningSource {
476
+ /** Who/what taught this */
477
+ agent?: string;
478
+ /** Platform where discovered */
479
+ platform?: string;
480
+ /** Content reference (post ID, thread ID, etc.) */
481
+ contentId?: string;
482
+ /** When discovered */
483
+ discoveredAt: string;
484
+ /** Session that captured this */
485
+ session?: string;
486
+ }
487
+ /**
488
+ * Capability gap — something the agent can't do but should.
489
+ *
490
+ * Extends self-diagnosis from "is my infrastructure broken?" to
491
+ * "is my infrastructure sufficient?"
492
+ */
493
+ export interface CapabilityGap {
494
+ /** Unique ID (e.g., "GAP-001") */
495
+ id: string;
496
+ /** Short title */
497
+ title: string;
498
+ /** Category of gap */
499
+ category: GapCategory;
500
+ /** How critical this gap is */
501
+ severity: 'critical' | 'high' | 'medium' | 'low';
502
+ /** Full description of the gap */
503
+ description: string;
504
+ /** How the gap was discovered */
505
+ discoveredFrom: {
506
+ context: string;
507
+ platform?: string;
508
+ discoveredAt: string;
509
+ session?: string;
510
+ };
511
+ /** What the agent currently does (or doesn't) */
512
+ currentState: string;
513
+ /** What should be built to close the gap */
514
+ proposedSolution?: string;
515
+ /** Current status */
516
+ status: 'identified' | 'addressed' | 'wont_fix';
517
+ /** How it was resolved */
518
+ resolution?: string;
519
+ /** When addressed */
520
+ addressedAt?: string;
521
+ }
522
+ export type GapCategory = 'skill' | 'knowledge' | 'integration' | 'workflow' | 'communication' | 'monitoring' | 'custom';
523
+ /**
524
+ * Action/commitment item — something the agent promised to do.
525
+ *
526
+ * Tracks commitments made during interactions so they don't get lost.
527
+ * Stale commitments are escalated automatically.
528
+ */
529
+ export interface ActionItem {
530
+ /** Unique ID (e.g., "ACT-001") */
531
+ id: string;
532
+ /** What was committed */
533
+ title: string;
534
+ /** Full description */
535
+ description: string;
536
+ /** Priority level */
537
+ priority: 'critical' | 'high' | 'medium' | 'low';
538
+ /** Current status */
539
+ status: 'pending' | 'in_progress' | 'completed' | 'cancelled';
540
+ /** Who this commitment was made to */
541
+ commitTo?: string;
542
+ /** When this was created */
543
+ createdAt: string;
544
+ /** When this should be done by (ISO date) */
545
+ dueBy?: string;
546
+ /** When completed */
547
+ completedAt?: string;
548
+ /** How it was resolved */
549
+ resolution?: string;
550
+ /** Where this commitment was made */
551
+ source?: {
552
+ platform?: string;
553
+ contentId?: string;
554
+ context?: string;
555
+ };
556
+ /** Tags for categorization */
557
+ tags?: string[];
558
+ }
559
+ /**
560
+ * Evolution manager configuration.
561
+ */
562
+ export interface EvolutionManagerConfig {
563
+ /** Directory for evolution state files */
564
+ stateDir: string;
565
+ /** Whether auto-implementation of approved proposals is enabled */
566
+ autoImplement?: boolean;
567
+ /** Maximum proposals before oldest get archived */
568
+ maxProposals?: number;
569
+ /** Maximum learning entries before oldest get archived */
570
+ maxLearnings?: number;
571
+ /** Maximum gaps before oldest addressed get archived */
572
+ maxGaps?: number;
573
+ /** Maximum action items before oldest completed get archived */
574
+ maxActions?: number;
575
+ }
366
576
  export interface InstarConfig {
367
577
  /** Project name (used in logging, tmux session names, etc.) */
368
578
  projectName: string;
@@ -404,6 +614,8 @@ export interface InstarConfig {
404
614
  version?: string;
405
615
  /** Safety configuration for autonomous operation */
406
616
  safety?: SafetyConfig;
617
+ /** Evolution system configuration */
618
+ evolution?: EvolutionManagerConfig;
407
619
  }
408
620
  /**
409
621
  * Safety configuration — controls the progression from supervised to autonomous operation.
package/dist/index.d.ts CHANGED
@@ -6,7 +6,10 @@
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 { ClaudeCliIntelligenceProvider } from './core/ClaudeCliIntelligenceProvider.js';
10
+ export { AnthropicIntelligenceProvider } from './core/AnthropicIntelligenceProvider.js';
9
11
  export { FeedbackManager } from './core/FeedbackManager.js';
12
+ export { EvolutionManager } from './core/EvolutionManager.js';
10
13
  export { DispatchManager } from './core/DispatchManager.js';
11
14
  export { UpdateChecker } from './core/UpdateChecker.js';
12
15
  export type { RollbackResult, UpdateCheckerConfig } from './core/UpdateChecker.js';
@@ -32,6 +35,6 @@ export { PrivateViewer } from './publishing/PrivateViewer.js';
32
35
  export type { PrivateView, PrivateViewerConfig } from './publishing/PrivateViewer.js';
33
36
  export { TunnelManager } from './tunnel/TunnelManager.js';
34
37
  export type { TunnelConfig, TunnelState } from './tunnel/TunnelManager.js';
35
- export type { Session, SessionStatus, SessionManagerConfig, ModelTier, JobDefinition, JobPriority, JobExecution, JobState, JobSchedulerConfig, UserProfile, UserChannel, UserPreferences, Message, OutgoingMessage, MessagingAdapter, MessagingAdapterConfig, QuotaState, AccountQuota, HealthStatus, ComponentHealth, ActivityEvent, InstarConfig, MonitoringConfig, RelationshipRecord, RelationshipManagerConfig, InteractionSummary, FeedbackItem, FeedbackConfig, UpdateInfo, UpdateResult, DispatchConfig, UpdateConfig, PublishingConfig, TunnelConfigType, SkipReason, SkipEvent, WorkloadSignal, AutoTuneState, } from './core/types.js';
38
+ export type { Session, SessionStatus, SessionManagerConfig, ModelTier, JobDefinition, JobPriority, JobExecution, JobState, JobSchedulerConfig, UserProfile, UserChannel, UserPreferences, Message, OutgoingMessage, MessagingAdapter, MessagingAdapterConfig, QuotaState, AccountQuota, HealthStatus, ComponentHealth, ActivityEvent, InstarConfig, MonitoringConfig, RelationshipRecord, RelationshipManagerConfig, InteractionSummary, FeedbackItem, FeedbackConfig, UpdateInfo, UpdateResult, DispatchConfig, UpdateConfig, PublishingConfig, TunnelConfigType, SkipReason, SkipEvent, WorkloadSignal, AutoTuneState, IntelligenceProvider, IntelligenceOptions, EvolutionProposal, EvolutionType, EvolutionStatus, LearningEntry, LearningSource, CapabilityGap, GapCategory, ActionItem, EvolutionManagerConfig, } from './core/types.js';
36
39
  export type { Dispatch, DispatchCheckResult, DispatchEvaluation, EvaluationDecision, DispatchFeedback, DispatchStats } from './core/DispatchManager.js';
37
40
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -7,7 +7,10 @@
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 { ClaudeCliIntelligenceProvider } from './core/ClaudeCliIntelligenceProvider.js';
11
+ export { AnthropicIntelligenceProvider } from './core/AnthropicIntelligenceProvider.js';
10
12
  export { FeedbackManager } from './core/FeedbackManager.js';
13
+ export { EvolutionManager } from './core/EvolutionManager.js';
11
14
  export { DispatchManager } from './core/DispatchManager.js';
12
15
  export { UpdateChecker } from './core/UpdateChecker.js';
13
16
  export { PostUpdateMigrator } from './core/PostUpdateMigrator.js';
@@ -396,6 +396,42 @@ Instar has a built-in feedback loop that connects all agents to the development
396
396
 
397
397
  **User feedback matters too.** When your user says "this isn't working" or "I wish I could..." — that's feedback. Categorize it and submit it the same way.
398
398
 
399
+ ### Evolution System
400
+
401
+ You have a built-in evolution system with four subsystems. This is not a metaphor — it's infrastructure that tracks your growth.
402
+
403
+ **Evolution Queue** — Staged self-improvement proposals.
404
+ - View: \`curl http://localhost:${port}/evolution/proposals\`
405
+ - Propose: \`/evolve\` skill or \`POST /evolution/proposals\`
406
+ - The \`evolution-review\` job evaluates and implements proposals every 6 hours.
407
+
408
+ **Learning Registry** — Structured, searchable insights.
409
+ - View: \`curl http://localhost:${port}/evolution/learnings\`
410
+ - Record: \`/learn\` skill or \`POST /evolution/learnings\`
411
+ - The \`insight-harvest\` job synthesizes patterns into proposals every 8 hours.
412
+
413
+ **Capability Gaps** — Track what you're missing.
414
+ - View: \`curl http://localhost:${port}/evolution/gaps\`
415
+ - Report: \`/gaps\` skill or \`POST /evolution/gaps\`
416
+
417
+ **Action Queue** — Commitments with follow-through tracking.
418
+ - View: \`curl http://localhost:${port}/evolution/actions\`
419
+ - Create: \`/commit-action\` skill or \`POST /evolution/actions\`
420
+ - The \`commitment-check\` job surfaces overdue items every 4 hours.
421
+
422
+ **Dashboard** — Full evolution health at a glance:
423
+ \`\`\`bash
424
+ curl http://localhost:${port}/evolution
425
+ \`\`\`
426
+
427
+ **Skills for evolution:**
428
+ - \`/evolve\` — Propose an improvement
429
+ - \`/learn\` — Record an insight
430
+ - \`/gaps\` — Report a missing capability
431
+ - \`/commit-action\` — Track a commitment
432
+
433
+ **The principle:** Evolution is not a separate activity from work. Every task is an opportunity to notice what could be better. The post-action reflection hook reminds you to pause after significant actions (commits, deploys) and consider what you learned. Most learning is lost because nobody paused to ask.
434
+
399
435
  ### Self-Evolution
400
436
 
401
437
  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.
@@ -18,6 +18,7 @@ import type { QuotaTracker } from '../monitoring/QuotaTracker.js';
18
18
  import type { TelegraphService } from '../publishing/TelegraphService.js';
19
19
  import type { PrivateViewer } from '../publishing/PrivateViewer.js';
20
20
  import type { TunnelManager } from '../tunnel/TunnelManager.js';
21
+ import type { EvolutionManager } from '../core/EvolutionManager.js';
21
22
  export declare class AgentServer {
22
23
  private app;
23
24
  private server;
@@ -37,6 +38,7 @@ export declare class AgentServer {
37
38
  publisher?: TelegraphService;
38
39
  viewer?: PrivateViewer;
39
40
  tunnel?: TunnelManager;
41
+ evolution?: EvolutionManager;
40
42
  });
41
43
  /**
42
44
  * Start the HTTP server.
@@ -36,6 +36,7 @@ export class AgentServer {
36
36
  publisher: options.publisher ?? null,
37
37
  viewer: options.viewer ?? null,
38
38
  tunnel: options.tunnel ?? null,
39
+ evolution: options.evolution ?? null,
39
40
  startTime: this.startTime,
40
41
  });
41
42
  this.app.use(routes);
@@ -18,6 +18,7 @@ import type { QuotaTracker } from '../monitoring/QuotaTracker.js';
18
18
  import type { TelegraphService } from '../publishing/TelegraphService.js';
19
19
  import type { PrivateViewer } from '../publishing/PrivateViewer.js';
20
20
  import type { TunnelManager } from '../tunnel/TunnelManager.js';
21
+ import type { EvolutionManager } from '../core/EvolutionManager.js';
21
22
  export interface RouteContext {
22
23
  config: InstarConfig;
23
24
  sessionManager: SessionManager;
@@ -32,6 +33,7 @@ export interface RouteContext {
32
33
  publisher: TelegraphService | null;
33
34
  viewer: PrivateViewer | null;
34
35
  tunnel: TunnelManager | null;
36
+ evolution: EvolutionManager | null;
35
37
  startTime: Date;
36
38
  }
37
39
  export declare function createRoutes(ctx: RouteContext): Router;
@@ -1324,6 +1324,224 @@ export function createRoutes(ctx) {
1324
1324
  res.status(503).json({ error: 'No message routing available' });
1325
1325
  }
1326
1326
  });
1327
+ // ── Evolution System ───────────────────────────────────────────
1328
+ // Dashboard — overview of all evolution subsystems
1329
+ router.get('/evolution', (_req, res) => {
1330
+ if (!ctx.evolution) {
1331
+ res.json({ enabled: false });
1332
+ return;
1333
+ }
1334
+ res.json({ enabled: true, ...ctx.evolution.getDashboard() });
1335
+ });
1336
+ // Evolution proposals
1337
+ router.get('/evolution/proposals', (req, res) => {
1338
+ if (!ctx.evolution) {
1339
+ res.json({ proposals: [] });
1340
+ return;
1341
+ }
1342
+ const status = req.query.status;
1343
+ const type = req.query.type;
1344
+ res.json({ proposals: ctx.evolution.listProposals({ status, type }) });
1345
+ });
1346
+ router.post('/evolution/proposals', (req, res) => {
1347
+ if (!ctx.evolution) {
1348
+ res.status(503).json({ error: 'Evolution system not configured' });
1349
+ return;
1350
+ }
1351
+ const { title, source, description, type, impact, effort, proposedBy, tags } = req.body;
1352
+ if (!title || typeof title !== 'string' || title.length > 500) {
1353
+ res.status(400).json({ error: '"title" must be a string under 500 characters' });
1354
+ return;
1355
+ }
1356
+ if (!description || typeof description !== 'string' || description.length > 10_000) {
1357
+ res.status(400).json({ error: '"description" must be a string under 10KB' });
1358
+ return;
1359
+ }
1360
+ const validTypes = ['capability', 'infrastructure', 'voice', 'workflow', 'philosophy', 'integration', 'performance'];
1361
+ if (type && !validTypes.includes(type)) {
1362
+ res.status(400).json({ error: `"type" must be one of: ${validTypes.join(', ')}` });
1363
+ return;
1364
+ }
1365
+ const proposal = ctx.evolution.addProposal({
1366
+ title, source: source || 'api', description,
1367
+ type: type || 'capability', impact, effort, proposedBy, tags,
1368
+ });
1369
+ res.status(201).json(proposal);
1370
+ });
1371
+ router.patch('/evolution/proposals/:id', (req, res) => {
1372
+ if (!ctx.evolution) {
1373
+ res.status(503).json({ error: 'Evolution system not configured' });
1374
+ return;
1375
+ }
1376
+ const { status, resolution } = req.body;
1377
+ const validStatuses = ['proposed', 'approved', 'in_progress', 'implemented', 'rejected', 'deferred'];
1378
+ if (!status || !validStatuses.includes(status)) {
1379
+ res.status(400).json({ error: `"status" must be one of: ${validStatuses.join(', ')}` });
1380
+ return;
1381
+ }
1382
+ const success = ctx.evolution.updateProposalStatus(req.params.id, status, resolution);
1383
+ if (!success) {
1384
+ res.status(404).json({ error: 'Proposal not found' });
1385
+ return;
1386
+ }
1387
+ res.json({ ok: true, id: req.params.id, status });
1388
+ });
1389
+ // Learning registry
1390
+ router.get('/evolution/learnings', (req, res) => {
1391
+ if (!ctx.evolution) {
1392
+ res.json({ learnings: [] });
1393
+ return;
1394
+ }
1395
+ const category = req.query.category;
1396
+ const applied = req.query.applied !== undefined ? req.query.applied === 'true' : undefined;
1397
+ res.json({ learnings: ctx.evolution.listLearnings({ category, applied }) });
1398
+ });
1399
+ router.post('/evolution/learnings', (req, res) => {
1400
+ if (!ctx.evolution) {
1401
+ res.status(503).json({ error: 'Evolution system not configured' });
1402
+ return;
1403
+ }
1404
+ const { title, category, description, source, tags, evolutionRelevance } = req.body;
1405
+ if (!title || typeof title !== 'string' || title.length > 500) {
1406
+ res.status(400).json({ error: '"title" must be a string under 500 characters' });
1407
+ return;
1408
+ }
1409
+ if (!description || typeof description !== 'string') {
1410
+ res.status(400).json({ error: '"description" is required' });
1411
+ return;
1412
+ }
1413
+ const learning = ctx.evolution.addLearning({
1414
+ title, category: category || 'general', description,
1415
+ source: source || { discoveredAt: new Date().toISOString() },
1416
+ tags, evolutionRelevance,
1417
+ });
1418
+ res.status(201).json(learning);
1419
+ });
1420
+ router.patch('/evolution/learnings/:id/apply', (req, res) => {
1421
+ if (!ctx.evolution) {
1422
+ res.status(503).json({ error: 'Evolution system not configured' });
1423
+ return;
1424
+ }
1425
+ const { appliedTo } = req.body;
1426
+ if (!appliedTo || typeof appliedTo !== 'string') {
1427
+ res.status(400).json({ error: '"appliedTo" is required' });
1428
+ return;
1429
+ }
1430
+ const success = ctx.evolution.markLearningApplied(req.params.id, appliedTo);
1431
+ if (!success) {
1432
+ res.status(404).json({ error: 'Learning not found' });
1433
+ return;
1434
+ }
1435
+ res.json({ ok: true, id: req.params.id, appliedTo });
1436
+ });
1437
+ // Capability gaps
1438
+ router.get('/evolution/gaps', (req, res) => {
1439
+ if (!ctx.evolution) {
1440
+ res.json({ gaps: [] });
1441
+ return;
1442
+ }
1443
+ const severity = req.query.severity;
1444
+ const category = req.query.category;
1445
+ const status = req.query.status;
1446
+ res.json({ gaps: ctx.evolution.listGaps({ severity, category, status }) });
1447
+ });
1448
+ router.post('/evolution/gaps', (req, res) => {
1449
+ if (!ctx.evolution) {
1450
+ res.status(503).json({ error: 'Evolution system not configured' });
1451
+ return;
1452
+ }
1453
+ const { title, category, severity, description, context, platform, session, currentState, proposedSolution } = req.body;
1454
+ if (!title || typeof title !== 'string' || title.length > 500) {
1455
+ res.status(400).json({ error: '"title" must be a string under 500 characters' });
1456
+ return;
1457
+ }
1458
+ if (!description || typeof description !== 'string') {
1459
+ res.status(400).json({ error: '"description" is required' });
1460
+ return;
1461
+ }
1462
+ const validSeverities = ['critical', 'high', 'medium', 'low'];
1463
+ if (severity && !validSeverities.includes(severity)) {
1464
+ res.status(400).json({ error: `"severity" must be one of: ${validSeverities.join(', ')}` });
1465
+ return;
1466
+ }
1467
+ const gap = ctx.evolution.addGap({
1468
+ title, category: category || 'custom', severity: severity || 'medium',
1469
+ description, context: context || '', platform, session,
1470
+ currentState, proposedSolution,
1471
+ });
1472
+ res.status(201).json(gap);
1473
+ });
1474
+ router.patch('/evolution/gaps/:id/address', (req, res) => {
1475
+ if (!ctx.evolution) {
1476
+ res.status(503).json({ error: 'Evolution system not configured' });
1477
+ return;
1478
+ }
1479
+ const { resolution } = req.body;
1480
+ if (!resolution || typeof resolution !== 'string') {
1481
+ res.status(400).json({ error: '"resolution" is required' });
1482
+ return;
1483
+ }
1484
+ const success = ctx.evolution.addressGap(req.params.id, resolution);
1485
+ if (!success) {
1486
+ res.status(404).json({ error: 'Gap not found' });
1487
+ return;
1488
+ }
1489
+ res.json({ ok: true, id: req.params.id, status: 'addressed' });
1490
+ });
1491
+ // Action queue
1492
+ router.get('/evolution/actions', (req, res) => {
1493
+ if (!ctx.evolution) {
1494
+ res.json({ actions: [] });
1495
+ return;
1496
+ }
1497
+ const status = req.query.status;
1498
+ const priority = req.query.priority;
1499
+ res.json({ actions: ctx.evolution.listActions({ status, priority }) });
1500
+ });
1501
+ router.get('/evolution/actions/overdue', (_req, res) => {
1502
+ if (!ctx.evolution) {
1503
+ res.json({ overdue: [] });
1504
+ return;
1505
+ }
1506
+ res.json({ overdue: ctx.evolution.getOverdueActions() });
1507
+ });
1508
+ router.post('/evolution/actions', (req, res) => {
1509
+ if (!ctx.evolution) {
1510
+ res.status(503).json({ error: 'Evolution system not configured' });
1511
+ return;
1512
+ }
1513
+ const { title, description, priority, commitTo, dueBy, source, tags } = req.body;
1514
+ if (!title || typeof title !== 'string' || title.length > 500) {
1515
+ res.status(400).json({ error: '"title" must be a string under 500 characters' });
1516
+ return;
1517
+ }
1518
+ if (!description || typeof description !== 'string') {
1519
+ res.status(400).json({ error: '"description" is required' });
1520
+ return;
1521
+ }
1522
+ const action = ctx.evolution.addAction({
1523
+ title, description, priority, commitTo, dueBy, source, tags,
1524
+ });
1525
+ res.status(201).json(action);
1526
+ });
1527
+ router.patch('/evolution/actions/:id', (req, res) => {
1528
+ if (!ctx.evolution) {
1529
+ res.status(503).json({ error: 'Evolution system not configured' });
1530
+ return;
1531
+ }
1532
+ const { status, resolution } = req.body;
1533
+ const validStatuses = ['pending', 'in_progress', 'completed', 'cancelled'];
1534
+ if (status && !validStatuses.includes(status)) {
1535
+ res.status(400).json({ error: `"status" must be one of: ${validStatuses.join(', ')}` });
1536
+ return;
1537
+ }
1538
+ const success = ctx.evolution.updateAction(req.params.id, { status, resolution });
1539
+ if (!success) {
1540
+ res.status(404).json({ error: 'Action not found' });
1541
+ return;
1542
+ }
1543
+ res.json({ ok: true, id: req.params.id, status });
1544
+ });
1327
1545
  return router;
1328
1546
  }
1329
1547
  export function formatUptime(ms) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "Persistent autonomy infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",