Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6db993e
feat(memory): ingest coding-agent sessions
senamakel Jul 14, 2026
eff1f78
chore(tauri): sync TinyCortex lockfile
senamakel Jul 14, 2026
e9035c2
ci: initialize nested TinyCortex submodules
senamakel Jul 14, 2026
6f0fa4f
fix(memory): cap coding-session status scans
senamakel Jul 14, 2026
974046f
Merge branch 'main' into pr/4863
M3gA-Mind Jul 14, 2026
313e7c3
fix(memory): parse stored category through FromStr to strip custom: p…
M3gA-Mind Jul 14, 2026
92c309c
fix(memory): parse TinyCortex custom categories
senamakel Jul 14, 2026
e726c5f
Merge branch 'main' into pr/4863
M3gA-Mind Jul 14, 2026
003065b
fix(memory): strip custom: prefix when storing echoed wire categories
M3gA-Mind Jul 14, 2026
73985a8
test(memory): include coding session controllers
senamakel Jul 14, 2026
3cbb280
Merge branch 'main' into pr/4863
M3gA-Mind Jul 14, 2026
8296d30
fix(memory): address review — ingestion timeout, error diagnostics, i18n
M3gA-Mind Jul 14, 2026
998d297
fix(memory): log enqueue-extract outcome + error path
M3gA-Mind Jul 14, 2026
552357b
Merge branch 'main' into pr/4863
M3gA-Mind Jul 14, 2026
f6b0b80
fix(memory): address ingestion review feedback
senamakel Jul 14, 2026
5f20d8b
test(memory): align raw coverage with tinycortex
senamakel Jul 14, 2026
d4f05f5
test(composio): isolate provider mock backend
senamakel Jul 14, 2026
5331e32
fix(memory): bound coding session ingestion batches
senamakel Jul 14, 2026
b871d09
test(tool-registry): serialize denial buffer cases
senamakel Jul 14, 2026
29b1de8
fix(agent): reject tool calls in wrap-up replies
senamakel Jul 14, 2026
85514fb
fix(agent): parse textual wrap-up tool calls
senamakel Jul 14, 2026
9af3c22
test(memory): tolerate transient queue gate contention
senamakel Jul 14, 2026
2f88c98
test(memory): align raw fixtures with tinycortex contracts
senamakel Jul 14, 2026
0ee940e
test(composio): isolate direct mode config reload
senamakel Jul 14, 2026
f8364be
fix(memory): address session ingestion review gaps
senamakel Jul 14, 2026
0131ec2
test(agent): align buffered wrap-up coverage contract
senamakel Jul 14, 2026
6f233fd
ci(release): initialize nested CLI submodules
senamakel Jul 14, 2026
66e3569
test(flows): isolate schema catalog fixtures
senamakel Jul 15, 2026
6b82ce6
test(e2e): register coding session memory flow
senamakel Jul 15, 2026
0f02c2e
fix(memory): bound coding session status reads
senamakel Jul 15, 2026
bdf672e
Merge remote-tracking branch 'upstream/main' into feat/tinycortex-ses…
senamakel Jul 15, 2026
2a02f3a
Merge remote-tracking branch 'upstream/main' into feat/tinycortex-ses…
senamakel Jul 15, 2026
3afa56a
fix(memory): satisfy warning-free clippy
senamakel Jul 15, 2026
bdc8d73
fix(analytics): identify coding session import action
senamakel Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-lite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ jobs:
- name: Init tinycortex submodule
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git submodule update --init vendor/tinycortex
git submodule update --init --recursive vendor/tinycortex

