Enable canonical versioned Go module consumption
Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements. Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate. Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable. Refs: Versioned dependencies U1/U3; feature/dependency-workspace
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
*.test
|
||||
*.out
|
||||
dist/
|
||||
node_modules/
|
||||
*.tgz
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module agent-system-core
|
||||
module git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git
|
||||
|
||||
go 1.27
|
||||
|
||||
Generated
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@agent-system/contracts",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@agent-system/contracts",
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@agent-system/contracts",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"description": "Agent-System public TypeScript wire contracts",
|
||||
"files": ["package"],
|
||||
"types": "./package/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./package/index.d.ts",
|
||||
"import": "./package/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "node scripts/build-package.mjs",
|
||||
"check:package": "node scripts/build-package.mjs --check",
|
||||
"typecheck": "tsc -p tsconfig.package.json --noEmit",
|
||||
"test": "npm run check:package && node --test scripts/test-package.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
}
|
||||
Vendored
+672
@@ -0,0 +1,672 @@
|
||||
export type ID = string;
|
||||
export interface Project {
|
||||
id: ID;
|
||||
owner?: ID;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
settings?: Record<string, unknown>;
|
||||
}
|
||||
export interface Conversation {
|
||||
id: ID;
|
||||
projectId: ID;
|
||||
owner?: ID;
|
||||
title: string;
|
||||
profileId?: ID;
|
||||
pinned?: boolean;
|
||||
archived?: boolean;
|
||||
allowCrossRead?: boolean;
|
||||
crossPrivate?: boolean;
|
||||
permissionMode?: 'default' | 'plan' | 'auto_edit' | '';
|
||||
sessionNote?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
export type Role = 'user' | 'assistant' | 'system';
|
||||
export type PartType = 'text' | 'thinking' | 'tool_call' | 'tool_result' | 'artifact_ref' | 'hitl' | 'plan' | 'error' | 'card' | 'check';
|
||||
export interface Part {
|
||||
type: PartType;
|
||||
text?: string;
|
||||
toolCall?: {
|
||||
id: string;
|
||||
name: string;
|
||||
arguments?: string;
|
||||
durationMs?: number;
|
||||
};
|
||||
toolResult?: {
|
||||
callId: string;
|
||||
status: 'ok' | 'error';
|
||||
summary?: string;
|
||||
truncated?: boolean;
|
||||
};
|
||||
artifact?: {
|
||||
artifactId: ID;
|
||||
name: string;
|
||||
kind?: string;
|
||||
};
|
||||
hitl?: {
|
||||
cardId: ID;
|
||||
question: string;
|
||||
resolved: boolean;
|
||||
answer?: string;
|
||||
};
|
||||
plan?: PlanSnapshot;
|
||||
card?: {
|
||||
kind: string;
|
||||
data: UINode[];
|
||||
cardId?: ID;
|
||||
};
|
||||
error?: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
check?: {
|
||||
passed: boolean;
|
||||
reason?: string;
|
||||
};
|
||||
process?: boolean;
|
||||
durationMs?: number;
|
||||
}
|
||||
export interface Message {
|
||||
id: ID;
|
||||
conversationId: ID;
|
||||
runId?: ID;
|
||||
role: Role;
|
||||
parts: Part[];
|
||||
createdAt: string;
|
||||
excluded?: boolean;
|
||||
variantGroup?: string;
|
||||
variantIndex?: number;
|
||||
}
|
||||
export type RunKind = 'conversation' | 'subagent' | 'tool';
|
||||
export type RunStatus = 'pending' | 'running' | 'waiting_hitl' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
|
||||
export type RunPhase = 'thinking' | 'acting' | 'responding' | 'finalizing' | 'done';
|
||||
export interface Usage {
|
||||
promptTokens: number;
|
||||
completionTokens: number;
|
||||
totalTokens: number;
|
||||
durationMs: number;
|
||||
toolCalls: number;
|
||||
model?: string;
|
||||
lastPromptTokens?: number;
|
||||
lastCompletionTokens?: number;
|
||||
cacheReadTokens?: number;
|
||||
cacheWriteTokens?: number;
|
||||
}
|
||||
export interface Run {
|
||||
id: ID;
|
||||
parentRunId?: ID;
|
||||
conversationId: ID;
|
||||
projectId: ID;
|
||||
owner?: ID;
|
||||
kind: RunKind;
|
||||
profileId?: ID;
|
||||
status: RunStatus;
|
||||
phase?: RunPhase;
|
||||
agentName?: string;
|
||||
createdAt: string;
|
||||
endedAt?: string;
|
||||
usage?: Usage;
|
||||
error?: {
|
||||
code: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
export interface Artifact {
|
||||
id: ID;
|
||||
projectId: ID;
|
||||
owner?: ID;
|
||||
name: string;
|
||||
kind: string;
|
||||
mime?: string;
|
||||
size: number;
|
||||
sourceRun?: ID;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
export interface PlanSnapshot {
|
||||
planId: ID;
|
||||
title: string;
|
||||
steps: {
|
||||
id: string;
|
||||
text: string;
|
||||
status: 'pending' | 'in_progress' | 'done' | 'skipped';
|
||||
agentRun?: ID;
|
||||
}[];
|
||||
}
|
||||
export interface TodoList {
|
||||
conversationId: ID;
|
||||
items: {
|
||||
id: string;
|
||||
text: string;
|
||||
status: 'pending' | 'in_progress' | 'done';
|
||||
}[];
|
||||
}
|
||||
export type StreamEventType = 'message.delta' | 'thinking.delta' | 'tool.call.delta' | 'tool.call' | 'tool.output' | 'tool.result' | 'run.phase' | 'run.check' | 'hitl.request' | 'hitl.resolved' | 'plan.update' | 'todo.update' | 'agent.spawn' | 'agent.progress' | 'agent.result' | 'artifact.created' | 'artifact.updated' | 'run.completed' | 'run.failed' | 'error' | 'card.created' | 'card.updated' | 'card.status' | 'card.resolved' | 'surface.updated';
|
||||
export interface StreamEvent<P = unknown> {
|
||||
seq: number;
|
||||
runId?: ID;
|
||||
conversationId: ID;
|
||||
ts: string;
|
||||
v: number;
|
||||
type: StreamEventType;
|
||||
payload?: P;
|
||||
}
|
||||
export interface MessageDeltaPayload {
|
||||
index: number;
|
||||
text: string;
|
||||
}
|
||||
export interface ToolCallDeltaPayload {
|
||||
index: number;
|
||||
callId?: string;
|
||||
name?: string;
|
||||
delta: string;
|
||||
}
|
||||
export interface ToolOutputPayload {
|
||||
callId: string;
|
||||
stream: 'stdout' | 'stderr';
|
||||
text: string;
|
||||
}
|
||||
export interface ToolResultPayload {
|
||||
callId: string;
|
||||
status: string;
|
||||
summary?: string;
|
||||
truncated?: boolean;
|
||||
durationMs?: number;
|
||||
}
|
||||
export interface AgentSpawnPayload {
|
||||
agentRun: ID;
|
||||
name: string;
|
||||
task: string;
|
||||
}
|
||||
export interface AgentProgressPayload {
|
||||
agentRun: ID;
|
||||
note?: string;
|
||||
phase?: RunPhase;
|
||||
}
|
||||
export interface AgentResultPayload {
|
||||
agentRun: ID;
|
||||
status: RunStatus;
|
||||
summary?: string;
|
||||
}
|
||||
export interface HITLResolvedPayload {
|
||||
cardId: ID;
|
||||
answer?: string;
|
||||
kind: 'choice' | 'free' | 'timeout' | 'cancelled';
|
||||
}
|
||||
export interface RunCompletedPayload {
|
||||
usage: Usage;
|
||||
context?: ContextObservation;
|
||||
}
|
||||
interface ContextSection {
|
||||
name: string;
|
||||
estTokens: number;
|
||||
}
|
||||
export interface ContextObservation {
|
||||
items: string[];
|
||||
truncations?: string[];
|
||||
estChars: number;
|
||||
sections?: ContextSection[];
|
||||
prefixHash?: string;
|
||||
}
|
||||
export interface RunCheckPayload {
|
||||
passed: boolean;
|
||||
reason?: string;
|
||||
}
|
||||
export interface RunFailedPayload {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
export type CardKind = 'progress' | 'confirm' | 'compare' | 'review-list' | 'result' | 'error' | 'status';
|
||||
export type CardStatus = 'active' | 'action_required' | 'done' | 'resolved' | 'failed' | 'dismissed';
|
||||
export interface ActivityCard {
|
||||
id: ID;
|
||||
projectId: ID;
|
||||
conversationId?: ID;
|
||||
sourceRunId?: ID;
|
||||
anchorMessageId?: ID;
|
||||
kind: CardKind;
|
||||
title: string;
|
||||
status: CardStatus;
|
||||
tone?: string;
|
||||
body?: UINode[];
|
||||
actions?: {
|
||||
id: string;
|
||||
label: string;
|
||||
style?: 'primary' | 'default' | 'danger';
|
||||
command?: string;
|
||||
}[];
|
||||
pinned?: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
export interface CardPayload {
|
||||
card: ActivityCard;
|
||||
}
|
||||
export interface CardStatusPayload {
|
||||
cardId: ID;
|
||||
status: CardStatus;
|
||||
tone?: string;
|
||||
note?: string;
|
||||
}
|
||||
export interface CardResolvedPayload {
|
||||
cardId: ID;
|
||||
actionId?: string;
|
||||
answer?: string;
|
||||
}
|
||||
export type HITLKind = 'choice' | 'input' | 'confirm' | 'form';
|
||||
export interface HITLChoice {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
impact: string;
|
||||
recommended?: boolean;
|
||||
}
|
||||
export interface HITLCard {
|
||||
id: ID;
|
||||
runId: ID;
|
||||
conversationId: ID;
|
||||
question: string;
|
||||
kind: HITLKind;
|
||||
choices?: HITLChoice[];
|
||||
allowFreeInput: boolean;
|
||||
freeInputHint?: string;
|
||||
timeoutSec?: number | null;
|
||||
form?: UINode[];
|
||||
createdAt: string;
|
||||
resolved: boolean;
|
||||
anchorMessageId?: ID;
|
||||
blocked?: boolean;
|
||||
blockReason?: string;
|
||||
detail?: string;
|
||||
}
|
||||
export interface HITLResponse {
|
||||
cardId: ID;
|
||||
choiceId?: string;
|
||||
freeText?: string;
|
||||
formData?: unknown;
|
||||
}
|
||||
export type UINodeType = 'section' | 'list' | 'kv' | 'form' | 'markdown' | 'doc-ref' | 'actions' | 'chart-lite' | 'avatar' | 'progress' | 'compare' | 'tree' | 'table' | 'steps' | 'tabs' | 'timeline';
|
||||
export interface UINode {
|
||||
type: UINodeType;
|
||||
/** Surface 层节点级标注(Generative UI U2):整页唯一稳定 id 与事实来源标识,旧路径不填。 */
|
||||
id?: string;
|
||||
source?: string[];
|
||||
title?: string;
|
||||
items?: string[];
|
||||
kv?: [string, string][];
|
||||
fields?: UIField[];
|
||||
markdown?: string;
|
||||
docId?: ID;
|
||||
actions?: {
|
||||
id: string;
|
||||
label: string;
|
||||
style?: 'primary' | 'default' | 'danger';
|
||||
}[];
|
||||
chart?: {
|
||||
kind: 'bar' | 'line';
|
||||
labels: string[];
|
||||
series: number[];
|
||||
};
|
||||
children?: UINode[];
|
||||
avatar?: {
|
||||
name?: string;
|
||||
sub?: string;
|
||||
tone?: string;
|
||||
stack?: {
|
||||
name?: string;
|
||||
sub?: string;
|
||||
tone?: string;
|
||||
}[];
|
||||
};
|
||||
progress?: {
|
||||
label?: string;
|
||||
value: number;
|
||||
tone?: string;
|
||||
};
|
||||
compare?: {
|
||||
left: {
|
||||
title?: string;
|
||||
tone?: string;
|
||||
nodes?: UINode[];
|
||||
};
|
||||
right: {
|
||||
title?: string;
|
||||
tone?: string;
|
||||
nodes?: UINode[];
|
||||
};
|
||||
};
|
||||
tree?: {
|
||||
nodes: {
|
||||
id: string;
|
||||
label: string;
|
||||
tone?: string;
|
||||
badge?: string;
|
||||
count?: number;
|
||||
selected?: boolean;
|
||||
children?: unknown[];
|
||||
}[];
|
||||
};
|
||||
table?: {
|
||||
columns: {
|
||||
key: string;
|
||||
label: string;
|
||||
}[];
|
||||
rows: {
|
||||
id?: string;
|
||||
cells: [string, string][];
|
||||
actions?: UINode['actions'];
|
||||
}[];
|
||||
};
|
||||
steps?: {
|
||||
current: number;
|
||||
steps: {
|
||||
label: string;
|
||||
status?: 'pending' | 'active' | 'done' | 'failed';
|
||||
tone?: string;
|
||||
}[];
|
||||
};
|
||||
tabs?: {
|
||||
active?: string;
|
||||
tabs: {
|
||||
id: string;
|
||||
label: string;
|
||||
nodes?: UINode[];
|
||||
}[];
|
||||
};
|
||||
timeline?: {
|
||||
items: {
|
||||
time?: string;
|
||||
title: string;
|
||||
tone?: string;
|
||||
markdown?: string;
|
||||
ref?: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
export interface UIField {
|
||||
key: string;
|
||||
label: string;
|
||||
type: 'text' | 'textarea' | 'number' | 'boolean' | 'select';
|
||||
options?: string[];
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
help?: string;
|
||||
default?: string;
|
||||
}
|
||||
export interface PromptTrigger {
|
||||
kind: 'intent' | 'command' | 'attachment' | 'manual';
|
||||
keywords?: string[];
|
||||
command?: string;
|
||||
}
|
||||
export interface PromptModule {
|
||||
id: string;
|
||||
layer: 'base' | 'env' | 'task' | 'agent' | 'tool' | 'aux' | 'style';
|
||||
version: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
priority?: number;
|
||||
triggers?: PromptTrigger[];
|
||||
requiresAttachments?: boolean;
|
||||
body: string;
|
||||
outputContract?: string;
|
||||
source?: 'builtin' | 'plugin' | 'user';
|
||||
owner?: string;
|
||||
basedOnVersion?: number;
|
||||
drifted?: boolean;
|
||||
disabled?: boolean;
|
||||
/** 子代理 profile。只对 agent.sub.* 有意义;空字段表示沿用内置。 */
|
||||
tools?: string[];
|
||||
model?: string;
|
||||
maxTurns?: number;
|
||||
}
|
||||
export interface PromptBlock {
|
||||
moduleId: string;
|
||||
version: number;
|
||||
layer: string;
|
||||
body: string;
|
||||
}
|
||||
export interface PromptHitRecord {
|
||||
moduleId: string;
|
||||
layer: string;
|
||||
reason: string;
|
||||
injected: boolean;
|
||||
}
|
||||
export interface AgentProfile {
|
||||
id: string;
|
||||
owner?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
builtin?: boolean;
|
||||
source?: string;
|
||||
systemPrompt: string;
|
||||
/** 提示词库模块引用(有序;空 = 默认 base+env 全集)。 */
|
||||
promptModules?: string[];
|
||||
model: {
|
||||
chat?: string;
|
||||
lightweight?: string;
|
||||
embedding?: string;
|
||||
rerank?: string;
|
||||
};
|
||||
tools: {
|
||||
allow: string[];
|
||||
deny?: string[];
|
||||
confirm?: string[];
|
||||
};
|
||||
context: {
|
||||
maxHistoryMessages: number;
|
||||
summarizeBeyond?: boolean;
|
||||
memoryBudgetPct?: number;
|
||||
knowledgeBudgetPct?: number;
|
||||
maxToolResultChars?: number;
|
||||
};
|
||||
hitl: {
|
||||
askOnAmbiguity: boolean;
|
||||
timeoutSec?: number | null;
|
||||
};
|
||||
budget: {
|
||||
maxTokens?: number | null;
|
||||
maxToolCalls?: number | null;
|
||||
maxTurns?: number | null;
|
||||
};
|
||||
}
|
||||
export interface SettingDesc {
|
||||
key: string;
|
||||
group: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
type: 'string' | 'number' | 'boolean' | 'enum' | 'secret';
|
||||
default?: unknown;
|
||||
options?: string[];
|
||||
effect: 'hot' | 'new-run' | 'restart';
|
||||
secret?: boolean;
|
||||
/** P2 受管:system = 系统受管项,仅系统管理面可写(用户/项目层拒绝)。 */
|
||||
scope?: 'system';
|
||||
}
|
||||
export interface PluginManifest {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
apiVersion: string;
|
||||
requires?: {
|
||||
network?: string[];
|
||||
};
|
||||
provides: {
|
||||
tools?: string[];
|
||||
workflows?: string[];
|
||||
intents?: string[];
|
||||
prompts?: string[];
|
||||
panels?: string[];
|
||||
cards?: string[];
|
||||
settings?: string[];
|
||||
commands?: string[];
|
||||
workspaces?: string[];
|
||||
skills?: string[];
|
||||
mcpServers?: string[];
|
||||
knowledge?: string[];
|
||||
parsers?: string[];
|
||||
agents?: string[];
|
||||
channels?: string[];
|
||||
pages?: string[];
|
||||
};
|
||||
scope?: {
|
||||
tenants?: string[];
|
||||
projects?: string[];
|
||||
};
|
||||
backend?: {
|
||||
mode?: 'inproc' | 'sidecar';
|
||||
command?: string;
|
||||
args?: string[];
|
||||
schemaVersion?: number;
|
||||
};
|
||||
}
|
||||
export interface PluginPanel {
|
||||
id: string;
|
||||
title: string;
|
||||
icon?: string;
|
||||
body: UINode[];
|
||||
refreshTool?: string;
|
||||
}
|
||||
/** 模板占位符:{{input}}、{{slots.<key>}}、{{steps.<id>.output}}。 */
|
||||
export interface WorkflowDef {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
intentId?: string;
|
||||
/** 整条工作流的 critic 重试上限。 */
|
||||
maxRetries?: number;
|
||||
steps: WorkflowStep[];
|
||||
}
|
||||
export interface StepCritic {
|
||||
criteria: string;
|
||||
/** 本步重试上限;缺省 1 次。 */
|
||||
maxRetries?: number;
|
||||
}
|
||||
export interface WorkflowStep {
|
||||
id: string;
|
||||
title: string;
|
||||
kind: 'prompt' | 'tool' | 'hitl' | 'artifact' | 'card' | 'script';
|
||||
prompt?: string;
|
||||
/** prompt 步骤的子代理 profile,缺省 general。 */
|
||||
agent?: string;
|
||||
tool?: string;
|
||||
args?: unknown;
|
||||
hitl?: {
|
||||
question: string;
|
||||
kind: HITLKind;
|
||||
choices?: HITLChoice[];
|
||||
allowFreeInput: boolean;
|
||||
freeInputHint?: string;
|
||||
};
|
||||
artifact?: string;
|
||||
card?: {
|
||||
kind: CardKind;
|
||||
title: string;
|
||||
tone?: string;
|
||||
body?: UINode[];
|
||||
actions?: {
|
||||
id: string;
|
||||
label: string;
|
||||
style?: 'primary' | 'default' | 'danger';
|
||||
command?: string;
|
||||
}[];
|
||||
};
|
||||
/** script 步骤在插件目录内的相对路径。 */
|
||||
script?: string;
|
||||
critic?: StepCritic;
|
||||
}
|
||||
export interface PluginWorkspace {
|
||||
id: string;
|
||||
title: string;
|
||||
icon?: string;
|
||||
shortcut?: string;
|
||||
badgeTool?: string;
|
||||
body: UINode[];
|
||||
refreshTool?: string;
|
||||
}
|
||||
export interface PluginPageDecl {
|
||||
id: string;
|
||||
title: string;
|
||||
module: string;
|
||||
}
|
||||
export interface ParsedDocument {
|
||||
meta: {
|
||||
filename: string;
|
||||
mime?: string;
|
||||
parser: string;
|
||||
confidence?: number;
|
||||
crossCheck?: {
|
||||
with: string;
|
||||
diffBlocks: number;
|
||||
verdict: 'agree' | 'differ';
|
||||
};
|
||||
parsedAt: string;
|
||||
};
|
||||
blocks: {
|
||||
type: 'heading' | 'para' | 'list' | 'table' | 'code' | 'image-ref';
|
||||
level?: number;
|
||||
text?: string;
|
||||
items?: string[];
|
||||
rows?: string[][];
|
||||
ref?: string;
|
||||
confidence?: number;
|
||||
}[];
|
||||
warnings?: string[];
|
||||
}
|
||||
export interface RetrievalHit {
|
||||
doc: {
|
||||
id: ID;
|
||||
owner?: ID;
|
||||
projectId?: ID;
|
||||
source: string;
|
||||
title: string;
|
||||
ref?: string;
|
||||
chunk: string;
|
||||
createdAt: string;
|
||||
};
|
||||
score: number;
|
||||
kind: 'fulltext' | 'vector' | 'plugin';
|
||||
}
|
||||
export interface UploadInfo {
|
||||
artifactId: ID;
|
||||
filename: string;
|
||||
mime?: string;
|
||||
size: number;
|
||||
parsed?: ParsedDocument;
|
||||
}
|
||||
export interface SurfaceCompat {
|
||||
schemaVersion: number;
|
||||
catalogVersion: string;
|
||||
pluginVersion?: string;
|
||||
}
|
||||
export interface SurfaceIdentity {
|
||||
surfaceId: ID;
|
||||
owner: string;
|
||||
projectId?: ID;
|
||||
conversationId?: ID;
|
||||
sourceRunId?: ID;
|
||||
pluginId: string;
|
||||
}
|
||||
export interface SurfaceSpec {
|
||||
title: string;
|
||||
nodes: UINode[];
|
||||
}
|
||||
export interface SurfaceSnapshot {
|
||||
identity: SurfaceIdentity;
|
||||
compat: SurfaceCompat;
|
||||
revision: number;
|
||||
baseRevision?: number;
|
||||
contextVersion?: string;
|
||||
activationEpoch: number;
|
||||
spec: SurfaceSpec;
|
||||
semanticText?: string;
|
||||
}
|
||||
export interface SurfaceMeta {
|
||||
surfaceId: ID;
|
||||
title: string;
|
||||
pluginId: string;
|
||||
revision: number;
|
||||
conversationId?: ID;
|
||||
updatedAt: string;
|
||||
}
|
||||
export {};
|
||||
@@ -0,0 +1,4 @@
|
||||
// Agent-System-Core 契约的 TypeScript 镜像。
|
||||
// 线格式契约以 Go 类型(camelCase JSON 标签)与 testdata/golden/*.json 黄金样本为准;
|
||||
// 本文件手工镜像同一契约,前端按未知 type/字段静默忽略(前向兼容原则)。
|
||||
export {};
|
||||
@@ -0,0 +1,24 @@
|
||||
// Explicit generation only: authenticated Git acquisition must never run package scripts.
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = dirname(dirname(fileURLToPath(import.meta.url)))
|
||||
const check = process.argv.includes('--check')
|
||||
const output = check ? mkdtempSync(join(tmpdir(), 'agent-contracts-build-')) : join(root, 'package')
|
||||
try {
|
||||
mkdirSync(output, { recursive: true })
|
||||
execFileSync(process.execPath, [join(root, 'node_modules/typescript/bin/tsc'), '-p', join(root, 'tsconfig.package.json'), '--outDir', output], { cwd: root, stdio: 'inherit' })
|
||||
for (const extension of ['js', 'd.ts']) renameSync(join(output, `types.${extension}`), join(output, `index.${extension}`))
|
||||
if (check) {
|
||||
for (const file of ['index.js', 'index.d.ts']) {
|
||||
if (!readFileSync(join(root, 'package', file)).equals(readFileSync(join(output, file)))) {
|
||||
throw new Error(`package/${file} is stale; run npm run build before committing the provider`)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (check) rmSync(output, { recursive: true, force: true })
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync, spawnSync } from 'node:child_process'
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { test } from 'node:test'
|
||||
|
||||
const root = dirname(dirname(fileURLToPath(import.meta.url)))
|
||||
|
||||
test('packed contracts compile in a consumer without the Core checkout', () => {
|
||||
const consumer = mkdtempSync(join(tmpdir(), 'agent-contracts-consumer-'))
|
||||
try {
|
||||
const pack = spawnSync('npm', ['pack', '--json', '--ignore-scripts', '--pack-destination', consumer], {
|
||||
cwd: root, encoding: 'utf8',
|
||||
})
|
||||
assert.equal(pack.status, 0, `Core must expose an independently packable contracts package:\n${pack.stderr}`)
|
||||
const [artifact] = JSON.parse(pack.stdout)
|
||||
assert.equal(artifact.name, '@agent-system/contracts')
|
||||
assert.ok(artifact.files.some(file => file.path === 'package/index.d.ts'), 'declarations must be packed')
|
||||
assert.ok(artifact.files.some(file => file.path === 'package/index.js'), 'ESM entry must be packed')
|
||||
assert.ok(!artifact.files.some(file => file.path.endsWith('.go')), 'Go implementation is outside the npm package')
|
||||
|
||||
writeFileSync(join(consumer, 'package.json'), JSON.stringify({ private: true, type: 'module' }))
|
||||
writeFileSync(join(consumer, 'consumer.ts'), `
|
||||
import type { ActivityCard, Message, SurfaceSnapshot } from '@agent-system/contracts'
|
||||
const card: ActivityCard = { id: 'card', projectId: 'project', conversationId: 'conversation', kind: 'progress', status: 'active', title: 'Working', createdAt: '2026-10-13T00:00:00Z', updatedAt: '2026-10-13T00:00:00Z' }
|
||||
const message: Message = { id: 'message', conversationId: 'conversation', role: 'assistant', parts: [{ type: 'text', text: card.title }], createdAt: card.createdAt }
|
||||
type Surface = SurfaceSnapshot
|
||||
// @ts-expect-error unknown message roles are not part of the wire contract
|
||||
const invalidRole: Message['role'] = 'operator'
|
||||
export { message, invalidRole }; export type { Surface }
|
||||
`)
|
||||
const tsc = [join(root, 'node_modules/typescript/bin/tsc'), '--noEmit', '--strict', '--skipLibCheck', '--module', 'NodeNext', '--moduleResolution', 'NodeNext', 'consumer.ts']
|
||||
const missing = spawnSync(process.execPath, tsc, { cwd: consumer, encoding: 'utf8' })
|
||||
assert.notEqual(missing.status, 0, 'the consumer must not resolve Core types before package installation')
|
||||
assert.match(missing.stdout, /TS2307.*@agent-system\/contracts/)
|
||||
execFileSync('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', '--offline', '--package-lock=false', join(consumer, artifact.filename)], { cwd: consumer, stdio: 'pipe' })
|
||||
execFileSync(process.execPath, tsc, { cwd: consumer, stdio: 'inherit' })
|
||||
execFileSync(process.execPath, ['--input-type=module', '-e', "import * as contracts from '@agent-system/contracts'; if (Object.keys(contracts).length) throw Error('contracts must remain type-only')"], { cwd: consumer, stdio: 'pipe' })
|
||||
const declaration = readFileSync(join(consumer, 'node_modules/@agent-system/contracts/package/index.d.ts'), 'utf8')
|
||||
assert.match(declaration, /export interface SurfaceSnapshot/)
|
||||
} finally {
|
||||
rmSync(consumer, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,16 @@
|
||||
# TypeScript contracts package
|
||||
|
||||
`ts/types.ts` is the sole TypeScript contract source. `npm run build` emits the
|
||||
tracked `package/index.d.ts` and type-only ESM entry for `@agent-system/contracts`.
|
||||
Consumers import types from the package instead of copying this source.
|
||||
|
||||
Run `npm ci --ignore-scripts`, remove acquisition credentials from the execution
|
||||
environment, then run `npm run build && npm test`. `npm test` detects stale
|
||||
generated output and installs an `npm pack --ignore-scripts` artifact into an
|
||||
isolated consumer that has no Core checkout.
|
||||
|
||||
The generated package is committed with its source so fixed Git dependencies work
|
||||
with lifecycle scripts disabled. The package has no `prepare`, `install`, or
|
||||
`prepack` hook. Pin a reachable provider commit in each consumer's manifest and
|
||||
lockfile. This package version is metadata; no Registry upload or release tag is
|
||||
created by these commands.
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"rootDir": "ts",
|
||||
"outDir": "package",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"files": ["ts/types.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user