Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ defines the Opportunity experience, introduces the ACE-owned design system, and
visual What / Why / How / When grammar across Briefs and decision-facing intelligence. It does not change the 0.9
Collaborative Intelligence promise.

Current post-0.8.1 candidate work adds the Atrium Intelligence Catalog as an experience entry
slice: the generic resource consumer enumerates all admitted onboarding profiles in product scope,
deduplicates them by profile identity, and always offers the Core-owned Custom Intelligence path.
World and Marketing contribute their own declarative profiles from their independent repositories;
Core names neither domain and grants no installation, connection, monitor, or activation authority.
This makes first use outcome-led across domains while leaving live selection/session binding and
activation on the existing governed Builder boundaries. It is preparatory product work, not a
claim that the 0.9 collaboration release gate has passed.

The prior [0.7.0 GitHub Release](https://github.com/augmented-cognition-engine/core/releases/tag/v0.7.0)
and [`ace-core==0.7.0`](https://pypi.org/project/ace-core/0.7.0/) package remain the passed
Intelligence Builder Foundation boundary, recorded by the
Expand Down
2 changes: 2 additions & 0 deletions ace/intelligence/contracts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@
IntelligenceOnboardingGuardrailsV1Alpha1,
IntelligenceOnboardingOutcomeV1Alpha1,
IntelligenceOnboardingProfileV1Alpha1,
IntelligenceOnboardingSourceGroupV1Alpha1,
)
from ace.intelligence.contracts.ledger import (
AttentionDisposition,
Expand Down Expand Up @@ -481,6 +482,7 @@
"IntelligenceOnboardingGuardrailsV1Alpha1",
"IntelligenceOnboardingOutcomeV1Alpha1",
"IntelligenceOnboardingProfileV1Alpha1",
"IntelligenceOnboardingSourceGroupV1Alpha1",
"MAX_RESOURCE_PLANE_PAGE_SIZE",
"RESOURCE_PLANE_CURSOR_VERSION",
"RESOURCE_PLANE_PAGE_VERSION",
Expand Down
58 changes: 58 additions & 0 deletions ace/intelligence/contracts/intelligence_builder_presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,39 @@ def cadence_slug(cls, value: str) -> str:
return validate_slug(value, name="cadence_id")


class IntelligenceOnboardingSourceGroupV1Alpha1(_BuilderPresentationContract):
"""One inert, reviewable group of evidence sources proposed during onboarding."""

source_group_id: str
label: str = Field(min_length=1, max_length=160)
description: str = Field(min_length=1, max_length=1_000)
evidence_role: str
source_ids: tuple[str, ...] = Field(min_length=1, max_length=32)
source_labels: tuple[str, ...] = Field(min_length=1, max_length=8)
access_label: str = Field(min_length=1, max_length=160)
default_selected: StrictBool = True

@field_validator("source_group_id", "evidence_role")
@classmethod
def slugs(cls, value: str, info) -> str:
return validate_slug(value, name=info.field_name)

@field_validator("source_ids", mode="before")
@classmethod
def ids(cls, value: Any) -> tuple[str, ...]:
return tuple(
validate_slug(item, name="source_ids") for item in normalized_strings(value, label="source_ids", maximum=32)
)

@field_validator("source_labels", mode="before")
@classmethod
def labels(cls, value: Any) -> tuple[str, ...]:
return tuple(
_text(item, name="source_labels", maximum=160)
for item in normalized_strings(value, label="source_labels", maximum=8)
)


class IntelligenceOnboardingFirstValueV1Alpha1(_BuilderPresentationContract):
public_sources_first: StrictBool = True
private_sources_optional: StrictBool = True
Expand All @@ -99,10 +132,14 @@ class IntelligenceOnboardingProfileV1Alpha1(_BuilderPresentationContract):
profile_id: str
profile_digest: str | None = None
topic_id: str
domain_label: str | None = Field(default=None, min_length=1, max_length=160)
topic_label: str | None = Field(default=None, min_length=1, max_length=160)
display_name: str = Field(min_length=1, max_length=160)
prompt: str = Field(min_length=1, max_length=300)
description: str = Field(min_length=1, max_length=2_000)
starter_prompts: tuple[str, ...] = Field(default_factory=tuple, max_length=8)
outcomes: tuple[IntelligenceOnboardingOutcomeV1Alpha1, ...] = Field(min_length=1, max_length=16)
source_groups: tuple[IntelligenceOnboardingSourceGroupV1Alpha1, ...] = Field(default_factory=tuple, max_length=16)
cadences: tuple[IntelligenceOnboardingCadenceV1Alpha1, ...] = Field(min_length=1, max_length=16)
default_cadence_id: str
first_value: IntelligenceOnboardingFirstValueV1Alpha1
Expand All @@ -123,6 +160,14 @@ def digest(cls, value: str | None) -> str | None:
def profile_slugs(cls, value: str, info) -> str:
return validate_slug(value, name=info.field_name)

@field_validator("starter_prompts", mode="before")
@classmethod
def prompts(cls, value: Any) -> tuple[str, ...]:
return tuple(
_text(item, name="starter_prompts", maximum=300)
for item in normalized_strings(value, label="starter_prompts", maximum=8)
)

@field_validator("outcomes")
@classmethod
def unique_outcomes(
Expand All @@ -137,6 +182,18 @@ def unique_cadences(
) -> tuple[IntelligenceOnboardingCadenceV1Alpha1, ...]:
return sorted_unique(value, key=lambda item: item.cadence_id, label="onboarding cadences", maximum=16)

@field_validator("source_groups")
@classmethod
def unique_source_groups(
cls, value: tuple[IntelligenceOnboardingSourceGroupV1Alpha1, ...]
) -> tuple[IntelligenceOnboardingSourceGroupV1Alpha1, ...]:
return sorted_unique(
value,
key=lambda item: item.source_group_id,
label="onboarding source groups",
maximum=16,
)

@model_validator(mode="after")
def bind_profile(self) -> Self:
if self.default_cadence_id not in {item.cadence_id for item in self.cadences}:
Expand All @@ -156,4 +213,5 @@ def bind_profile(self) -> Self:
"IntelligenceOnboardingGuardrailsV1Alpha1",
"IntelligenceOnboardingOutcomeV1Alpha1",
"IntelligenceOnboardingProfileV1Alpha1",
"IntelligenceOnboardingSourceGroupV1Alpha1",
]
18 changes: 7 additions & 11 deletions core/ui/canvas/src/app/atrium/IntelligenceOS.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ import { KernelNav } from '../ext/defaults/KernelNav'
import { AskAce } from './AskAce'
import { OnboardingPreview } from './OnboardingPreview'
import {
hasOnboardingProfileResource,
onboardingProfileFromResources,
onboardingProfilesFromResources,
onboardingSessionFromResources,
} from './onboardingModel'
import { pageFreshness, productDisplayName } from './experienceModel'
Expand Down Expand Up @@ -426,9 +425,8 @@ export function IntelligenceOS() {
const productName = productDisplayName(page?.product_id)
const freshness = pageFreshness(page)
const [onboardingOpen, setOnboardingOpen] = useState(false)
const onboardingProfile = useMemo(() => onboardingProfileFromResources(page?.items ?? []), [page?.items])
const onboardingProfiles = useMemo(() => onboardingProfilesFromResources(page?.items ?? []), [page?.items])
const onboardingSession = useMemo(() => onboardingSessionFromResources(page?.items ?? []), [page?.items])
const hasOnboarding = useMemo(() => hasOnboardingProfileResource(page?.items ?? []), [page?.items])

function openFirstBrief() {
requestAnimationFrame(() => document.getElementById('latest-brief')?.scrollIntoView({ behavior: 'smooth' }))
Expand All @@ -451,12 +449,10 @@ export function IntelligenceOS() {
</div>
</div>
<div className="ml-auto flex items-center gap-2">
{hasOnboarding && (
<Button type="button" variant="outline" size="sm" onClick={() => setOnboardingOpen(true)}>
<Sparkles className="size-3.5" />
<span className="hidden sm:inline">{onboardingSession === null ? 'Build intelligence' : 'View build'}</span>
</Button>
)}
<Button type="button" variant="outline" size="sm" onClick={() => setOnboardingOpen(true)}>
<Sparkles className="size-3.5" />
<span className="hidden sm:inline">{onboardingSession === null ? 'Build intelligence' : 'View build'}</span>
</Button>
{page !== null && (
<Badge variant={page.state === 'degraded' ? 'outline' : 'secondary'} className="hidden rounded-sm border border-border/70 bg-card font-mono text-[9px] sm:inline-flex">
{page.state === 'degraded' ? <CircleAlert className="mr-1 size-3 text-warning" /> : <ShieldCheck className="mr-1 size-3 text-brand" />}
Expand Down Expand Up @@ -512,7 +508,7 @@ export function IntelligenceOS() {
<OnboardingPreview
open={onboardingOpen}
onOpenChange={setOnboardingOpen}
profile={onboardingProfile}
profiles={onboardingProfiles}
session={onboardingSession}
onOpenBrief={openFirstBrief}
/>
Expand Down
Loading