- name: Cache TinyCortex build artifacts
uses: Swatinem/rust-cache@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
with:
ref: ${{ github.event.release.tag_name }}
fetch-depth: 1
submodules: true
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@1.93.0
- name: Cache Cargo
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ jobs:
# fork the core image doesn't need. The Dockerfile COPYs vendor/ because
# [patch.crates-io] resolves Rust SDK crates from vendor/.
- name: Init vendored Rust submodules
run: git submodule update --init vendor/tinyagents vendor/tinyflows vendor/tinycortex vendor/tinyjuice vendor/tinychannels vendor/tinyplace
run: git submodule update --init --recursive vendor/tinyagents vendor/tinyflows vendor/tinycortex vendor/tinyjuice vendor/tinychannels vendor/tinyplace
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ jobs:
# fork the core image doesn't need. The Dockerfile COPYs vendor/ because
# [patch.crates-io] resolves Rust SDK crates from vendor/.
- name: Init vendored Rust submodules
run: git submodule update --init vendor/tinyagents vendor/tinyflows vendor/tinycortex vendor/tinyjuice vendor/tinychannels vendor/tinyplace
run: git submodule update --init --recursive vendor/tinyagents vendor/tinyflows vendor/tinycortex vendor/tinyjuice vendor/tinychannels vendor/tinyplace
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build image (no push)
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ jobs:
- name: Init tinycortex submodule
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git submodule update --init vendor/tinycortex
git submodule update --init --recursive vendor/tinycortex

- name: Cache TinyCortex build artifacts
uses: Swatinem/rust-cache@v2
Expand Down
23 changes: 20 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ tinyagents = { version = "1.7", features = ["sqlite", "repl"] }
# security gating, and the global singleton stay host-side. rusqlite/git2 are
# aligned to the host pins (=0.40 / 0.21) so one bundled SQLite + one libgit2
# link. Keep the version pin in lockstep with the submodule tag.
tinycortex = { version = "0.1", features = ["git-diff", "sync"] }
tinycortex = { version = "0.1", features = ["git-diff", "persona", "sync"] }
Comment thread
senamakel marked this conversation as resolved.
tinychannels = { version = "0.1", features = ["relay-websocket"] }
# TokenJuice code compressor — AST-aware signature extraction. Optional (C build)
# behind the default `tokenjuice-treesitter` feature; disabling it falls back to
Expand Down
1 change: 1 addition & 0 deletions app/scripts/e2e-run-all-flows.sh
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ if should_run_suite "notifications"; then
echo "## Running suite: notifications"
run "test/e2e/specs/notifications.spec.ts" "notifications" "notifications"
run "test/e2e/specs/memory-roundtrip.spec.ts" "memory-roundtrip" "notifications"
run "test/e2e/specs/coding-session-memory.spec.ts" "coding-session-memory" "notifications"
run "test/e2e/specs/cron-jobs-flow.spec.ts" "cron-jobs" "notifications"
run "test/e2e/specs/autocomplete-flow.spec.ts" "autocomplete" "notifications"
_mini_summary "notifications"
Expand Down
23 changes: 20 additions & 3 deletions app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

157 changes: 157 additions & 0 deletions app/src/components/intelligence/CodingSessionsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { useCallback, useEffect, useMemo, useState } from 'react';

import { useT } from '../../lib/i18n/I18nContext';
import {
type CodingSessionSourceStatus,
getCodingSessionStatus,
ingestCodingSessions,
} from '../../services/memorySourcesService';
import type { ToastNotification } from '../../types/intelligence';
import Button from '../ui/Button';

interface CodingSessionsCardProps {
onToast?: (toast: Omit<ToastNotification, 'id'>) => void;
}

const SOURCE_LABEL_KEYS: Record<CodingSessionSourceStatus['kind'], string> = {
claude_code: 'memorySources.codingSessions.claude',
codex: 'memorySources.codingSessions.codex',
};

