Merge branch 'codex/invariant-package-registration-gate' into codex/package-invariant-checks
# Conflicts: # .agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.i18n.yaml # .agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.md # .agents/notes/implemented/architecture/2026-07-19-package-invariant-runtime-contracts.zh.md # .agents/notes/implemented/architecture/2026-07-19-package-owned-invariant-service.i18n.yaml # docs/rfc/INDEX.md # packages/AGENTS.md
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* SDK-facing JSON-RPC plugin over stdio. An external `cordis.yml` decides
|
||||
* whether to load it; see the single-executable RFC and package README.
|
||||
* whether to load it; see the single-executable Agent Note and package README.
|
||||
* Stdout is reserved for protocol frames, so the tree must not load a stdout logger.
|
||||
* This plugin answers `shutdown`, disposes its own fiber, and exits 0; the app bin
|
||||
* owns EOF and signal exits. Keep named plugin exports with no default export so
|
||||
@@ -22,8 +22,10 @@ export const name = 'jsonrpc'
|
||||
// Only the agent factory is required; initialize reads the optional LLM seam with ctx.get().
|
||||
export const inject = ['agents']
|
||||
|
||||
/** Runtime-only test seams; no field is configurable from `cordis.yml`. */
|
||||
/** JSON-RPC deployment config plus runtime-only test seams. */
|
||||
export interface JsonRpcConfig {
|
||||
/** Report max-token turn/subagent termination as a successful SDK result. */
|
||||
maxTokensAsSuccess?: boolean
|
||||
/** Transport input override; production uses `process.stdin`. */
|
||||
input?: Readable
|
||||
/** Transport output override; production uses `process.stdout`. */
|
||||
@@ -32,7 +34,9 @@ export interface JsonRpcConfig {
|
||||
exit?: (code: number) => void
|
||||
}
|
||||
|
||||
export const Config: Schema<JsonRpcConfig> = Schema.object({})
|
||||
export const Config: Schema<JsonRpcConfig> = Schema.object({
|
||||
maxTokensAsSuccess: Schema.boolean().default(false),
|
||||
})
|
||||
|
||||
/**
|
||||
* Serve SDK requests over the configured streams. Effect disposal shuts down
|
||||
@@ -41,6 +45,8 @@ export const Config: Schema<JsonRpcConfig> = Schema.object({})
|
||||
* owns root-context disposal for EOF and signals.
|
||||
*/
|
||||
export function apply(ctx: Context, config: JsonRpcConfig): void {
|
||||
// Cordis applies the schema default before invoking the plugin.
|
||||
const resolvedConfig = config as JsonRpcConfig & { maxTokensAsSuccess: boolean }
|
||||
// The later transport callback must dispose this plugin's fiber, not its ambient context.
|
||||
const fiber = ctx.fiber
|
||||
/* v8 ignore next -- production stdio wiring; tests always inject the runtime seams */
|
||||
@@ -51,7 +57,9 @@ export function apply(ctx: Context, config: JsonRpcConfig): void {
|
||||
const exit = config.exit ?? ((code: number): void => { process.exit(code) })
|
||||
|
||||
const transport = new JsonRpcLineTransport(input, output)
|
||||
const server = new HarnessSdkServer(ctx, transport)
|
||||
const server = new HarnessSdkServer(ctx, transport, {
|
||||
maxTokensAsSuccess: resolvedConfig.maxTokensAsSuccess,
|
||||
})
|
||||
|
||||
// Share one exit task and attempt flush and disposal independently before exiting.
|
||||
let exitTask: Promise<void> | undefined
|
||||
|
||||
@@ -57,7 +57,22 @@ function subagentParentOf(carrier: Scoped<SubagentService>): Agent {
|
||||
return carrierKeyOf(carrier) as Agent
|
||||
}
|
||||
|
||||
/** SDK server whose subscriptions and created agents live until {@link shutdown}. */
|
||||
/** Deployment-specific status mapping for SDK turn and subagent outcomes. */
|
||||
export interface HarnessSdkServerOptions {
|
||||
/** Report max-token termination as an accepted result instead of an infrastructure error. */
|
||||
maxTokensAsSuccess?: boolean
|
||||
}
|
||||
|
||||
function successStatus(reason: string, options: HarnessSdkServerOptions): 'ok' | 'error' {
|
||||
if (reason === 'completed') return 'ok'
|
||||
return reason === 'max-tokens' && options.maxTokensAsSuccess === true ? 'ok' : 'error'
|
||||
}
|
||||
|
||||
/**
|
||||
* SDK server over one booted harness context and transport peer. Construction
|
||||
* subscribes to session, agent, and subagent lifecycle events until shutdown;
|
||||
* reinitialization is unsupported.
|
||||
*/
|
||||
export class HarnessSdkServer {
|
||||
private cwd = process.cwd()
|
||||
private provider = 'deepseek'
|
||||
@@ -72,7 +87,9 @@ export class HarnessSdkServer {
|
||||
constructor(
|
||||
private readonly ctx: Context,
|
||||
private readonly transport: JsonRpcTransportPeer,
|
||||
private readonly options: HarnessSdkServerOptions = {},
|
||||
) {
|
||||
const serverOptions = this.options
|
||||
this.disposers.push(ctx.on('session/event', (session, event) => {
|
||||
if (event.type === 'turn/end') {
|
||||
const rec = this.sessions.get(String(session.id))
|
||||
@@ -99,7 +116,7 @@ export class HarnessSdkServer {
|
||||
agentId: String(info.id),
|
||||
parentSessionId: String(parent.session.id),
|
||||
childSessionId: String(info.id),
|
||||
status: info.stopReason === 'completed' ? 'ok' : 'error',
|
||||
status: successStatus(info.stopReason, serverOptions),
|
||||
stopReason: info.stopReason,
|
||||
...(info.lastAssistantMessage === undefined ? {} : { lastAssistantMessage: info.lastAssistantMessage }),
|
||||
})
|
||||
@@ -233,7 +250,7 @@ export class HarnessSdkServer {
|
||||
|
||||
private finishedStatus(reason: TurnEndReason | undefined): 'ok' | 'error' {
|
||||
if (!reason) return 'error'
|
||||
return reason.kind === 'completed' ? 'ok' : 'error'
|
||||
return successStatus(reason.kind, this.options)
|
||||
}
|
||||
|
||||
private hasAdapterFor(provider: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user