@jaimevalasek/aioson 1.4.0 → 1.6.0
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/CHANGELOG.md +31 -1
- package/LICENSE +661 -21
- package/README.md +9 -1
- package/docs/design-previews/aurora-command-ui-website.html +884 -0
- package/docs/design-previews/aurora-command-ui.html +682 -0
- package/docs/design-previews/bold-editorial-ui-website.html +658 -0
- package/docs/design-previews/bold-editorial-ui.html +717 -0
- package/docs/design-previews/clean-saas-ui-website.html +1202 -0
- package/docs/design-previews/clean-saas-ui.html +549 -0
- package/docs/design-previews/cognitive-core-ui-website.html +1009 -0
- package/docs/design-previews/cognitive-core-ui.html +463 -0
- package/docs/design-previews/glassmorphism-ui-website.html +572 -0
- package/docs/design-previews/glassmorphism-ui.html +886 -0
- package/docs/design-previews/index.html +699 -0
- package/docs/design-previews/interface-design-website.html +1187 -0
- package/docs/design-previews/interface-design.html +513 -0
- package/docs/design-previews/neo-brutalist-ui-website.html +621 -0
- package/docs/design-previews/neo-brutalist-ui.html +797 -0
- package/docs/design-previews/premium-command-center-ui-website.html +1217 -0
- package/docs/design-previews/premium-command-center-ui.html +552 -0
- package/docs/design-previews/warm-craft-ui-website.html +684 -0
- package/docs/design-previews/warm-craft-ui.html +739 -0
- package/docs/en/cli-reference.md +20 -9
- package/docs/en/squad-dashboard.md +372 -0
- package/docs/openclaw-bridge.md +308 -0
- package/docs/pt/README.md +7 -0
- package/docs/pt/agent-sharding.md +132 -0
- package/docs/pt/agentes.md +131 -11
- package/docs/pt/busca-de-contexto.md +129 -0
- package/docs/pt/cache-de-contexto.md +156 -0
- package/docs/pt/cenarios.md +46 -2
- package/docs/pt/comandos-cli.md +88 -1
- package/docs/pt/design-hybrid-forge.md +107 -0
- package/docs/pt/inicio-rapido.md +72 -5
- package/docs/pt/inteligencia-adaptativa.md +324 -0
- package/docs/pt/monitor-de-contexto.md +104 -0
- package/docs/pt/recuperacao-de-sessao.md +125 -0
- package/docs/pt/sandbox.md +125 -0
- package/docs/pt/skills.md +98 -6
- package/docs/pt/squad-dashboard.md +373 -0
- package/docs/testing/genome-2.0-matrix.md +5 -5
- package/docs/testing/genome-2.0-rollout.md +9 -9
- package/package.json +2 -2
- package/src/agent-loader.js +280 -0
- package/src/backup-local.js +74 -0
- package/src/cli.js +192 -0
- package/src/commands/agent-loader.js +85 -0
- package/src/commands/backup-local-cmd.js +25 -0
- package/src/commands/context-cache.js +90 -0
- package/src/commands/context-monitor.js +92 -0
- package/src/commands/context-search.js +66 -0
- package/src/commands/design-hybrid-options.js +385 -0
- package/src/commands/health.js +214 -0
- package/src/commands/init.js +54 -13
- package/src/commands/install.js +52 -13
- package/src/commands/learning-evolve.js +355 -0
- package/src/commands/live.js +34 -0
- package/src/commands/recovery.js +43 -0
- package/src/commands/runtime.js +242 -0
- package/src/commands/sandbox.js +37 -0
- package/src/commands/setup-context.js +29 -4
- package/src/commands/setup.js +178 -0
- package/src/commands/skill.js +79 -32
- package/src/commands/squad-daemon.js +209 -0
- package/src/commands/squad-dashboard.js +39 -0
- package/src/commands/squad-deploy.js +64 -0
- package/src/commands/squad-doctor.js +52 -0
- package/src/commands/squad-mcp.js +270 -0
- package/src/commands/squad-processes.js +56 -0
- package/src/commands/squad-recovery.js +42 -0
- package/src/commands/squad-roi.js +291 -0
- package/src/commands/squad-score.js +250 -0
- package/src/commands/squad-status.js +37 -1
- package/src/commands/squad-validate.js +62 -1
- package/src/commands/squad-webhook.js +160 -0
- package/src/commands/squad-worker.js +191 -0
- package/src/commands/squad-worktrees.js +75 -0
- package/src/commands/tool-registry-cmd.js +232 -0
- package/src/commands/update.js +7 -0
- package/src/commands/web-map.js +70 -0
- package/src/commands/web-scrape.js +71 -0
- package/src/constants.js +17 -0
- package/src/context-cache.js +159 -0
- package/src/context-search.js +326 -0
- package/src/context-writer.js +45 -1
- package/src/design-variation-catalog.js +503 -0
- package/src/i18n/messages/en.js +159 -3
- package/src/i18n/messages/es.js +147 -2
- package/src/i18n/messages/fr.js +147 -2
- package/src/i18n/messages/pt-BR.js +158 -3
- package/src/install-animation.js +260 -0
- package/src/install-profile.js +143 -0
- package/src/install-wizard.js +474 -0
- package/src/installer.js +38 -10
- package/src/lib/webhook-server.js +328 -0
- package/src/mcp-connectors/registry.js +602 -0
- package/src/parser.js +7 -1
- package/src/recovery-context-session.js +154 -0
- package/src/runtime-store.js +355 -2
- package/src/sandbox.js +177 -0
- package/src/squad/external-session.js +180 -0
- package/src/squad/inter-squad.js +74 -0
- package/src/squad/recovery-context.js +201 -0
- package/src/squad/worktree-manager.js +114 -0
- package/src/squad-daemon.js +490 -0
- package/src/squad-dashboard/api.js +223 -0
- package/src/squad-dashboard/attachment-handler.js +93 -0
- package/src/squad-dashboard/context-monitor.js +157 -0
- package/src/squad-dashboard/execution-logs.js +115 -0
- package/src/squad-dashboard/hunk-review.js +209 -0
- package/src/squad-dashboard/metrics.js +133 -0
- package/src/squad-dashboard/process-monitor.js +125 -0
- package/src/squad-dashboard/renderer.js +858 -0
- package/src/squad-dashboard/server.js +232 -0
- package/src/squad-dashboard/styles.js +525 -0
- package/src/squad-dashboard/token-tracker.js +99 -0
- package/src/tool-executor.js +94 -0
- package/src/updater.js +11 -3
- package/src/web.js +284 -0
- package/src/worker-runner.js +339 -0
- package/template/.aioson/agents/analyst.md +62 -3
- package/template/.aioson/agents/architect.md +42 -0
- package/template/.aioson/agents/design-hybrid-forge.md +127 -0
- package/template/.aioson/agents/dev.md +223 -11
- package/template/.aioson/agents/deyvin.md +65 -0
- package/template/.aioson/agents/neo.md +152 -0
- package/template/.aioson/agents/orache.md +17 -0
- package/template/.aioson/agents/orchestrator.md +26 -0
- package/template/.aioson/agents/pm.md +58 -0
- package/template/.aioson/agents/product.md +88 -12
- package/template/.aioson/agents/qa.md +80 -0
- package/template/.aioson/agents/setup.md +128 -22
- package/template/.aioson/agents/sheldon.md +704 -0
- package/template/.aioson/agents/squad.md +191 -0
- package/template/.aioson/agents/tester.md +410 -0
- package/template/.aioson/agents/ux-ui.md +12 -0
- package/template/.aioson/config.md +21 -0
- package/template/.aioson/context/forensics/.gitkeep +0 -0
- package/template/.aioson/context/seeds/seed-example.md +27 -0
- package/template/.aioson/context/user-profile.md +42 -0
- package/template/.aioson/locales/en/agents/analyst.md +8 -0
- package/template/.aioson/locales/en/agents/architect.md +8 -0
- package/template/.aioson/locales/en/agents/dev.md +66 -7
- package/template/.aioson/locales/en/agents/deyvin.md +8 -0
- package/template/.aioson/locales/en/agents/neo.md +8 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/en/agents/qa.md +49 -0
- package/template/.aioson/locales/en/agents/setup.md +35 -2
- package/template/.aioson/locales/en/agents/sheldon.md +340 -0
- package/template/.aioson/locales/en/agents/ux-ui.md +8 -0
- package/template/.aioson/locales/es/agents/analyst.md +8 -0
- package/template/.aioson/locales/es/agents/architect.md +8 -0
- package/template/.aioson/locales/es/agents/dev.md +66 -7
- package/template/.aioson/locales/es/agents/deyvin.md +8 -0
- package/template/.aioson/locales/es/agents/neo.md +48 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/es/agents/qa.md +26 -0
- package/template/.aioson/locales/es/agents/setup.md +35 -2
- package/template/.aioson/locales/es/agents/sheldon.md +192 -0
- package/template/.aioson/locales/es/agents/squad.md +63 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +8 -0
- package/template/.aioson/locales/fr/agents/analyst.md +8 -0
- package/template/.aioson/locales/fr/agents/architect.md +8 -0
- package/template/.aioson/locales/fr/agents/dev.md +66 -7
- package/template/.aioson/locales/fr/agents/deyvin.md +8 -0
- package/template/.aioson/locales/fr/agents/neo.md +48 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/fr/agents/qa.md +26 -0
- package/template/.aioson/locales/fr/agents/setup.md +35 -2
- package/template/.aioson/locales/fr/agents/sheldon.md +192 -0
- package/template/.aioson/locales/fr/agents/squad.md +63 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +8 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +19 -0
- package/template/.aioson/locales/pt-BR/agents/architect.md +19 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +75 -12
- package/template/.aioson/locales/pt-BR/agents/deyvin.md +8 -0
- package/template/.aioson/locales/pt-BR/agents/neo.md +147 -0
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/pt-BR/agents/product.md +8 -3
- package/template/.aioson/locales/pt-BR/agents/qa.md +60 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +35 -2
- package/template/.aioson/locales/pt-BR/agents/sheldon.md +192 -0
- package/template/.aioson/locales/pt-BR/agents/squad.md +105 -0
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +8 -0
- package/template/.aioson/schemas/squad-blueprint.schema.json +21 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +178 -1
- package/template/.aioson/skills/design/aurora-command-ui/SKILL.md +243 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/art-direction.md +293 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/components.md +827 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/dashboards.md +250 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/design-tokens.md +585 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/motion.md +365 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/patterns.md +482 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/websites.md +387 -0
- package/template/.aioson/skills/design/bold-editorial-ui/SKILL.md +205 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/art-direction.md +338 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/components.md +977 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/dashboards.md +218 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/design-tokens.md +326 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/motion.md +461 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/patterns.md +293 -0
- package/template/.aioson/skills/design/bold-editorial-ui/references/websites.md +352 -0
- package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +210 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +319 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/components.md +365 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/dashboards.md +196 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/design-tokens.md +244 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +235 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/patterns.md +215 -0
- package/template/.aioson/skills/design/clean-saas-ui/references/websites.md +295 -0
- package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +55 -9
- package/template/.aioson/skills/design/cognitive-core-ui/references/art-direction.md +339 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +1 -1
- package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +100 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +43 -9
- package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +40 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +1 -1
- package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +99 -12
- package/template/.aioson/skills/design/glassmorphism-ui/SKILL.md +222 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/art-direction.md +159 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/components.md +498 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/dashboards.md +236 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/design-tokens.md +274 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/motion.md +355 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/patterns.md +198 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/websites.md +307 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +213 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/art-direction.md +228 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/components.md +855 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/dashboards.md +334 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/design-tokens.md +342 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/motion.md +286 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/patterns.md +458 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/websites.md +723 -0
- package/template/.aioson/skills/design/warm-craft-ui/SKILL.md +209 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/art-direction.md +324 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/components.md +508 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/dashboards.md +223 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/design-tokens.md +374 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/motion.md +356 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/patterns.md +288 -0
- package/template/.aioson/skills/design/warm-craft-ui/references/websites.md +289 -0
- package/template/.aioson/skills/premium-visual-design/SKILL.md +83 -0
- package/template/.aioson/skills/premium-visual-design/components/agent-badge.md +92 -0
- package/template/.aioson/skills/premium-visual-design/components/dependency-node.md +102 -0
- package/template/.aioson/skills/premium-visual-design/components/mention-autocomplete.md +136 -0
- package/template/.aioson/skills/premium-visual-design/components/notification-center.md +136 -0
- package/template/.aioson/skills/premium-visual-design/components/review-action-bar.md +188 -0
- package/template/.aioson/skills/premium-visual-design/components/team-switcher.md +131 -0
- package/template/.aioson/skills/premium-visual-design/patterns/agent-message-thread.md +198 -0
- package/template/.aioson/skills/premium-visual-design/patterns/notification-panel.md +275 -0
- package/template/.aioson/skills/premium-visual-design/patterns/review-workflow-ui.md +234 -0
- package/template/.aioson/skills/premium-visual-design/patterns/task-dependency-graph.md +147 -0
- package/template/.aioson/skills/premium-visual-design/tokens/status-extended.md +142 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +45 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +109 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +44 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +37 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/hardening-lane.md +49 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +66 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +75 -0
- package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +144 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/crossover-protocol.md +221 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/naming-registry.md +88 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +291 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +117 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +188 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/variation-library.md +125 -0
- package/template/.aioson/skills/squad/formats/catalog.json +15 -0
- package/template/.aioson/skills/squad/formats/content/blog-post.md +47 -0
- package/template/.aioson/skills/squad/formats/content/newsletter.md +47 -0
- package/template/.aioson/skills/squad/formats/creative/podcast-script.md +43 -0
- package/template/.aioson/skills/squad/formats/creative/video-script.md +41 -0
- package/template/.aioson/skills/squad/formats/social/instagram-feed.md +42 -0
- package/template/.aioson/skills/squad/formats/social/linkedin-post.md +42 -0
- package/template/.aioson/skills/squad/formats/social/tiktok.md +39 -0
- package/template/.aioson/skills/squad/formats/social/twitter-thread.md +39 -0
- package/template/.aioson/skills/squad/formats/social/youtube-long.md +47 -0
- package/template/.aioson/skills/squad/formats/social/youtube-shorts.md +39 -0
- package/template/.aioson/skills/squad/patterns/multi-platform-pattern.md +108 -0
- package/template/.aioson/skills/squad/patterns/persona-based-pattern.md +98 -0
- package/template/.aioson/skills/squad/patterns/pipeline-pattern.md +106 -0
- package/template/.aioson/skills/squad/patterns/review-loop-pattern.md +81 -0
- package/template/.aioson/skills/squad/references/checklist-templates.md +122 -0
- package/template/.aioson/skills/squad/references/executor-archetypes.md +123 -0
- package/template/.aioson/skills/squad/references/workflow-templates.md +169 -0
- package/template/.aioson/skills/static/debugging-protocol.md +42 -0
- package/template/.aioson/skills/static/git-worktrees.md +36 -0
- package/template/.aioson/tasks/implementation-plan.md +19 -0
- package/template/.aioson/tasks/squad-design.md +28 -0
- package/template/.aioson/tasks/squad-profile.md +48 -0
- package/template/.aioson/tasks/squad-review.md +61 -0
- package/template/.aioson/tasks/squad-task-decompose.md +66 -0
- package/template/.claude/commands/aioson/agent/neo.md +5 -0
- package/template/.claude/commands/aioson/agent/tester.md +5 -0
- package/template/.gemini/GEMINI.md +1 -0
- package/template/.gemini/commands/aios-neo.toml +4 -0
- package/template/.gemini/commands/aios-tester.toml +6 -0
- package/template/AGENTS.md +26 -1
- package/template/CLAUDE.md +6 -2
- package/template/OPENCODE.md +2 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const {
|
|
6
|
+
getSquadOverview,
|
|
7
|
+
getRecentContent,
|
|
8
|
+
getLearnings,
|
|
9
|
+
getRecentDeliveries,
|
|
10
|
+
getRecentEvents,
|
|
11
|
+
getSquadMetrics
|
|
12
|
+
} = require('./metrics');
|
|
13
|
+
const { getContextUsage } = require('./context-monitor');
|
|
14
|
+
const { getTokenUsage } = require('./token-tracker');
|
|
15
|
+
const { getActiveProcesses, stopProcess } = require('./process-monitor');
|
|
16
|
+
const { getHunks, updateHunk, getReviewProgress, HUNK_STATES } = require('./hunk-review');
|
|
17
|
+
const { generateRecovery, readRecovery } = require('../squad/recovery-context');
|
|
18
|
+
const { getLogsForTask, getSessionLog } = require('./execution-logs');
|
|
19
|
+
|
|
20
|
+
const SQUADS_DIR = path.join('.aioson', 'squads');
|
|
21
|
+
|
|
22
|
+
async function loadSquadList(projectDir) {
|
|
23
|
+
const squadsDir = path.join(projectDir, SQUADS_DIR);
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = await fs.readdir(squadsDir, { withFileTypes: true });
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const squads = [];
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (!entry.isDirectory()) continue;
|
|
34
|
+
const manifestPath = path.join(squadsDir, entry.name, 'squad.manifest.json');
|
|
35
|
+
try {
|
|
36
|
+
const raw = await fs.readFile(manifestPath, 'utf8');
|
|
37
|
+
const manifest = JSON.parse(raw);
|
|
38
|
+
squads.push({
|
|
39
|
+
slug: entry.name,
|
|
40
|
+
name: manifest.name || entry.name,
|
|
41
|
+
mode: manifest.mode || 'mixed',
|
|
42
|
+
goal: manifest.goal || '',
|
|
43
|
+
status: manifest.status || 'active',
|
|
44
|
+
executorCount: (manifest.executors || []).length,
|
|
45
|
+
manifest
|
|
46
|
+
});
|
|
47
|
+
} catch {
|
|
48
|
+
squads.push({
|
|
49
|
+
slug: entry.name,
|
|
50
|
+
name: entry.name,
|
|
51
|
+
mode: 'unknown',
|
|
52
|
+
goal: '',
|
|
53
|
+
status: 'unknown',
|
|
54
|
+
executorCount: 0,
|
|
55
|
+
manifest: null
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return squads;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function detectPanels(manifest) {
|
|
63
|
+
const panels = ['overview', 'content', 'learnings', 'logs'];
|
|
64
|
+
|
|
65
|
+
if (manifest) {
|
|
66
|
+
if (manifest.mode === 'content') panels.push('content-preview');
|
|
67
|
+
if (manifest.mode === 'software') panels.push('tasks');
|
|
68
|
+
|
|
69
|
+
const hasWebhooks = manifest.outputStrategy &&
|
|
70
|
+
manifest.outputStrategy.delivery &&
|
|
71
|
+
Array.isArray(manifest.outputStrategy.delivery.webhooks) &&
|
|
72
|
+
manifest.outputStrategy.delivery.webhooks.length > 0;
|
|
73
|
+
const hasMcps = Array.isArray(manifest.mcps) && manifest.mcps.length > 0;
|
|
74
|
+
if (hasWebhooks || hasMcps) panels.push('integrations');
|
|
75
|
+
|
|
76
|
+
const channelMcps = (manifest.mcps || []).filter(function (m) {
|
|
77
|
+
return ['whatsapp', 'telegram', 'sms', 'voice'].some(function (ch) {
|
|
78
|
+
return (m.slug || '').includes(ch);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
if (channelMcps.length > 0) panels.push('channels');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
panels.push('metrics');
|
|
85
|
+
panels.push('processes');
|
|
86
|
+
return panels;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function loadSquadData(db, squadSlug) {
|
|
90
|
+
const overview = getSquadOverview(db, squadSlug);
|
|
91
|
+
const content = getRecentContent(db, squadSlug);
|
|
92
|
+
const learnings = getLearnings(db, squadSlug);
|
|
93
|
+
const deliveries = getRecentDeliveries(db, squadSlug);
|
|
94
|
+
const events = getRecentEvents(db, squadSlug);
|
|
95
|
+
const customMetrics = getSquadMetrics(db, squadSlug);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
overview,
|
|
99
|
+
content,
|
|
100
|
+
learnings,
|
|
101
|
+
deliveries,
|
|
102
|
+
events,
|
|
103
|
+
customMetrics,
|
|
104
|
+
pipelineInfo: overview.pipelineInfo,
|
|
105
|
+
metrics: {
|
|
106
|
+
content_items: overview.contentItems,
|
|
107
|
+
sessions: overview.sessions,
|
|
108
|
+
learnings: overview.learnings,
|
|
109
|
+
delivery_rate: overview.deliveryRate
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* GET /api/squads/:slug/context
|
|
116
|
+
* Returns context usage with warning levels for all agents in a squad.
|
|
117
|
+
*/
|
|
118
|
+
async function getContextData(projectDir, squadSlug, agentSlug) {
|
|
119
|
+
return getContextUsage(projectDir, squadSlug, agentSlug || null);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* GET /api/squads/:slug/tokens?breakdown=true
|
|
124
|
+
* Returns token usage with cost estimates and waste flags.
|
|
125
|
+
*/
|
|
126
|
+
async function getTokenData(projectDir, squadSlug, breakdown) {
|
|
127
|
+
return getTokenUsage(projectDir, squadSlug, breakdown);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* GET /api/processes[?squad=:slug]
|
|
132
|
+
* Returns active agent processes across all squads (or filtered by squad).
|
|
133
|
+
*/
|
|
134
|
+
async function getProcesses(projectDir, squadSlug) {
|
|
135
|
+
return getActiveProcesses(projectDir, squadSlug || null);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* POST /api/processes/:pid/stop
|
|
140
|
+
* Sends SIGTERM to the process and removes its process file.
|
|
141
|
+
*/
|
|
142
|
+
async function stopProcessById(projectDir, pid) {
|
|
143
|
+
return stopProcess(projectDir, pid);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* GET /api/tasks/:id/hunks
|
|
148
|
+
* Returns hunk review state for a task.
|
|
149
|
+
* Requires squadSlug and optionally diff (to init on first call).
|
|
150
|
+
*/
|
|
151
|
+
async function getTaskHunks(projectDir, squadSlug, taskId, diff) {
|
|
152
|
+
return getHunks(projectDir, squadSlug, taskId, diff || null);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* POST /api/tasks/:id/hunks/:hunkId/approve
|
|
157
|
+
*/
|
|
158
|
+
async function approveHunk(projectDir, squadSlug, taskId, hunkId) {
|
|
159
|
+
return updateHunk(projectDir, squadSlug, taskId, hunkId, HUNK_STATES.APPROVED, null);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* POST /api/tasks/:id/hunks/:hunkId/reject
|
|
164
|
+
*/
|
|
165
|
+
async function rejectHunk(projectDir, squadSlug, taskId, hunkId, comment) {
|
|
166
|
+
return updateHunk(projectDir, squadSlug, taskId, hunkId, HUNK_STATES.REJECTED, comment || null);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* POST /api/tasks/:id/hunks/:hunkId/comment
|
|
171
|
+
*/
|
|
172
|
+
async function commentHunk(projectDir, squadSlug, taskId, hunkId, comment) {
|
|
173
|
+
return updateHunk(projectDir, squadSlug, taskId, hunkId, HUNK_STATES.REVISED, comment || null);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* GET /api/squads/:slug/agents/:agent/recovery
|
|
178
|
+
* Returns the recovery-context.md for an agent (generates if missing).
|
|
179
|
+
*/
|
|
180
|
+
async function getAgentRecovery(projectDir, squadSlug, agentSlug) {
|
|
181
|
+
// Try to read existing first
|
|
182
|
+
const existing = await readRecovery(projectDir, squadSlug);
|
|
183
|
+
if (existing) return { ok: true, content: existing, squadSlug, agentSlug };
|
|
184
|
+
// Generate on demand
|
|
185
|
+
const result = await generateRecovery(projectDir, squadSlug, agentSlug);
|
|
186
|
+
if (!result.ok) return result;
|
|
187
|
+
const content = await readRecovery(projectDir, squadSlug);
|
|
188
|
+
return { ok: true, content, squadSlug, agentSlug, tokens: result.tokens };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* GET /api/squads/:slug/tasks/:taskId/logs
|
|
193
|
+
* Returns all session logs for a task, sorted oldest-first.
|
|
194
|
+
*/
|
|
195
|
+
async function getTaskLogs(projectDir, squadSlug, taskId) {
|
|
196
|
+
return getLogsForTask(projectDir, squadSlug, taskId);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* GET /api/squads/:slug/tasks/:taskId/logs/:sessionId
|
|
201
|
+
* Returns a single session log by sessionId.
|
|
202
|
+
*/
|
|
203
|
+
async function getTaskSessionLog(projectDir, squadSlug, taskId, sessionId) {
|
|
204
|
+
return getSessionLog(projectDir, squadSlug, taskId, sessionId);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = {
|
|
208
|
+
loadSquadList,
|
|
209
|
+
detectPanels,
|
|
210
|
+
loadSquadData,
|
|
211
|
+
getContextData,
|
|
212
|
+
getTokenData,
|
|
213
|
+
getProcesses,
|
|
214
|
+
stopProcessById,
|
|
215
|
+
getTaskHunks,
|
|
216
|
+
approveHunk,
|
|
217
|
+
rejectHunk,
|
|
218
|
+
commentHunk,
|
|
219
|
+
getAgentRecovery,
|
|
220
|
+
getReviewProgress,
|
|
221
|
+
getTaskLogs,
|
|
222
|
+
getTaskSessionLog
|
|
223
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const SQUADS_DIR = path.join('.aioson', 'squads');
|
|
7
|
+
|
|
8
|
+
const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']);
|
|
9
|
+
|
|
10
|
+
const MIME_MAP = {
|
|
11
|
+
'.png': 'image/png',
|
|
12
|
+
'.jpg': 'image/jpeg',
|
|
13
|
+
'.jpeg': 'image/jpeg',
|
|
14
|
+
'.gif': 'image/gif',
|
|
15
|
+
'.webp': 'image/webp',
|
|
16
|
+
'.svg': 'image/svg+xml',
|
|
17
|
+
'.pdf': 'application/pdf',
|
|
18
|
+
'.txt': 'text/plain',
|
|
19
|
+
'.md': 'text/markdown',
|
|
20
|
+
'.json': 'application/json'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function attachmentsDir(projectDir, squadSlug) {
|
|
24
|
+
return path.join(projectDir, SQUADS_DIR, squadSlug, 'attachments');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function safeName(filename) {
|
|
28
|
+
return path.basename(filename).replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Save a Buffer or string as an attachment.
|
|
33
|
+
* Returns { ok, filename, filePath, isImage }.
|
|
34
|
+
*/
|
|
35
|
+
async function saveAttachment(projectDir, squadSlug, filename, buffer) {
|
|
36
|
+
const dir = attachmentsDir(projectDir, squadSlug);
|
|
37
|
+
await fs.mkdir(dir, { recursive: true });
|
|
38
|
+
const safe = safeName(filename);
|
|
39
|
+
const dest = path.join(dir, safe);
|
|
40
|
+
await fs.writeFile(dest, buffer);
|
|
41
|
+
const ext = path.extname(safe).toLowerCase();
|
|
42
|
+
return { ok: true, filePath: dest, filename: safe, isImage: IMAGE_EXTS.has(ext) };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* List all attachments for a squad.
|
|
47
|
+
* Returns array of { filename, filePath, isImage, mime, size }.
|
|
48
|
+
*/
|
|
49
|
+
async function listAttachments(projectDir, squadSlug) {
|
|
50
|
+
const dir = attachmentsDir(projectDir, squadSlug);
|
|
51
|
+
let entries;
|
|
52
|
+
try {
|
|
53
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
54
|
+
} catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const result = [];
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (!entry.isFile()) continue;
|
|
60
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
61
|
+
let size = 0;
|
|
62
|
+
try {
|
|
63
|
+
const stat = await fs.stat(path.join(dir, entry.name));
|
|
64
|
+
size = stat.size;
|
|
65
|
+
} catch { /* ignore */ }
|
|
66
|
+
result.push({
|
|
67
|
+
filename: entry.name,
|
|
68
|
+
filePath: path.join(dir, entry.name),
|
|
69
|
+
isImage: IMAGE_EXTS.has(ext),
|
|
70
|
+
mime: MIME_MAP[ext] || 'application/octet-stream',
|
|
71
|
+
size
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Read an attachment file for serving (e.g. inline image preview).
|
|
79
|
+
* Returns { ok, buffer, mime, filename } or { ok: false }.
|
|
80
|
+
*/
|
|
81
|
+
async function readAttachment(projectDir, squadSlug, filename) {
|
|
82
|
+
const safe = safeName(filename);
|
|
83
|
+
const filePath = path.join(attachmentsDir(projectDir, squadSlug), safe);
|
|
84
|
+
try {
|
|
85
|
+
const buffer = await fs.readFile(filePath);
|
|
86
|
+
const ext = path.extname(safe).toLowerCase();
|
|
87
|
+
return { ok: true, buffer, mime: MIME_MAP[ext] || 'application/octet-stream', filename: safe };
|
|
88
|
+
} catch {
|
|
89
|
+
return { ok: false };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { saveAttachment, listAttachments, readAttachment, IMAGE_EXTS, MIME_MAP };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { generateRecovery, shouldRefreshOnEvent } = require('../squad/recovery-context');
|
|
6
|
+
|
|
7
|
+
const SQUADS_DIR = path.join('.aioson', 'squads');
|
|
8
|
+
|
|
9
|
+
// Minimum ratio drop between consecutive measurements to be considered a compact
|
|
10
|
+
const COMPACT_DROP_THRESHOLD = 0.30;
|
|
11
|
+
|
|
12
|
+
// Warning level thresholds (ratio of used/windowSize)
|
|
13
|
+
const THRESHOLDS = { warning: 0.85, critical: 0.95 };
|
|
14
|
+
|
|
15
|
+
// Notification event types (dispatched by caller when notification system is available)
|
|
16
|
+
const EVENTS = {
|
|
17
|
+
CONTEXT_WARNING: 'context_warning',
|
|
18
|
+
CONTEXT_CRITICAL: 'context_critical'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Context categories (6) — order determines donut segment order
|
|
22
|
+
const CATEGORIES = [
|
|
23
|
+
'system_prompt',
|
|
24
|
+
'conversation_history',
|
|
25
|
+
'tool_outputs',
|
|
26
|
+
'files_loaded',
|
|
27
|
+
'inline_data',
|
|
28
|
+
'other'
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function computeWarningLevel(used, windowSize) {
|
|
32
|
+
if (!windowSize || windowSize <= 0) return 'unknown';
|
|
33
|
+
const ratio = used / windowSize;
|
|
34
|
+
if (ratio >= 1.0) return 'overflow';
|
|
35
|
+
if (ratio >= THRESHOLDS.critical) return 'critical';
|
|
36
|
+
if (ratio >= THRESHOLDS.warning) return 'warning';
|
|
37
|
+
return 'normal';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Read context-monitor.json for a squad and compute warning levels.
|
|
42
|
+
* @param {string} projectDir
|
|
43
|
+
* @param {string} squadSlug
|
|
44
|
+
* @param {string|null} agentSlug — if set, return only that agent
|
|
45
|
+
* @returns {object|null}
|
|
46
|
+
*/
|
|
47
|
+
async function getContextUsage(projectDir, squadSlug, agentSlug) {
|
|
48
|
+
const filePath = path.join(projectDir, SQUADS_DIR, squadSlug, 'context-monitor.json');
|
|
49
|
+
let data;
|
|
50
|
+
try {
|
|
51
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
52
|
+
data = JSON.parse(raw);
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const agents = data.agents || {};
|
|
58
|
+
|
|
59
|
+
if (agentSlug) {
|
|
60
|
+
const agent = agents[agentSlug];
|
|
61
|
+
if (!agent) return null;
|
|
62
|
+
const warningLevel = computeWarningLevel(agent.totalUsed || 0, agent.windowSize || 0);
|
|
63
|
+
return { squadSlug, agentSlug, ...agent, warningLevel };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const enrichedAgents = {};
|
|
67
|
+
for (const [slug, agent] of Object.entries(agents)) {
|
|
68
|
+
enrichedAgents[slug] = {
|
|
69
|
+
...agent,
|
|
70
|
+
warningLevel: computeWarningLevel(agent.totalUsed || 0, agent.windowSize || 0)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return { squadSlug, agents: enrichedAgents, updatedAt: data.updatedAt };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Return pending notification events for a context snapshot.
|
|
78
|
+
* Caller dispatches these once the notification system exists.
|
|
79
|
+
*/
|
|
80
|
+
function checkNotificationEvents(squadSlug, contextData) {
|
|
81
|
+
if (!contextData || !contextData.agents) return [];
|
|
82
|
+
const events = [];
|
|
83
|
+
for (const [agentSlug, agent] of Object.entries(contextData.agents)) {
|
|
84
|
+
if (agent.warningLevel === 'critical' || agent.warningLevel === 'overflow') {
|
|
85
|
+
events.push({ type: EVENTS.CONTEXT_CRITICAL, squadSlug, agentSlug, warningLevel: agent.warningLevel });
|
|
86
|
+
} else if (agent.warningLevel === 'warning') {
|
|
87
|
+
events.push({ type: EVENTS.CONTEXT_WARNING, squadSlug, agentSlug, warningLevel: agent.warningLevel });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return events;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Detect if a context compact occurred between two consecutive measurements.
|
|
95
|
+
* A compact is inferred when totalUsed drops by > 30% from the previous snapshot.
|
|
96
|
+
* @param {number} prevUsed — previous totalUsed value
|
|
97
|
+
* @param {number} currUsed — current totalUsed value
|
|
98
|
+
* @returns {boolean}
|
|
99
|
+
*/
|
|
100
|
+
function isCompactDetected(prevUsed, currUsed) {
|
|
101
|
+
if (!prevUsed || prevUsed <= 0) return false;
|
|
102
|
+
const drop = (prevUsed - currUsed) / prevUsed;
|
|
103
|
+
return drop > COMPACT_DROP_THRESHOLD;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Compare two context-monitor snapshots and trigger recovery injection if a
|
|
108
|
+
* compact is detected for any agent.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} projectDir
|
|
111
|
+
* @param {string} squadSlug
|
|
112
|
+
* @param {object} prevData — previous result from getContextUsage (or null)
|
|
113
|
+
* @param {object} currData — current result from getContextUsage
|
|
114
|
+
* @returns {Array<{agentSlug, recovery}>} list of recovery results triggered
|
|
115
|
+
*/
|
|
116
|
+
async function checkAndInjectRecovery(projectDir, squadSlug, prevData, currData) {
|
|
117
|
+
if (!prevData || !currData) return [];
|
|
118
|
+
const prevAgents = prevData.agents || {};
|
|
119
|
+
const currAgents = currData.agents || {};
|
|
120
|
+
const triggered = [];
|
|
121
|
+
|
|
122
|
+
for (const [agentSlug, curr] of Object.entries(currAgents)) {
|
|
123
|
+
const prev = prevAgents[agentSlug];
|
|
124
|
+
if (!prev) continue;
|
|
125
|
+
if (isCompactDetected(prev.totalUsed || 0, curr.totalUsed || 0)) {
|
|
126
|
+
const recovery = await generateRecovery(projectDir, squadSlug, agentSlug);
|
|
127
|
+
triggered.push({ agentSlug, recovery });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return triggered;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Optionally trigger a recovery refresh when a specific runtime event fires.
|
|
136
|
+
* @param {string} projectDir
|
|
137
|
+
* @param {string} squadSlug
|
|
138
|
+
* @param {string} agentSlug
|
|
139
|
+
* @param {string} eventType
|
|
140
|
+
*/
|
|
141
|
+
async function onRuntimeEvent(projectDir, squadSlug, agentSlug, eventType) {
|
|
142
|
+
if (!shouldRefreshOnEvent(eventType)) return null;
|
|
143
|
+
return generateRecovery(projectDir, squadSlug, agentSlug);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = {
|
|
147
|
+
getContextUsage,
|
|
148
|
+
computeWarningLevel,
|
|
149
|
+
checkNotificationEvents,
|
|
150
|
+
isCompactDetected,
|
|
151
|
+
checkAndInjectRecovery,
|
|
152
|
+
onRuntimeEvent,
|
|
153
|
+
CATEGORIES,
|
|
154
|
+
EVENTS,
|
|
155
|
+
THRESHOLDS,
|
|
156
|
+
COMPACT_DROP_THRESHOLD
|
|
157
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const SQUADS_DIR = path.join('.aioson', 'squads');
|
|
7
|
+
|
|
8
|
+
const ENTRY_TYPES = {
|
|
9
|
+
TOOL_CALL: 'tool_call',
|
|
10
|
+
REASONING: 'reasoning',
|
|
11
|
+
MILESTONE: 'milestone',
|
|
12
|
+
ERROR: 'error'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Session log file schema:
|
|
17
|
+
* {
|
|
18
|
+
* agentSlug: string,
|
|
19
|
+
* taskId: string,
|
|
20
|
+
* startedAt: ISO string,
|
|
21
|
+
* summary: string | null,
|
|
22
|
+
* entries: Array<{
|
|
23
|
+
* type: 'tool_call' | 'reasoning' | 'milestone' | 'error',
|
|
24
|
+
* timestamp: ISO string,
|
|
25
|
+
* // tool_call
|
|
26
|
+
* toolName?: string,
|
|
27
|
+
* input?: any,
|
|
28
|
+
* output?: any,
|
|
29
|
+
* durationMs?: number,
|
|
30
|
+
* // reasoning
|
|
31
|
+
* text?: string,
|
|
32
|
+
* // milestone
|
|
33
|
+
* label?: string,
|
|
34
|
+
* // error
|
|
35
|
+
* message?: string,
|
|
36
|
+
* stack?: string
|
|
37
|
+
* }>
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* File path: .aioson/squads/{slug}/logs/{task-id}/session-{timestamp}.json
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
function logsDir(projectDir, squadSlug, taskId) {
|
|
44
|
+
return path.join(projectDir, SQUADS_DIR, squadSlug, 'logs', taskId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function listSessionFiles(projectDir, squadSlug, taskId) {
|
|
48
|
+
const dir = logsDir(projectDir, squadSlug, taskId);
|
|
49
|
+
let entries;
|
|
50
|
+
try {
|
|
51
|
+
entries = await fs.readdir(dir);
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
return entries
|
|
56
|
+
.filter(f => f.startsWith('session-') && f.endsWith('.json'))
|
|
57
|
+
.map(f => {
|
|
58
|
+
const ts = f.replace(/^session-/, '').replace(/\.json$/, '');
|
|
59
|
+
return { sessionId: f.replace('.json', ''), filename: f, timestamp: ts, filePath: path.join(dir, f) };
|
|
60
|
+
})
|
|
61
|
+
.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns all sessions (with entries) for a given task, sorted oldest-first.
|
|
66
|
+
*/
|
|
67
|
+
async function getLogsForTask(projectDir, squadSlug, taskId) {
|
|
68
|
+
const files = await listSessionFiles(projectDir, squadSlug, taskId);
|
|
69
|
+
const sessions = [];
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
try {
|
|
72
|
+
const raw = await fs.readFile(file.filePath, 'utf8');
|
|
73
|
+
const session = JSON.parse(raw);
|
|
74
|
+
sessions.push({
|
|
75
|
+
sessionId: file.sessionId,
|
|
76
|
+
timestamp: file.timestamp,
|
|
77
|
+
taskId,
|
|
78
|
+
squadSlug,
|
|
79
|
+
agentSlug: session.agentSlug || null,
|
|
80
|
+
startedAt: session.startedAt || file.timestamp,
|
|
81
|
+
summary: session.summary || null,
|
|
82
|
+
entries: session.entries || []
|
|
83
|
+
});
|
|
84
|
+
} catch {
|
|
85
|
+
sessions.push({
|
|
86
|
+
sessionId: file.sessionId,
|
|
87
|
+
timestamp: file.timestamp,
|
|
88
|
+
taskId,
|
|
89
|
+
squadSlug,
|
|
90
|
+
agentSlug: null,
|
|
91
|
+
startedAt: file.timestamp,
|
|
92
|
+
summary: null,
|
|
93
|
+
entries: [],
|
|
94
|
+
parseError: true
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return sessions;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Returns a single session log by sessionId (filename without .json).
|
|
103
|
+
*/
|
|
104
|
+
async function getSessionLog(projectDir, squadSlug, taskId, sessionId) {
|
|
105
|
+
const dir = logsDir(projectDir, squadSlug, taskId);
|
|
106
|
+
const filePath = path.join(dir, `${sessionId}.json`);
|
|
107
|
+
try {
|
|
108
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
109
|
+
return JSON.parse(raw);
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { ENTRY_TYPES, getLogsForTask, getSessionLog };
|