instar 1.3.310 → 1.3.312
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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +32 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +8 -5
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +17 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/AgentWorktreeDetector.d.ts +27 -3
- package/dist/core/AgentWorktreeDetector.d.ts.map +1 -1
- package/dist/core/AgentWorktreeDetector.js +77 -19
- package/dist/core/AgentWorktreeDetector.js.map +1 -1
- package/dist/core/BootSelfKnowledge.d.ts +121 -0
- package/dist/core/BootSelfKnowledge.d.ts.map +1 -0
- package/dist/core/BootSelfKnowledge.js +285 -0
- package/dist/core/BootSelfKnowledge.js.map +1 -0
- package/dist/core/InstarWorktreeManager.d.ts +7 -0
- package/dist/core/InstarWorktreeManager.d.ts.map +1 -1
- package/dist/core/InstarWorktreeManager.js +47 -1
- package/dist/core/InstarWorktreeManager.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +10 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +161 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +26 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/messaging/AttentionTopicGuard.d.ts +14 -0
- package/dist/messaging/AttentionTopicGuard.d.ts.map +1 -1
- package/dist/messaging/AttentionTopicGuard.js +21 -0
- package/dist/messaging/AttentionTopicGuard.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +40 -3
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +61 -11
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +8 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +3 -3
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +137 -2
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/CollaborationSurfacer.d.ts +4 -1
- package/dist/threadline/CollaborationSurfacer.d.ts.map +1 -1
- package/dist/threadline/CollaborationSurfacer.js +1 -1
- package/dist/threadline/CollaborationSurfacer.js.map +1 -1
- package/dist/threadline/TelegramBridge.d.ts +4 -1
- package/dist/threadline/TelegramBridge.d.ts.map +1 -1
- package/dist/threadline/TelegramBridge.js +1 -1
- package/dist/threadline/TelegramBridge.js.map +1 -1
- package/dist/threadline/hubCommands.d.ts +4 -1
- package/dist/threadline/hubCommands.d.ts.map +1 -1
- package/dist/threadline/hubCommands.js +3 -3
- package/dist/threadline/hubCommands.js.map +1 -1
- package/package.json +2 -2
- package/scripts/lint-no-direct-destructive.js +4 -0
- package/scripts/lint-no-unfunneled-topic-creation.js +129 -0
- package/src/data/builtin-manifest.json +73 -65
- package/src/scaffold/templates.ts +8 -0
- package/src/templates/scripts/secret-get.mjs +143 -0
- package/upgrades/1.3.311.md +20 -0
- package/upgrades/1.3.312.md +31 -0
- package/upgrades/side-effects/bounded-notification-surface.md +34 -0
- package/upgrades/side-effects/session-boot-self-knowledge.md +78 -0
|
@@ -195,6 +195,7 @@ export class PostUpdateMigrator {
|
|
|
195
195
|
this.migrateStaleLifelineSignal(result);
|
|
196
196
|
this.migrateThreadlineConversationStore(result);
|
|
197
197
|
this.migrateThreadlineAgentInfoIdentity(result);
|
|
198
|
+
this.migrateWorktreeMisplacedFloodItems(result);
|
|
198
199
|
return result;
|
|
199
200
|
}
|
|
200
201
|
/**
|
|
@@ -537,6 +538,42 @@ export class PostUpdateMigrator {
|
|
|
537
538
|
result.errors.push(`threadline-agent-info-identity write: ${err instanceof Error ? err.message : String(err)}`);
|
|
538
539
|
}
|
|
539
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* 2026-06-05 worktree-misplaced flood cleanup (Bounded Notification
|
|
543
|
+
* Surface). The pre-fix AgentWorktreeDetector emitted one attention item
|
|
544
|
+
* PER worktree with ids `worktree-misplaced:<sha256>`; a transiently-wrong
|
|
545
|
+
* safe-root read mass-created 110 false-positive OPEN items on flooded
|
|
546
|
+
* agents. The fixed detector emits a single `worktree-misplaced-summary:*`
|
|
547
|
+
* item, so the old per-path items are permanently stale — purge them.
|
|
548
|
+
* Idempotent: a store with no old-format ids is left untouched.
|
|
549
|
+
*/
|
|
550
|
+
migrateWorktreeMisplacedFloodItems(result) {
|
|
551
|
+
const storePath = path.join(this.config.stateDir, 'state', 'attention-items.json');
|
|
552
|
+
if (!fs.existsSync(storePath)) {
|
|
553
|
+
result.skipped.push('worktree-misplaced flood items: no attention store');
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
try {
|
|
557
|
+
const data = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
|
|
558
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
559
|
+
// Old per-path format only: `worktree-misplaced:<hash>`. The new
|
|
560
|
+
// aggregated format is `worktree-misplaced-summary:<hash>` — kept.
|
|
561
|
+
const isStale = (id) => typeof id === 'string' && id.startsWith('worktree-misplaced:');
|
|
562
|
+
const staleCount = items.filter((i) => isStale(i.id)).length;
|
|
563
|
+
if (staleCount === 0) {
|
|
564
|
+
result.skipped.push('worktree-misplaced flood items: none present');
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const kept = items.filter((i) => !isStale(i.id));
|
|
568
|
+
const tmp = `${storePath}.${process.pid}.tmp`;
|
|
569
|
+
fs.writeFileSync(tmp, JSON.stringify({ ...data, items: kept }, null, 2));
|
|
570
|
+
fs.renameSync(tmp, storePath);
|
|
571
|
+
result.upgraded.push(`worktree-misplaced flood items: purged ${staleCount} stale per-path item(s) from the attention store`);
|
|
572
|
+
}
|
|
573
|
+
catch (err) {
|
|
574
|
+
result.errors.push(`worktree-misplaced flood items: ${err instanceof Error ? err.message : String(err)}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
540
577
|
migrateBootWrapperToCjs(result) {
|
|
541
578
|
if (process.platform !== 'darwin') {
|
|
542
579
|
result.skipped.push('boot-wrapper .cjs: non-darwin, no plist to migrate');
|
|
@@ -2639,6 +2676,24 @@ Rule: I do not state that work landed inside another agent's state unless I have
|
|
|
2639
2676
|
patched = true;
|
|
2640
2677
|
result.upgraded.push('CLAUDE.md: added Cross-Agent Communication Discipline (anti-confabulation) section');
|
|
2641
2678
|
}
|
|
2679
|
+
// Session Boot Self-Knowledge (spec: session-boot-self-knowledge.md).
|
|
2680
|
+
// Existing agents need the rule ("a secret named in your boot block is in
|
|
2681
|
+
// the vault — retrieve, don't re-ask") + the facts writer + the retrieval
|
|
2682
|
+
// script. Content-sniffed on the same heading the template emits.
|
|
2683
|
+
if (!content.includes('**Session Boot Self-Knowledge**')) {
|
|
2684
|
+
const bootSelfKnowledgeSection = `
|
|
2685
|
+
**Session Boot Self-Knowledge** — Your session-start context includes an auto-injected \`<session-self-knowledge>\` block: the NAMES of secrets in your encrypted vault (never values) + self-asserted operational facts about this agent/machine. (Rides the developmentAgent gate until the fleet flip.)
|
|
2686
|
+
- **The rule**: a secret named in your boot block is ALREADY in your vault — retrieve it with \`node .instar/scripts/secret-get.mjs <name>\` (pipe stdout straight into the consuming command, e.g. \`... github_token | gh auth login --with-token\` — NEVER echo the value into chat/transcripts) instead of asking the user to re-send it. Only re-ask if you have evidence it is invalid (expired/revoked/decrypt-failed).
|
|
2687
|
+
- Discover vault key names anytime: \`node .instar/scripts/secret-get.mjs --names\` (names+lengths to stderr) or \`curl -H "Authorization: Bearer $AUTH" "http://localhost:${port}/self-knowledge/session-context?full=1"\`.
|
|
2688
|
+
- **Record a durable operational fact** (a channel path, a logged-in seat, a machine-specific truth worth knowing at every boot): \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/self-knowledge/facts -H 'Content-Type: application/json' -d '{"fact":"..."}'\` (auto-stamped with date+machine). Remove: \`curl -X DELETE -H "Authorization: Bearer $AUTH" http://localhost:${port}/self-knowledge/facts -H 'Content-Type: application/json' -d '{"match":"substring"}'\`. Facts are per-machine and appear at the next session start.
|
|
2689
|
+
- **When to use** (PROACTIVE — this is the trigger): the moment you discover an operational fact future sessions will need (where a tool lives, which machine owns a seat, a non-obvious path), record it as a fact — never leave it to session memory.
|
|
2690
|
+
- If the boot block reports the vault as DECRYPT-FAILED: do NOT repair, rotate, or delete anything — a decrypt failure is usually recoverable; destructive action loses secrets permanently. Surface it to the operator and stop.
|
|
2691
|
+
- Off-switch: \`selfKnowledge.sessionContext.enabled: false\` in \`.instar/config.json\` (applies at the next session start).
|
|
2692
|
+
`;
|
|
2693
|
+
content += '\n' + bootSelfKnowledgeSection;
|
|
2694
|
+
patched = true;
|
|
2695
|
+
result.upgraded.push('CLAUDE.md: added Session Boot Self-Knowledge section');
|
|
2696
|
+
}
|
|
2642
2697
|
// Apprenticeship Program (Step 1, APPRENTICESHIP-STEP1-PROGRAM-SCAFFOLD-SPEC.md).
|
|
2643
2698
|
// Existing agents need to know the program registry + lifecycle gates exist —
|
|
2644
2699
|
// an agent that doesn't know about a capability effectively doesn't have it.
|
|
@@ -2983,6 +3038,26 @@ The attention queue spawns ONE Telegram forum topic per item — right for a gen
|
|
|
2983
3038
|
else {
|
|
2984
3039
|
result.skipped.push('CLAUDE.md: Topic-Flood Guard section already present');
|
|
2985
3040
|
}
|
|
3041
|
+
// Bounded Notification Surface (2026-06-05, flood #3) — extends the
|
|
3042
|
+
// flood-guard awareness with the universal last-resort budget INSIDE
|
|
3043
|
+
// createForumTopic (covers every caller, not just attention items) and the
|
|
3044
|
+
// aggregate-at-the-emitter rule. Idempotent via the unique marker phrase.
|
|
3045
|
+
if (!content.includes('Bounded Notification Surface')) {
|
|
3046
|
+
const section = `
|
|
3047
|
+
### Bounded Notification Surface (universal auto-topic budget)
|
|
3048
|
+
|
|
3049
|
+
Beyond the attention-queue breaker above, the topic-creation primitive itself (\`TelegramAdapter.createForumTopic\`) enforces a LAST-RESORT budget on every automatically-created topic — covering every caller, current and future, no matter what source labels it passes (the 2026-06-05 worktree-detector flood dodged the per-source budget by giving every item a unique source; this ceiling is the layer that cannot be dodged). User-initiated and bounded create-once system topics are exempt; everything else is budgeted by default. Tune via \`messaging[].config.topicCreationBudget\` = \`{ "windowMs": 600000, "maxTopicsPerSource": 8, "maxTopicsGlobal": 12 }\`.
|
|
3050
|
+
|
|
3051
|
+
- If I am building a feature that notifies per-element over a collection: AGGREGATE — one summary item carrying the count and the list, never one item per element. The burst-invariant CI test (\`tests/integration/notification-flood-burst-invariant.test.ts\`) fails any build that violates the bound.
|
|
3052
|
+
- If a topic creation fails with "topic-creation budget exceeded": that is the flood ceiling doing its job — fix the calling feature's volume (aggregate), don't raise the budget.
|
|
3053
|
+
`;
|
|
3054
|
+
content += '\n' + section;
|
|
3055
|
+
patched = true;
|
|
3056
|
+
result.upgraded.push('CLAUDE.md: added Bounded Notification Surface section');
|
|
3057
|
+
}
|
|
3058
|
+
else {
|
|
3059
|
+
result.skipped.push('CLAUDE.md: Bounded Notification Surface section already present');
|
|
3060
|
+
}
|
|
2986
3061
|
// Multi-Machine Session Pool (§L2) — tells the agent about the active-active
|
|
2987
3062
|
// pool + the Machines tab + nickname-based placement/transfer. Without it an
|
|
2988
3063
|
// agent asked "what machines am I on / move this to the mini / where is this
|
|
@@ -4202,6 +4277,11 @@ Create worktrees for collaborator repos with \`instar worktree create <branch>\`
|
|
|
4202
4277
|
'**Coordination Mandate**',
|
|
4203
4278
|
'**ReviewExchange (autonomous code review)**',
|
|
4204
4279
|
'**Cutover Readiness**',
|
|
4280
|
+
// Session Boot Self-Knowledge (spec session-boot-self-knowledge): vault
|
|
4281
|
+
// secret NAMES + operational facts at boot. A Codex/Gemini agent that
|
|
4282
|
+
// never learns the facts writer + secret-get retrieval will re-ask the
|
|
4283
|
+
// user for stored credentials — the exact loop this feature closes.
|
|
4284
|
+
'**Session Boot Self-Knowledge**',
|
|
4205
4285
|
];
|
|
4206
4286
|
for (const shadowName of ['AGENTS.md', 'GEMINI.md']) {
|
|
4207
4287
|
const shadowPath = path.join(this.config.projectDir, shadowName);
|
|
@@ -4411,6 +4491,21 @@ Create worktrees for collaborator repos with \`instar worktree create <branch>\`
|
|
|
4411
4491
|
catch (err) {
|
|
4412
4492
|
result.errors.push(`secret-drop-retrieve.mjs: ${err instanceof Error ? err.message : String(err)}`);
|
|
4413
4493
|
}
|
|
4494
|
+
// Vault retrieval helper — always overwrite (sibling of the above; spec
|
|
4495
|
+
// session-boot-self-knowledge §Retrieval affordance). The boot block names
|
|
4496
|
+
// vault secrets; this is the hardened read path it points at (value →
|
|
4497
|
+
// stdout for piping, names/diagnostics → stderr, never echoed). Without
|
|
4498
|
+
// it, "a secret named here is in your vault" is aspirational.
|
|
4499
|
+
try {
|
|
4500
|
+
const secretGetContent = this.loadRelayTemplate('secret-get.mjs');
|
|
4501
|
+
if (secretGetContent) {
|
|
4502
|
+
fs.writeFileSync(path.join(instarScriptsDir, 'secret-get.mjs'), secretGetContent, { mode: 0o755 });
|
|
4503
|
+
result.upgraded.push('scripts/secret-get.mjs (hardened vault retrieval)');
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
catch (err) {
|
|
4507
|
+
result.errors.push(`secret-get.mjs: ${err instanceof Error ? err.message : String(err)}`);
|
|
4508
|
+
}
|
|
4414
4509
|
// Session-clock injector — always overwrite. New, non-customizable shared
|
|
4415
4510
|
// routine (docs/specs/ROBUST-SESSION-TIME-AWARENESS-SPEC.md Component 2):
|
|
4416
4511
|
// renders the SESSION CLOCK line (render mode for the autonomous-stop-hook,
|
|
@@ -5811,6 +5906,37 @@ except Exception:
|
|
|
5811
5906
|
fi
|
|
5812
5907
|
fi
|
|
5813
5908
|
|
|
5909
|
+
# SESSION BOOT SELF-KNOWLEDGE injection (spec: session-boot-self-knowledge.md).
|
|
5910
|
+
# Fetches /self-knowledge/session-context and injects the deterministic "what I
|
|
5911
|
+
# already have" block: vault secret NAMES (never values) + self-asserted
|
|
5912
|
+
# operational facts — so the agent never re-asks the user for a secret it
|
|
5913
|
+
# already holds and never claims ignorance of a channel it owns. Placed AFTER
|
|
5914
|
+
# the org-intent + preferences blocks (authoritative contract first — this is
|
|
5915
|
+
# background signal; the server wraps it in a <session-self-knowledge
|
|
5916
|
+
# src='boot'> envelope). Fail-open: 503 (dark / disabled) / 404 (version skew:
|
|
5917
|
+
# old server) / unreachable / empty -> silent skip; curl -sf is what makes a
|
|
5918
|
+
# non-2xx emit nothing, and the Bearer token travels ONLY in the header.
|
|
5919
|
+
if [ -n "\$PORT" ] && [ -n "\$TOKEN" ]; then
|
|
5920
|
+
BOOT_SK_RESPONSE=\$(curl -sf --max-time 4 --connect-timeout 1 -H "Authorization: Bearer \$TOKEN" \\
|
|
5921
|
+
"http://localhost:\${PORT}/self-knowledge/session-context" 2>/dev/null)
|
|
5922
|
+
if [ -n "\$BOOT_SK_RESPONSE" ]; then
|
|
5923
|
+
BOOT_SK_BLOCK=\$(echo "\$BOOT_SK_RESPONSE" | python3 -c "
|
|
5924
|
+
import sys, json
|
|
5925
|
+
try:
|
|
5926
|
+
d = json.load(sys.stdin)
|
|
5927
|
+
if d.get('present') and d.get('block'):
|
|
5928
|
+
print(d['block'])
|
|
5929
|
+
except Exception:
|
|
5930
|
+
pass
|
|
5931
|
+
" 2>/dev/null)
|
|
5932
|
+
if [ -n "\$BOOT_SK_BLOCK" ]; then
|
|
5933
|
+
echo ""
|
|
5934
|
+
echo "\$BOOT_SK_BLOCK"
|
|
5935
|
+
echo ""
|
|
5936
|
+
fi
|
|
5937
|
+
fi
|
|
5938
|
+
fi
|
|
5939
|
+
|
|
5814
5940
|
# BEGIN integrated-being-v2
|
|
5815
5941
|
# INTEGRATED-BEING V2 — session-write binding (see docs/specs/integrated-being-ledger-v2.md §3)
|
|
5816
5942
|
# Generates a session UUID, registers with /shared-state/session-bind, writes the
|
|
@@ -6851,6 +6977,41 @@ except Exception:
|
|
|
6851
6977
|
fi
|
|
6852
6978
|
fi
|
|
6853
6979
|
|
|
6980
|
+
# SESSION BOOT SELF-KNOWLEDGE re-injection (spec: session-boot-self-knowledge.md).
|
|
6981
|
+
# A days-long session compacts; the boot block injected at session start only
|
|
6982
|
+
# survives if the compaction summary happens to carry it — willpower, not
|
|
6983
|
+
# structure. Re-fetching here makes the block durable across compaction AND
|
|
6984
|
+
# fresher than the original: a secret stored mid-session appears in the
|
|
6985
|
+
# post-compaction context. Same fail-open contract as the boot fetch: dark /
|
|
6986
|
+
# unreachable / version-skew -> silent skip, header-only Bearer.
|
|
6987
|
+
if [ -f "$INSTAR_DIR/config.json" ]; then
|
|
6988
|
+
BOOT_SK_PORT=\${PORT:-\$(grep -oE '"port"[[:space:]]*:[[:space:]]*[0-9]+' "$INSTAR_DIR/config.json" | head -1 | grep -oE '[0-9]+' | head -1)}
|
|
6989
|
+
BOOT_SK_TOKEN="\${INSTAR_AUTH_TOKEN:-}"
|
|
6990
|
+
if [ -z "\$BOOT_SK_TOKEN" ]; then
|
|
6991
|
+
BOOT_SK_TOKEN=\$(python3 -c "import json; v=json.load(open('$INSTAR_DIR/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
6992
|
+
fi
|
|
6993
|
+
if [ -n "\$BOOT_SK_PORT" ] && [ -n "\$BOOT_SK_TOKEN" ]; then
|
|
6994
|
+
BOOT_SK_RESPONSE=\$(curl -sf --max-time 4 --connect-timeout 1 -H "Authorization: Bearer \$BOOT_SK_TOKEN" \
|
|
6995
|
+
"http://localhost:\${BOOT_SK_PORT}/self-knowledge/session-context" 2>/dev/null)
|
|
6996
|
+
if [ -n "\$BOOT_SK_RESPONSE" ]; then
|
|
6997
|
+
BOOT_SK_BLOCK=\$(echo "\$BOOT_SK_RESPONSE" | python3 -c "
|
|
6998
|
+
import sys, json
|
|
6999
|
+
try:
|
|
7000
|
+
d = json.load(sys.stdin)
|
|
7001
|
+
if d.get('present') and d.get('block'):
|
|
7002
|
+
print(d['block'])
|
|
7003
|
+
except Exception:
|
|
7004
|
+
pass
|
|
7005
|
+
" 2>/dev/null)
|
|
7006
|
+
if [ -n "\$BOOT_SK_BLOCK" ]; then
|
|
7007
|
+
echo ""
|
|
7008
|
+
echo "\$BOOT_SK_BLOCK"
|
|
7009
|
+
echo ""
|
|
7010
|
+
fi
|
|
7011
|
+
fi
|
|
7012
|
+
fi
|
|
7013
|
+
fi
|
|
7014
|
+
|
|
6854
7015
|
echo "=== END IDENTITY RECOVERY ==="
|
|
6855
7016
|
`;
|
|
6856
7017
|
}
|