711 lines · 20.0 KB · TypeScript
RawDownload
#!/usr/bin/env node// @effect-diagnostics nodeBuiltinImport:offimport { appendFileSync } from "node:fs"; import * as Effect from "effect/Effect"; import * as NodeServices from "@effect/platform-node/NodeServices";import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as EffectAcpAgent from "effect-acp/agent";import * as AcpError from "effect-acp/errors";import type * as AcpSchema from "effect-acp/schema"; const requestLogPath = process.env.T3_ACP_REQUEST_LOG_PATH;const exitLogPath = process.env.T3_ACP_EXIT_LOG_PATH;const emitToolCalls = process.env.T3_ACP_EMIT_TOOL_CALLS === "1";const emitInterleavedAssistantToolCalls =  process.env.T3_ACP_EMIT_INTERLEAVED_ASSISTANT_TOOL_CALLS === "1";const emitGenericToolPlaceholders = process.env.T3_ACP_EMIT_GENERIC_TOOL_PLACEHOLDERS === "1";const emitAskQuestion = process.env.T3_ACP_EMIT_ASK_QUESTION === "1";const emitXAiAskUserQuestion = process.env.T3_ACP_EMIT_XAI_ASK_USER_QUESTION === "1";const failSetConfigOption = process.env.T3_ACP_FAIL_SET_CONFIG_OPTION === "1";const exitOnSetConfigOption = process.env.T3_ACP_EXIT_ON_SET_CONFIG_OPTION === "1";const promptResponseText = process.env.T3_ACP_PROMPT_RESPONSE_TEXT;const promptDelayMs = Number(process.env.T3_ACP_PROMPT_DELAY_MS ?? "0");const permissionOptionIds = {  allowOnce: process.env.T3_ACP_ALLOW_ONCE_OPTION_ID ?? "allow-once",  allowAlways: process.env.T3_ACP_ALLOW_ALWAYS_OPTION_ID ?? "allow-always",  rejectOnce: process.env.T3_ACP_REJECT_ONCE_OPTION_ID ?? "reject-once",};const sessionId = "mock-session-1"; let currentModeId = "ask";let currentModelId = "default";let parameterizedModelPicker = false;let currentReasoning = "medium";let currentContext = "272k";let currentFast = false;const cancelledSessions = new Set<string>(); function logExit(reason: string): void {  if (!exitLogPath) {    return;  }  appendFileSync(exitLogPath, `${reason}\n`, "utf8");} process.once("SIGTERM", () => {  logExit("SIGTERM");  process.exit(0);}); process.once("SIGINT", () => {  logExit("SIGINT");  process.exit(0);}); process.once("exit", (code) => {  logExit(`exit:${code}`);}); function configOptions(): ReadonlyArray<AcpSchema.SessionConfigOption> {  if (parameterizedModelPicker) {    const baseOptions: Array<AcpSchema.SessionConfigOption> = [      {        id: "mode",        name: "Mode",        category: "mode",        type: "select",        currentValue: currentModeId,        options: availableModes.map((mode) => ({          value: mode.id,          name: mode.name,          ...(mode.description ? { description: mode.description } : {}),        })),      },      {        id: "model",        name: "Model",        category: "model",        type: "select",        currentValue: currentModelId,        options: [          { value: "default", name: "Auto" },          { value: "composer-2", name: "Composer 2" },          { value: "gpt-5.4", name: "GPT-5.4" },          { value: "claude-opus-4-6", name: "Opus 4.6" },        ],      },    ];     switch (currentModelId) {      case "gpt-5.4":        return [          ...baseOptions,          {            id: "reasoning",            name: "Reasoning",            category: "thought_level",            type: "select",            currentValue: currentReasoning,            options: [              { value: "none", name: "None" },              { value: "low", name: "Low" },              { value: "medium", name: "Medium" },              { value: "high", name: "High" },              { value: "extra-high", name: "Extra High" },            ],          },          {            id: "context",            name: "Context",            category: "model_config",            type: "select",            currentValue: currentContext,            options: [              { value: "272k", name: "272K" },              { value: "1m", name: "1M" },            ],          },          {            id: "fast",            name: "Fast",            category: "model_config",            type: "select",            currentValue: String(currentFast),            options: [              { value: "false", name: "Off" },              { value: "true", name: "Fast" },            ],          },        ];      case "composer-2":        return [          ...baseOptions,          {            id: "fast",            name: "Fast",            category: "model_config",            type: "select",            currentValue: String(currentFast),            options: [              { value: "false", name: "Off" },              { value: "true", name: "Fast" },            ],          },        ];      case "claude-opus-4-6":        return [          ...baseOptions,          {            id: "reasoning",            name: "Reasoning",            category: "thought_level",            type: "select",            currentValue: currentReasoning,            options: [              { value: "low", name: "Low" },              { value: "medium", name: "Medium" },              { value: "high", name: "High" },            ],          },          {            id: "thinking",            name: "Thinking",            category: "model_config",            type: "boolean",            currentValue: true,          },        ];      default:        return baseOptions;    }  }   return [    {      id: "model",      name: "Model",      category: "model",      type: "select" as const,      currentValue: currentModelId,      options: [        { value: "default", name: "Auto" },        { value: "composer-2", name: "Composer 2" },        { value: "composer-2[fast=true]", name: "Composer 2 Fast" },        { value: "gpt-5.3-codex[reasoning=medium,fast=false]", name: "Codex 5.3" },      ],    },  ];} function modelConfigOptionsFor(modelId: string): ReadonlyArray<AcpSchema.SessionConfigOption> {  const previousModelId = currentModelId;  try {    currentModelId = modelId;    return configOptions().filter(      (option) => option.category !== "mode" && option.category !== "model",    );  } finally {    currentModelId = previousModelId;  }} function availableModels(): ReadonlyArray<{  readonly value: string;  readonly name: string;  readonly configOptions: ReadonlyArray<AcpSchema.SessionConfigOption>;}> {  return [    { value: "default", name: "Auto" },    { value: "composer-2", name: "Composer 2" },    { value: "gpt-5.4", name: "GPT-5.4" },    { value: "claude-opus-4-6", name: "Opus 4.6" },  ].map((model) => ({    value: model.value,    name: model.name,    configOptions: modelConfigOptionsFor(model.value),  }));} const availableModes: ReadonlyArray<AcpSchema.SessionMode> = [  {    id: "ask",    name: "Ask",    description: "Request permission before making any changes",  },  {    id: "architect",    name: "Architect",    description: "Design and plan software systems without implementation",  },  {    id: "code",    name: "Code",    description: "Write and modify code with full tool access",  },]; function modeState(): AcpSchema.SessionModeState {  return {    currentModeId,    availableModes,  };} const grokAcpModels: ReadonlyArray<AcpSchema.ModelInfo> = [  { modelId: "grok-build", name: "Grok Build" },  { modelId: "grok-mock-alt", name: "Grok Mock Alt" },]; function modelState(): AcpSchema.SessionModelState {  const modelId = grokAcpModels.some((model) => model.modelId === currentModelId)    ? currentModelId    : "grok-build";  return {    currentModelId: modelId,    availableModels: grokAcpModels,  };} const program = Effect.gen(function* () {  const agent = yield* EffectAcpAgent.AcpAgent;   yield* agent.handleInitialize((request) =>    Effect.sync(() => {      parameterizedModelPicker =        request.clientCapabilities?._meta?.parameterizedModelPicker === true;      return {        protocolVersion: 1,        agentCapabilities: { loadSession: true },      };    }),  );   yield* agent.handleAuthenticate(() => Effect.succeed({}));   yield* agent.handleCreateSession(() =>    Effect.succeed({      sessionId,      modes: modeState(),      models: modelState(),      configOptions: configOptions(),    }),  );   yield* agent.handleLoadSession((request) =>    agent.client      .sessionUpdate({        sessionId: String(request.sessionId ?? sessionId),        update: {          sessionUpdate: "user_message_chunk",          content: { type: "text", text: "replay" },        },      })      .pipe(        Effect.as({          modes: modeState(),          models: modelState(),          configOptions: configOptions(),        }),      ),  );   yield* agent.handleSetSessionModel((request) =>    Effect.gen(function* () {      if (!grokAcpModels.some((model) => model.modelId === request.modelId)) {        return yield* AcpError.AcpRequestError.invalidParams(          `Unknown mock model id: ${request.modelId}`,          {            method: "session/set_model",            params: request,          },        );      }      currentModelId = request.modelId;      return {};    }),  );   yield* agent.handleSetSessionConfigOption((request) =>    Effect.gen(function* () {      if (exitOnSetConfigOption) {        return yield* Effect.sync(() => {          process.exit(7);        });      }      if (failSetConfigOption) {        return yield* AcpError.AcpRequestError.invalidParams(          "Mock invalid params for session/set_config_option",          {            method: "session/set_config_option",            params: request,          },        );      }      if (request.configId === "mode" && typeof request.value === "string") {        currentModeId = request.value;      }      if (request.configId === "model" && typeof request.value === "string") {        currentModelId = request.value;      }      if (request.configId === "reasoning" && typeof request.value === "string") {        currentReasoning = request.value;      }      if (request.configId === "context" && typeof request.value === "string") {        currentContext = request.value;      }      if (request.configId === "fast") {        currentFast = request.value === true || request.value === "true";      }      return {        configOptions: configOptions(),      };    }),  );   yield* agent.handleCancel(({ sessionId }) =>    Effect.sync(() => {      cancelledSessions.add(String(sessionId ?? "mock-session-1"));    }),  );   yield* agent.handlePrompt((request) =>    Effect.gen(function* () {      const requestedSessionId = String(request.sessionId ?? sessionId);       if (Number.isFinite(promptDelayMs) && promptDelayMs > 0) {        yield* Effect.sleep(`${promptDelayMs} millis`);      }       if (emitInterleavedAssistantToolCalls) {        const toolCallId = "tool-call-1";         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "agent_message_chunk",            content: { type: "text", text: "before tool" },          },        });         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "tool_call",            toolCallId,            title: "Terminal",            kind: "execute",            status: "pending",            rawInput: {              command: ["echo", "hello"],            },          },        });         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "tool_call_update",            toolCallId,            status: "completed",            rawOutput: {              exitCode: 0,              stdout: "hello",              stderr: "",            },          },        });         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "agent_message_chunk",            content: { type: "text", text: "after tool" },          },        });         return { stopReason: "end_turn" };      }       if (emitToolCalls) {        const toolCallId = "tool-call-1";         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "tool_call",            toolCallId,            title: "Terminal",            kind: "execute",            status: "pending",            rawInput: {              command: ["cat", "server/package.json"],            },          },        });         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "tool_call_update",            toolCallId,            status: "in_progress",          },        });         const permission = yield* agent.client.requestPermission({          sessionId: requestedSessionId,          toolCall: {            toolCallId,            title: "`cat server/package.json`",            kind: "execute",            status: "pending",            content: [              {                type: "content",                content: {                  type: "text",                  text: "Not in allowlist: cat server/package.json",                },              },            ],          },          options: [            { optionId: permissionOptionIds.allowOnce, name: "Allow once", kind: "allow_once" },            {              optionId: permissionOptionIds.allowAlways,              name: "Allow always",              kind: "allow_always",            },            { optionId: permissionOptionIds.rejectOnce, name: "Reject", kind: "reject_once" },          ],        });         const cancelled =          cancelledSessions.delete(requestedSessionId) ||          permission.outcome.outcome === "cancelled";         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "tool_call_update",            toolCallId,            title: "Terminal",            kind: "execute",            status: "completed",            rawOutput: {              exitCode: 0,              stdout: '{ "name": "t3" }',              stderr: "",            },          },        });         yield* agent.client.sessionUpdate({          sessionId: requestedSessionId,          update: {            sessionUpdate: "agent_message_chunk",            content: { type: "text", text: "hello from mock" },

Lines 1–500 of 711.