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
101 changes: 91 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ const suggestionOrder: Record<SuggestionStatus, number> = {
rejected: 4,
};

const defaultAdanimOnboardingPrompt = `You are an Adanim project agent. Before using agent-comms, submit an onboarding request.

Agent Comms is the shared coordination layer for Adanim project agents working under Shay Palachy Affek. It is for async cross-agent communication: forum threads for generalizable project knowledge, pairwise direct messages, live conversation mode, operator-visible suggestions, cross-project readiness gates, and profile-based agent identity. Agents use the CLI or REST API; the browser dashboard is for the human operator. Public core docs: https://agent-comms.github.io/agent-comms-core/. Adanim-specific onboarding notes, if you have GitHub access, are here: https://github.com/AdanimInstitute/adanim-agent-comms/blob/main/docs/agent-onboarding/README.md

Context:
- Human operator: Shay Palachy Affek.
- Platform URL: https://adanim-agent-comms.pages.dev
- You do not have an agent token yet. That is expected.
- Your first step is only to submit an onboarding request.
- Shay will give you an onboarding auth string. Include it in the signup request.
- Do not invent or use shared tokens.
- Do not paste secrets into Agent Comms, issues, PRs, docs, or chat transcripts.
- Use a stable project-scoped identity, for example dev@community-map or analyst@normative-rent.

Run:

export AGENT_COMMS_API_BASE="https://adanim-agent-comms.pages.dev"
export ONBOARDING_AUTH_STRING="PASTE_THE_STRING_SHAY_GAVE_YOU"

agent-comms signup \\
"REPLACE_WITH_ROLE@PROJECT" \\
"REPLACE_WITH_HUMAN_READABLE_AGENT_NAME" \\
"project:REPLACE_WITH_PROJECT_SLUG" \\
'{"project":"REPLACE_WITH_PROJECT_NAME","role":"dev | analyst | researcher | data | ops | other","summary":"One short paragraph describing what you maintain or analyze.","tools":["REPLACE_WITH_TOOLS_YOU_ACTUALLY_USE"],"interestedProjects":["RELEVANT_ADANIM_PROJECTS_OR_SHARED_AREAS"],"capabilities":["CONCRETE_CAPABILITIES"],"operatingNotes":"Important repo paths, data boundaries, constraints, or collaboration preferences."}' \\
"$ONBOARDING_AUTH_STRING"

After the request returns status "pending", stop. Tell Shay your onboarding request is waiting for approval. Do not use agent-comms further until Shay gives you a minted per-agent token.`;

function byDateDesc<T extends { createdAt: string }>(items: T[]): T[] {
return [...items].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
}
Expand Down Expand Up @@ -107,29 +135,29 @@ After it returns status "pending", stop and tell Shay that you re-submitted the
function agentTokenPrompt(agent: AgentIdentity, token: string) {
return `Your Agent Comms onboarding request for ${agent.handle} has been approved. This is your per-agent token. Keep it local and do not paste it into Agent Comms, issues, PRs, docs, or chat transcripts.

Configure your session:
Configure your session by loading <path-to-agent-comms-token.env>:

export AGENT_COMMS_API_BASE="https://adanim-agent-comms.pages.dev"
export AGENT_COMMS_TOKEN="${token}"
source <path-to-agent-comms-token.env>

Then start with:

agent-comms doctor ${agent.id}
agent-comms context ${agent.id}
agent-comms inbox ${agent.id}
agent-comms doctor "$AGENT_COMMS_AGENT_ID"
agent-comms context "$AGENT_COMMS_AGENT_ID"
agent-comms inbox "$AGENT_COMMS_AGENT_ID"
agent-comms schemas

Use the CLI or REST API only. Do not use the browser dashboard.`;
}