export function CodingSessionsCard({ onToast }: CodingSessionsCardProps) {
const { t } = useT();
const [sources, setSources] = useState<CodingSessionSourceStatus[]>([]);
const [loading, setLoading] = useState(true);
const [ingesting, setIngesting] = useState(false);
const [error, setError] = useState<string | null>(null);

const load = useCallback(async () => {
console.debug('[coding-sessions] status: entry');
setError(null);
try {
const next = await getCodingSessionStatus();
setSources(next);
console.debug('[coding-sessions] status: exit sources=%d', next.length);
} catch (cause) {
console.error('[coding-sessions] status failed', cause);
setError(cause instanceof Error ? cause.message : String(cause));
} finally {
setLoading(false);
}
}, []);

useEffect(() => {
void load();
}, [load]);

const totals = useMemo(
() => ({
files: sources.reduce((sum, source) => sum + source.session_files, 0),
evidence: sources.reduce((sum, source) => sum + source.evidence_units, 0),
}),
[sources]
);
const hasImportableHistory =
totals.files > 0 || sources.some(source => source.scan_truncated === true);

const ingest = useCallback(async () => {
console.debug('[coding-sessions] ingest: entry');
setIngesting(true);
setError(null);
try {
const result = await ingestCodingSessions(false);
console.debug(
'[coding-sessions] ingest: exit processed=%d failed=%d budget_hit=%s',
result.sessions_processed,
result.sessions_failed,
result.budget_hit
);
const incomplete = result.sessions_failed > 0 || result.budget_hit;
onToast?.({
type: incomplete ? 'warning' : 'success',
title: t('memorySources.codingSessions.complete'),
message:
result.sessions_failed > 0
? t('memorySources.codingSessions.partialFailure')
.replace('{failed}', String(result.sessions_failed))
.replace('{processed}', String(result.sessions_processed))
: result.budget_hit
? t('memorySources.codingSessions.moreRemaining')
: t('memorySources.codingSessions.completeMessage')
.replace('{processed}', String(result.sessions_processed))
.replace('{observations}', String(result.observations)),
});
await load();
} catch (cause) {
console.error('[coding-sessions] ingest failed', cause);
const message = cause instanceof Error ? cause.message : String(cause);
setError(message);
onToast?.({ type: 'error', title: t('memorySources.codingSessions.failed'), message });
} finally {
setIngesting(false);
}
}, [load, onToast, t]);

return (
<section
className="rounded-lg border border-line bg-surface p-4"
data-testid="coding-sessions-card">
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<h3 className="text-sm font-semibold text-content">
{t('memorySources.codingSessions.title')}
</h3>
<p className="mt-1 text-xs text-content-secondary">
{t('memorySources.codingSessions.description')}
</p>
</div>
<Button
analyticsId="brain-sources-coding-sessions-ingest"
size="sm"
onClick={() => void ingest()}
disabled={loading || ingesting || !hasImportableHistory}
data-testid="coding-sessions-ingest">
Comment thread
senamakel marked this conversation as resolved.
{ingesting
? t('memorySources.codingSessions.ingesting')
: t('memorySources.codingSessions.ingest')}
</Button>
</div>

<div className="mt-3 grid gap-2 sm:grid-cols-2">
{sources.map(source => (
<div
key={source.kind}
className="rounded-md border border-line-subtle bg-surface-secondary px-3 py-2"
data-testid={`coding-session-source-${source.kind}`}>
<div className="text-xs font-medium text-content">
{t(SOURCE_LABEL_KEYS[source.kind])}
</div>
<div className="mt-1 text-xs text-content-secondary">
{source.available
? t('memorySources.codingSessions.counts')
.replace('{files}', String(source.session_files))
.replace('{evidence}', String(source.evidence_units))
: t('memorySources.codingSessions.notFound')}
</div>
{source.available && source.scan_truncated && (
<div className="mt-1 text-xs text-amber-600 dark:text-amber-400">
{t('memorySources.codingSessions.truncated')}
</div>
)}
</div>
))}
</div>

{loading && (
<p className="mt-3 text-xs text-content-secondary">
{t('memorySources.codingSessions.scanning')}
</p>
)}
{error && (
<p className="mt-3 text-xs text-coral-600 dark:text-coral-400" role="alert">
{error}
</p>
)}
</section>
);
}
Loading
Loading