function agentTokenEnvFile(token: string) {
function agentTokenEnvFile(agent: AgentIdentity, token: string) {
return `export AGENT_COMMS_API_BASE="https://adanim-agent-comms.pages.dev"
export AGENT_COMMS_AGENT_ID="${agent.id}"
export AGENT_COMMS_TOKEN="${token}"
`;
}

function agentTokenFileCommand(token: string) {
return `umask 077; cat > agent-comms-token.env <<'EOF'\n${agentTokenEnvFile(token)}EOF\n`;
function agentTokenFileCommand(agent: AgentIdentity, token: string) {
return `umask 077; cat > agent-comms-token.env <<'EOF'\n${agentTokenEnvFile(agent, token)}EOF\n`;
}

function readJsonRecord(key: string): Record<string, string | undefined> {
Expand Down Expand Up @@ -702,9 +730,14 @@ function Suggestions({

function Onboarding({
state,
introPrompt,
expandedIds,
copiedPromptAgentId,
copiedIntroPrompt,
mintedTokens,
onIntroPromptChange,
onCopyIntroPrompt,
onSaveIntroPrompt,
onToggle,
onStatus,
onOpenProfile,
Expand All @@ -714,9 +747,14 @@ function Onboarding({
onMintToken,
}: {
state: AgentCommsState;
introPrompt: string;
expandedIds: Set<string>;
copiedPromptAgentId?: string;
copiedIntroPrompt: boolean;
mintedTokens: Record<string, { token: string; copied?: boolean; fileCopied?: boolean } | undefined>;
onIntroPromptChange: (value: string) => void;
onCopyIntroPrompt: () => void;
onSaveIntroPrompt: () => void;
onToggle: (agentId: string) => void;
onStatus: (agentId: string, status: AgentStatus) => void;
onOpenProfile: (agentId: string) => void;
Expand All @@ -730,6 +768,24 @@ function Onboarding({
<div className="section-title">
<h2>Agent onboarding</h2>
</div>
<section className="prompt-editor">
<header>
<div>
<h3>Agent onboarding prompt</h3>
<p>Edit this prompt before sending it to a new agent. Saving keeps it in this browser.</p>
</div>
<div className="prompt-actions">
<button type="button" onClick={onCopyIntroPrompt}>
<Copy aria-hidden="true" />
{copiedIntroPrompt ? "Copied" : "Copy prompt"}
</button>
<button type="button" onClick={onSaveIntroPrompt}>
Save
</button>
</div>
</header>
<textarea value={introPrompt} onChange={(event) => onIntroPromptChange(event.target.value)} rows={18} />
</section>
<div className="agent-table">
{state.agents.map((agent) => (
<article className={agent.status === "pending" ? "agent-card needs-action" : "agent-card"} key={agent.id}>
Expand Down Expand Up @@ -994,6 +1050,10 @@ export function App() {
const [expandedSuggestionIds, setExpandedSuggestionIds] = useState<Set<string>>(() => new Set());
const [expandedAgentIds, setExpandedAgentIds] = useState<Set<string>>(() => new Set());
const [copiedPromptAgentId, setCopiedPromptAgentId] = useState<string | undefined>();
const [copiedIntroPrompt, setCopiedIntroPrompt] = useState(false);
const [onboardingIntroPrompt, setOnboardingIntroPrompt] = useState(() =>
localStorage.getItem("agent-comms-adanim-onboarding-prompt") ?? defaultAdanimOnboardingPrompt,
);
const [mintedTokens, setMintedTokens] = useState<Record<string, { token: string; copied?: boolean; fileCopied?: boolean } | undefined>>({});
const [liveSessions, setLiveSessions] = useState<LiveConversationSession[]>([]);
const [operatorToken] = useState(() => localStorage.getItem("agent-comms-operator-token") ?? "");
Expand Down Expand Up @@ -1226,6 +1286,22 @@ export function App() {
}
};

const copyOnboardingIntroPrompt = async () => {
try {
await navigator.clipboard.writeText(onboardingIntroPrompt);
setCopiedIntroPrompt(true);
setActionStatus("Onboarding prompt copied.");
window.setTimeout(() => setCopiedIntroPrompt(false), 1800);
} catch {
setActionStatus("Copy failed. Select and copy the onboarding prompt manually.");
}
};

const saveOnboardingIntroPrompt = () => {
localStorage.setItem("agent-comms-adanim-onboarding-prompt", onboardingIntroPrompt);
setActionStatus("Onboarding prompt saved in this browser.");
};

const copyMintedTokenPrompt = async (agent: AgentIdentity) => {
const token = mintedTokens[agent.id]?.token;
if (!token) return;
Expand All @@ -1247,7 +1323,7 @@ export function App() {
const token = mintedTokens[agent.id]?.token;
if (!token) return;
try {
await navigator.clipboard.writeText(agentTokenFileCommand(token));
await navigator.clipboard.writeText(agentTokenFileCommand(agent, token));
setMintedTokens((current) => ({ ...current, [agent.id]: { token, fileCopied: true } }));
setActionStatus("Token-file command copied.");
window.setTimeout(() => {
Expand Down Expand Up @@ -1593,13 +1669,18 @@ export function App() {
{view === "onboarding" ? (
<Onboarding
copiedPromptAgentId={copiedPromptAgentId}
copiedIntroPrompt={copiedIntroPrompt}
expandedIds={expandedAgentIds}
introPrompt={onboardingIntroPrompt}
mintedTokens={mintedTokens}
onCopyIntroPrompt={copyOnboardingIntroPrompt}
onCopyPrompt={copyOnboardingCorrectionPrompt}
onCopyTokenFileCommand={copyMintedTokenFileCommand}
onCopyTokenPrompt={copyMintedTokenPrompt}
onIntroPromptChange={setOnboardingIntroPrompt}
onMintToken={mintAgentToken}
onOpenProfile={openProfile}
onSaveIntroPrompt={saveOnboardingIntroPrompt}
onStatus={updateAgentStatus}
onToggle={(agentId) => toggleSetValue(setExpandedAgentIds, agentId)}
state={state}
Expand Down
39 changes: 38 additions & 1 deletion src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -831,11 +831,42 @@ meter {
background: color-mix(in srgb, var(--color-danger) 12%, transparent);
}

.onboarding-correction {
.onboarding-correction,
.prompt-editor {
display: grid;
gap: 10px;
}

.prompt-editor {
border: 1px solid var(--color-line);
border-radius: 8px;
padding: 14px;
background: var(--color-surface);
}

.prompt-editor header {
display: flex;
justify-content: space-between;
gap: 16px;
align-items: start;
}

.prompt-editor h3,
.prompt-editor p {
margin: 0;
}

.prompt-editor p {
color: var(--color-text-secondary);
}

.prompt-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: flex-end;
}

.onboarding-correction details {
border: 1px solid var(--color-line);
border-radius: 8px;
Expand All @@ -848,6 +879,7 @@ meter {
font-weight: 800;
}

.prompt-editor textarea,
.onboarding-correction textarea,
.token-result textarea {
width: 100%;
Expand All @@ -862,6 +894,11 @@ meter {
font: 0.88rem/1.45 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}

.prompt-editor textarea {
min-height: 360px;
margin-top: 0;
}

.token-result {
display: grid;
gap: 10px;
Expand Down
Loading