SpyBara
Go Premium

plugins/mods/api.md 2026-11-07 23:58 UTC to 2026-11-08 22:58 UTC

No changes for this document in this history range.

2026
Mon 2 23:57 Wed 11 22:59

Gunakan mods API

Panggil mods API dari mod Claude Code untuk menambahkan perintah dan tool, memanggil model, menjalankan pekerjaan pada timer, mengirim pesan ke sesi lain, dan mengakses file serta jaringan.

Mods API adalah kumpulan metode yang dipanggil mod untuk bertindak: menambahkan perintah dan alat, memanggil model, menjalankan pekerjaan antar peristiwa, dan mengakses sistem file, proses, dan jaringan. Setiap hook menerimanya sebagai argumen pertamanya, $, dengan metode yang dikelompokkan dalam namespace seperti $.ui dan $.fs. Events menentukan kapan hook berjalan, dan mods API adalah apa yang dipanggil hook setelah berjalan.

Bangun mod pertama Anda sebelum Anda mulai di sini. Untuk setiap metode, lihat mods API methods atau baca tipe untuk build Anda.

Tambahkan perintah atau alat

Mod dapat menambahkan perintah untuk dijalankan pengguna dan alat untuk dipanggil Claude. Daftarkan keduanya dalam hook session.start. Claude Code menunggu hook itu sebelum prompt pertama, jadi apa yang Anda daftarkan tersedia dari giliran pertama.

Tambahkan perintah

Perintah adalah untuk pengguna. Daftarkan, kemudian tangani command.run untuk namanya. Contoh ini menambahkan perintah /standup yang mengambil jumlah hari opsional:

on('session.start', async ($, e, next) => {
  // Add /standup to the command list, with the description the user sees there
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// The matcher limits the hook to /standup, so other commands don't reach it
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args is the text typed after the command name, or an empty string
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})

Setelah sesi dimulai, /standup muncul dengan deskripsinya dalam daftar yang Anda lihat saat mengetik /. argumentHint ditampilkan dalam prompt setelah Anda mengetik perintah dan spasi, seperti /standup [days]. Saat Anda menjalankan /standup 3, hook kedua mengembalikan Summary for the last 3 day(s): ..., dan transkrip menunjukkan teks itu setelah nama plugin. Hook tidak pernah memanggil next, karena perintah tidak memiliki perilaku selain milik Anda.

text yang Anda kembalikan dicetak dalam transkrip dan Claude membacanya. Untuk tidak mencetak apa pun, seperti perintah yang hanya membuka pane, kembalikan {}. Untuk membiarkan perintah berjalan saat Claude sedang bekerja, tambahkan immediate: true ke pendaftaran.

Pilih nama yang tidak digunakan oleh perintah bawaan. Ketik / dalam sesi untuk melihatnya. $.command.register melempar untuk nama yang diambil, dengan pesan seperti "/focus" refused: it is the built-in /focus. Hook yang melempar dilewati, jadi sisa hook session.start Anda tidak berjalan juga. Daftarkan perintah terakhir dalam hook itu, atau bungkus panggilan dalam try dan catch.

Tambahkan alat

Alat adalah untuk Claude. Daftarkan dengan nama, deskripsi yang dibaca Claude, dan JSON Schema untuk inputnya. Claude melihatnya dengan nama yang lebih panjang yang terdiri dari mcp__, nama plugin Anda, dua garis bawah, dan nama yang Anda daftarkan. Anda menangani panggilannya dalam hook tool.call yang disaring ke nama lengkap itu. Contoh ini, dari plugin bernama my-mod, mendaftarkan ticket, jadi nama lengkapnya adalah mcp__my-mod__ticket. Ini memberi Claude alat yang mencari tiket dalam pelacak masalah:

on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude decides when to call the tool from this description
    description: 'Look up a ticket by its id and return its title and status',
    // The arguments Claude has to send: one required string named id
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// The full tool name is mcp__, the plugin's name, and the registered name
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // The tool's arguments are fields of e, so the id is e.id
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // Return a result either way, so Claude learns when the lookup failed
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})

Saat Anda bertanya tentang tiket, Claude dapat memanggil mcp__my-mod__ticket dengan id-nya. Hook kedua mengambil tiket dan mengembalikan badan respons, yang dibaca Claude sebagai hasil alat. Saat server menjawab dengan status kesalahan, Claude membaca Lookup failed with status dan nomornya.

[!TIP]

Saat MCP tool search menunda tool yang terdaftar, Claude melihat namanya tetapi tidak melihat deskripsinya sampai Claude mencarinya. Jika Claude perlu mempertimbangkan tool tersebut di setiap giliran, tambahkan isDeferred: false ke pendaftaran untuk memuat tool lengkap sejak awal. Field ini memerlukan Claude Code v2.1.293 atau yang lebih baru, dan versi sebelumnya mengabaikannya.

Panggil model

Mod dapat mengirim permintaannya sendiri ke model untuk pekerjaan kecil seperti mengklasifikasikan atau merangkum teks. $.model.complete mengirim prompt Anda secara tersendiri, dan $.model.fork({ prompt }) mengirim percakapan saat ini dengan prompt Anda di bagian akhir.

Tabel ini membandingkan isi setiap permintaan:

Dalam permintaan $.model.complete $.model.fork
Model model yang Anda berikan Model sesi
System prompt Blok atribusi singkat, lalu system Anda jika Anda memberikannya System prompt sesi
Pesan Satu pesan pengguna, yaitu prompt Anda Percakapan sejauh ini, lalu prompt Anda sebagai pesan pengguna
CLAUDE.md dan konteks proyek lainnya Tidak disertakan Disertakan, seperti pada permintaan terakhir percakapan
Tool Tidak ada Tool Claude, yang tidak dapat dipanggil oleh model

Fork mengulang permintaan terakhir percakapan, sehingga Claude API melayani sebagian besarnya dari cache prompt selama percakapan masih berada di cache.

Kedua panggilan menggunakan kredensial sesi, sehingga tagihannya dibebankan ke paket, kunci API, atau penyedia cloud pengguna. Tipe untuk build Anda mendokumentasikan setiap metode $.model.

Kirim satu prompt

Berikan model dan prompt ke $.model.complete. prompt menjadi pesan pengguna. Untuk memberikan instruksi kepada model, seperti peran atau format output, berikan juga system, yang menjadi system prompt.

Hook ini menjawab perintah /triage, didaftarkan sebagai perintah, dengan menanyakan model kecil untuk memberi label pada teks yang diketik setelahnya:

on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    // The system prompt sets the job, and the prompt carries the text to label
    system: 'Reply with one word: bug, feature, or question.',
    prompt: e.args,
    // One word needs few tokens, and the call gives up after 15 seconds
    maxTokens: 20,
    timeoutMs: 15000,
  })
  // r.text exists only when the model answered, so check r.isAnswered first
  const label = r.isAnswered ? r.text.trim() : 'unknown'
  return { text: 'Label: ' + label }
})

Saat Anda menjalankan /triage the export button does nothing, mod mengirim teks itu ke model dan mencetak jawabannya, seperti Label: bug. Saat model tidak menjawab, labelnya adalah unknown.

Kegagalan Claude API tidak menolak panggilan, jadi periksa r.isAnswered, dan baca r.reason saat nilainya false. Panggilan ditolak untuk permintaan yang tidak akan dikirim Claude Code, seperti model yang diblokir organisasi Anda.

Tipe untuk build Anda mencantumkan opsi lain, seperti effort, dan batas memberikan default maxTokens.

Gunakan prompt caching

$.model.complete mendukung prompt caching Claude API. API menyimpan ke cache bagian awal permintaan, yang disebut prefiks, hingga cache breakpoint yang Anda tetapkan. Saat setiap panggilan dimulai dengan konten statis panjang yang sama, seperti instruksi atau materi referensi, tetapkan breakpoint di akhir konten tersebut. Panggilan berikutnya kemudian membacanya dari cache alih-alih membayar harga input penuh untuk konten itu.

Untuk menetapkan breakpoint, berikan prompt sebagai array blok { text } alih-alih string, dan tambahkan cache: true ke blok terakhir dari konten statis. Claude Code mengirim blok tersebut dengan field cache_control milik API. system menerima bentuk array yang sama. Untuk memilih di antara keduanya, lihat Memilih antara prompt dan system.

[!NOTE]

Array blok memerlukan Claude Code v2.1.292 atau lebih baru. Versi sebelumnya menolak array dalam prompt dengan error yang diakhiri dengan takes { model, prompt } (host check), dan tidak menyertakan array dalam system ke dalam permintaan.

Versi hook /triage ini mengirim serangkaian aturan pelabelan yang panjang sebelum teks yang akan diberi label, dengan breakpoint setelah aturan tersebut. RULES adalah string milik Anda sendiri:

on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    prompt: [
      // Identical on every call, so it forms the cached prefix
      { text: RULES, cache: true },
      // Changes on every call, so it goes after the breakpoint
      { text: e.args },
    ],
  })
  return { text: 'Label: ' + (r.isAnswered ? r.text.trim() : 'unknown') }
})

TTL dan jumlah breakpoint memiliki batas berikut:

  • TTL: entri cache bertahan lima menit setelah penggunaan terakhirnya. TTL berasal dari pengaturan Claude Code pengguna, bukan dari panggilan. Untuk satu jam, atur subagentPromptCacheTtl ke 1h.
  • Breakpoint per permintaan: API menerima hingga empat, dan satu lagi akan dikembalikan sebagai api-error dalam r.reason

Memilih antara prompt dan system

Letakkan konten statis yang digunakan bersama oleh panggilan Anda di awal prompt, kecuali Anda tahu bahwa permintaan Anda langsung menuju Claude API:

  • Langsung ke Claude API, dengan kunci API atau langganan Claude: kedua field dapat digunakan
  • Melalui Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, Microsoft Foundry, atau LLM gateway: gunakan prompt. Claude Code memulai system prompt dengan blok atribusi yang fingerprint-nya berasal dari awal pesan pengguna. Endpoint api.anthropic.com menghapus blok tersebut sebelum caching. Endpoint lain menerimanya sebagai bagian dari prompt, sehingga breakpoint dalam system dapat meleset saat prompt dimulai secara berbeda.
  • Dalam mod yang dijalankan orang lain: gunakan prompt, karena Anda tidak memilih penyedia mereka

system muncul sebelum prompt dalam prefiks, sehingga breakpoint dalam prompt juga mencakup system, dan panggilan dengan system yang berbeda akan meleset dari cache.

Memeriksa cache hit

Hasil dari $.model.complete memiliki objek usage dengan field cache milik API. usage.cache_creation_input_tokens menghitung token yang ditulis panggilan ke cache, dan usage.cache_read_input_tokens menghitung token yang dibaca panggilan dari cache. Harapkan penulisan pada panggilan pertama dan pembacaan pada panggilan berikutnya dalam TTL.

Jika setiap panggilan menulis dan tidak ada yang membaca, prefiks berbeda antar panggilan atau jarak antar panggilan lebih lama dari TTL. Untuk prefiks yang berbeda, lihat Memilih antara prompt dan system.

Jika kedua field tetap bernilai nol pada panggilan yang dijawab model, tidak ada yang disimpan ke cache. Periksa setiap penyebab berikut:

Apa yang diterima hook model.complete

Jika Anda memasang hook pada event model.complete untuk memeriksa atau mengubah permintaan mod lain, baca teks dari field berikut:

  • e.prompt: selalu berupa string. Saat pemanggil memberikan array, nilainya adalah teks blok-blok yang digabungkan secara berurutan.
  • e.system: string yang dibangun dengan cara yang sama, atau tidak ada saat pemanggil tidak memberikan system
  • e.promptBlocks dan e.systemBlocks: array milik pemanggil, masing-masing ada saat pemanggil memberikan array untuk field tersebut

Claude Code mengirim string yang diteruskan hook Anda ke next dan menggunakan array yang Anda berikan bersamanya untuk menempatkan cache breakpoint. Claude Code mempertahankan blok-blok awal yang masih cocok dengan awal string, beserta breakpoint-nya, dan mengirim sisa string tanpa breakpoint. Misalnya, next({ ...e, prompt: e.prompt + NOTE }) mempertahankan breakpoint pemanggil, sedangkan hook yang mengubah awal prompt menghapusnya.

Jalankan pekerjaan di latar belakang

Pekerjaan yang melampaui satu peristiwa, seperti memeriksa sesuatu sekali semenit, berjalan pada timer yang Anda mulai dari session.start. Hook itu sendiri berjalan untuk satu peristiwa dan memiliki batas waktu untuk waktu eksekusinya sendiri. Waktu yang dihabiskan menunggu next atau panggilan mods API tidak dihitung, kecuali $.clock.sleep. $.clock.every dan $.clock.after menggantikan setInterval dan setTimeout, dengan penundaan dalam milidetik terlebih dahulu: $.clock.after(5000, fn) memanggil fn sekali, lima detik dari sekarang. Masing-masing mengembalikan timer dengan metode cancel(), dan await $.clock.now() memberikan waktu dalam milidetik.

Hook ini mencari pemeriksaan permintaan tarik sekali semenit dan menampilkan hasilnya di bawah prompt. summarize adalah fungsi Anda sendiri yang mengubah output JSON perintah menjadi beberapa kata:

on('session.start', async ($, e, next) => {
  // Call the function every 60,000 milliseconds, starting one minute from now
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // Replace the line under the prompt with the latest summary
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // Return without waiting for the timer, so the session starts right away
  return next(e)
})

Sesi dimulai seperti biasa. Satu menit kemudian, baris muncul di bawah prompt dengan ⚠, nama mod, dan kemudian checks: dan ringkasan Anda. Itu diganti sekali semenit setelahnya. Callback timer berjalan di luar peristiwa apa pun, jadi terus berjalan antar giliran dan tidak memulai satu. Jika callback melempar, kesalahan masuk ke debug log dan timer berjalan lagi pada interval berikutnya.

Tampilkan sesuatu tanpa memulai giliran

Pekerjaan latar belakang dapat menunjukkan kepada pengguna sesuatu tanpa memulai giliran. Masing-masing panggilan ini menempatkan teks di tempat yang berbeda:

Panggilan Apa yang dilihat pengguna
$.ui.status(text) Satu baris di bawah prompt yang tetap sampai Anda mengubahnya. Dimulai dengan ⚠ dan nama mod, seperti ⚠ my-mod: checks: 3 passing.
$.ui.toast(text) Notifikasi toast dengan nama mod yang hilang setelah beberapa detik. Berupa kotak di kanan atas dalam rendering layar penuh, dan satu baris di sebelah kanan di bawah prompt dalam renderer klasik.
$.ui.log(text) Baris redup dalam transkrip yang tidak dibaca Claude. Dimulai dengan ● dan nama mod, seperti ● my-mod: build finished.
$.ui.notify(text) Notifikasi dari terminal mereka, di luar Claude Code, yang dikirim sesuai pengaturan preferredNotifChannel mereka. Nama mod menjadi judulnya kecuali Anda meneruskan { title } yang tidak kosong, yang ditampilkan persis seperti yang Anda berikan.

$.ui.notify adalah satu-satunya panggilan di antara ini yang memberikan jawaban. Panggilan ini menyelesaikan dengan { isSent: true, channel } saat channel pengguna menulis notifikasi ke terminal mereka. channel adalah nilai pengaturan yang mengirimkannya, seperti kitty atau terminal_bell. Channel terminal_bell membunyikan bel dan tidak menampilkan teks Anda maupun judulnya.

Jika tidak, panggilan menyelesaikan dengan { isSent: false, reason }:

reason Arti
disabled Pengguna mengatur preferredNotifChannel ke notifications_disabled
no-channel Pengaturannya auto dan terminal mereka tidak memiliki notifikasi yang dapat dikirim Claude Code
no-surface Sesi tidak memiliki terminal, seperti dalam eksekusi -p, Agent SDK, aplikasi desktop, ekstensi VS Code, atau sesi cloud. Juga merupakan jawaban untuk panggilan yang dilakukan sebelum sesi dimulai, saat mod Anda sedang dimuat, dan untuk panggilan yang dilakukan saat Claude Code sedang keluar
refused Hook milik mod lain pada ui.notify menjawab menggantikan Claude Code tanpa mengirim

Panggilan menolak alih-alih menyelesaikan saat text bukan string, saat hook milik mod lain pada ui.notify mengembalikan { deny }, atau saat penulisan ke terminal gagal.

Mod Anda tidak dapat memilih channel, dan Claude Code tidak membatasi seberapa sering mod mengirim notifikasi. Pengaturan pengguna adalah satu-satunya pengendali.

Saat panggilan mencapai sesi yang sedang berjalan, hook Notification milik pengguna berjalan dengan tipe plugin_notification. Hook tersebut tetap berjalan bahkan saat jawabannya disabled, dan dalam sesi tanpa terminal, dengan jawaban no-surface. Tidak ada hook yang berjalan untuk panggilan yang dilakukan sebelum sesi dimulai atau saat Claude Code sedang keluar. Hook tempat mod Anda memanggil $.ui.notify dikecualikan dari eksekusi tersebut, sehingga hook classic.Notification yang mengirim notifikasi tidak dipanggil lagi untuk notifikasinya sendiri.

Terminal tidak melaporkan apakah notifikasi telah ditampilkan, jadi isSent: true berarti notifikasi telah dikirim, bukan berarti pengguna telah melihatnya.

Contoh ini mengirim notifikasi saat giliran percakapan utama berakhir tanpa interupsi, dan beralih ke toast sebagai fallback saat tidak ada notifikasi dengan teks yang terkirim. Contoh ini tidak menunggu notifikasi sebelum meneruskan peristiwa, karena hook Notification milik pengguna berjalan terlebih dahulu dan dapat memakan waktu:

on('turn.complete', ($, e, next) => {
  const isMainTurn = e.agentId === undefined && !e.isAborted
  const fallBack = () => $.ui.toast('Turn finished')
  if (isMainTurn) {
    $.ui.notify('Turn finished').then(sent => {
      const hasText = sent.isSent && sent.channel !== 'terminal_bell'
      if (!hasText) fallBack()
    }, fallBack)
  }
  return next(e)
})

Di ekstensi VS Code, baris status dan toast ditampilkan di atas kotak prompt, dan baris log ditampilkan dalam percakapan.

Mulai giliran dari pekerjaan latar belakang

Saat pekerjaan latar belakang menemukan sesuatu yang memerlukan perhatian Claude, itu dapat memulai giliran dengan mengirimkan prompt dengan $.prompt.submit({ text }). Claude membaca teks setelah kalimat yang menyebutkan mod Anda sebagai pengirim. Untuk mengirimnya sebagai kata-kata pengguna sendiri, tanpa kalimat itu, tambahkan asUser: true. Panggilan menunggu sampai sesi menganggur dan kemudian memulai giliran baru. Itu menyelesaikan saat giliran itu dimulai, jadi jangan await dalam handler yang berjalan saat Claude sedang bekerja.

Hentikan pekerjaan latar belakang

Timer berhenti saat modul dimuat ulang. Untuk pekerjaan jangka panjang dalam hook, next.signal adalah AbortSignal yang membatalkan saat peristiwa yang ditangani hook Anda ditinggalkan, misalnya saat pengguna menginterupsi. Teruskan ke apa pun yang berjalan lama.

Panggilan dan pekerjaan latar belakang setelah pengguna menginterupsi giliran

Alasan the session was stopped and nobody has sent input since dalam hasil atau kesalahan berarti pengguna menghentikan giliran yang sedang berjalan, misalnya dengan Esc atau Stop. Setelah interupsi tersebut, panggilan yang memulai pekerjaan baru dalam sesi ditolak sampai input pengguna berikutnya, seperti pesan di prompt.

Selain $.prompt.submit dan $.command.run, penanganan panggilan dan pekerjaan latar belakang setelah interupsi ini memerlukan Claude Code v2.1.304 atau yang lebih baru.

Coba ulang panggilan yang ditolak atau dibuang

Lakukan kembali panggilan yang ditolak atau dibuang dari hook turn.complete. Panggilan tetap ditolak sampai input pengguna berikutnya, dan tidak ada yang mengantrekan atau mengulanginya untuk Anda.

Saat hasil atau kesalahan suatu panggilan berisi salah satu string berikut, simpan argumennya untuk retry nanti:

  • the session was stopped and nobody has sent input since: panggilan ditolak
  • was removed from the queue before it ran: prompt atau perintah keluar dari antrean tanpa dijalankan dan dibuang, misalnya saat pengguna menghentikan giliran

Dalam contoh ini, panggil submitPrompt dari pekerjaan latar belakang Anda sebagai pengganti $.prompt.submit. Letakkan variabel pendingText dan fungsi submitPrompt di atas register, dan hook turn.complete di dalamnya, karena validasi menolak $ yang diteruskan ke fungsi yang dideklarasikan di dalam register. Saat hasil atau kesalahan membawa salah satu string tersebut, submitPrompt menyimpan teks di pendingText agar hook turn.complete mengirimkannya kembali:

// The text of the last refused or dropped prompt, or null
let pendingText = null

async function submitPrompt($, text) {
  let reason
  try {
    const result = await $.prompt.submit({ text })
    reason = result?.drop ?? ''
  } catch (error) {
    reason = String(error?.message ?? error)
  }
  if (reason.includes('the session was stopped and nobody has sent input since') ||
      reason.includes('was removed from the queue before it ran')) {
    pendingText = text
  }
  // Any other result or error isn't retried
}

on('turn.complete', async ($, e, next) => {
  if (pendingText !== null) {
    const text = pendingText
    // Empty pendingText before retrying, so that a second run of this hook
    // doesn't submit the same text.
    pendingText = null
    // Not awaited: the hook would stay open until the new turn starts
    submitPrompt($, text)
  }
  return next(e)
})

Sebelum input pengguna berikutnya, turn.complete dapat terpicu untuk giliran yang diinterupsi itu sendiri dan untuk giliran subagent, dan panggilan yang dilakukan saat itu ditolak lagi. Dalam kasus tersebut, submitPrompt menyimpan teks untuk turn.complete berikutnya.

Untuk panggilan yang menolak (reject), seperti $.command.run, tangkap kesalahannya dan uji pesannya dengan cara yang sama seperti submitPrompt menguji reason.

Apa yang dikembalikan panggilan yang ditolak

Dalam hasil, <reason> mewakili alasan panggilan ditolak. Setelah interupsi, alasannya adalah the session was stopped and nobody has sent input since. Untuk alasan lain, lihat Panggilan yang ditolak dalam eksekusi claude -p dan sesi Agent SDK.

Beberapa panggilan yang ditolak Hasil
$.prompt.submit Menyelesaikan dengan { drop: '<reason>' } dan tidak mengantrekan prompt apa pun
$.command.run Menolak dengan kesalahan yang pesannya diakhiri dengan <reason>
$.agent.spawn Menyelesaikan dengan { deny: '<reason>' } dan tidak memulai subagent apa pun
$.session.send ke subagent yang sudah selesai Menyelesaikan dengan { isDelivered: false, reason: 'Agent "<id>" was not resumed: <reason>.' }, dengan <id> adalah id subagent. Subagent yang masih berjalan menerima pesan tersebut.
$.session.send ke rekan satu tim dalam tim agent Menyelesaikan dengan { isDelivered: false, reason: 'Teammate "<name>" was not messaged: <reason>.' }, dengan <name> adalah nama rekan satu tim tersebut
$.tool.call untuk Bash dengan run_in_background: true, Monitor, atau CronCreate Menyelesaikan dengan { deny: '<tool> was not run: it starts background work, a schedule or a goal, and <reason>' }, dengan <tool> adalah nama tool tersebut

Saat salah satu panggilan $.session.send atau $.tool.call ini ditolak setelah interupsi dalam sesi interaktif, pengguna dapat melihat toast Stopped:.

Pekerjaan yang dimulai mod Anda sebelum interupsi

Setelah pengguna menghentikan giliran yang sedang berjalan, misalnya dengan Esc, sebagian pekerjaan yang sudah dimulai mod Anda dibuang dan sebagian lagi menunggu input pengguna berikutnya.

Pekerjaan Apa yang terjadi sampai input pengguna berikutnya Apa yang harus dilakukan
Prompt dari $.prompt.submit yang masih dalam antrean Dibuang. Panggilan menyelesaikan dengan { drop: 'the prompt was removed from the queue before it ran' }. Kirimkan kembali seperti yang ditunjukkan dalam Coba ulang panggilan yang ditolak atau dibuang
Perintah dari $.command.run yang masih dalam antrean Dibuang. Panggilan menolak dengan kesalahan yang pesannya mencakup the command was removed from the queue before it ran. Jalankan kembali seperti yang ditunjukkan dalam Coba ulang panggilan yang ditolak atau dibuang
Pekerjaan satu kali yang dijadwalkan dengan tool seperti CronCreate yang jatuh tempo sebelum input pengguna berikutnya Tidak memulai giliran saat itu, dan berjalan setelah input tersebut Tidak perlu melakukan apa pun
Pekerjaan berulang yang dijadwalkan dengan tool seperti CronCreate yang jatuh tempo sebelum input pengguna berikutnya Tidak memulai giliran saat itu. Pekerjaan tersebut kehilangan eksekusi itu dan tetap mempertahankan jadwalnya. Tidak perlu melakukan apa pun
Peristiwa dari monitor yang dimulai mod Anda Monitor terus berjalan. Peristiwa yang dimunculkan monitor sebelum input pengguna berikutnya dibuang, kecuali pemberitahuan bahwa monitor telah berakhir, yang tiba setelah input tersebut. Tidak ada yang dapat dilakukan
Subagent yang di-spawn mod Anda dan kemudian dilanjutkan dengan $.session.send yang selesai sebelum input pengguna berikutnya Pemberitahuan selesainya menunggu, dan mencapai percakapan setelah input tersebut Tidak perlu melakukan apa pun
Perintah Bash atau PowerShell latar depan dari $.tool.call yang sudah berjalan saat pengguna menghentikan giliran dan mencapai timeout-nya sebelum input pengguna berikutnya Dihentikan pada timeout-nya alih-alih dipindahkan ke latar belakang. Panggilan menyelesaikan dengan isError diatur, dan output mencakup baris Ended instead of moving to the background: plugins' input is held, by a Stop or by the host's limits. Jalankan kembali setelah input pengguna berikutnya, seperti yang ditunjukkan dalam Coba ulang panggilan yang ditolak atau dibuang

Saat pekerjaan terjadwal jatuh tempo atau monitor memunculkan peristiwa sebelum input pengguna berikutnya, pengguna dapat melihat toast Stopped:.

Panggilan yang ditolak dalam eksekusi claude -p dan sesi Agent SDK

Dalam eksekusi claude -p atau sesi Agent SDK, hasil seperti { drop: 'the run reached its --max-turns limit' } berarti eksekusi tersebut menolak panggilan mod Anda.

$.prompt.submit yang ditolak menyelesaikan dengan { drop: '<reason>' }. Panggilan lain yang ditolak, seperti $.command.run dan $.agent.spawn, memberikan hasil yang ditunjukkan dalam Apa yang dikembalikan panggilan yang ditolak dengan alasannya menggantikan <reason>. Temukan <reason> Anda di bawah untuk mengetahui apa yang harus dilakukan:

  • the run reached its --max-turns limit: giliran dari prompt mod Anda dan dari subagent yang di-spawn-nya dihitung terhadap --max-turns atau batas giliran Agent SDK. Untuk mendapatkan lebih banyak giliran, mulai eksekusi dengan batas yang lebih tinggi. Jika tidak bisa, buat mod Anda berhenti melakukan panggilan yang ditolak. Jika eksekusi menerima prompt lain dari pengguna atau aplikasi SDK, hitungan giliran diatur ulang dan eksekusi kembali menerima panggilan.
  • the run is past its --max-budget-usd limit: mulai eksekusi dengan --max-budget-usd yang lebih tinggi, atau naikkan batas pengeluaran Agent SDK. Jika tidak bisa, buat mod Anda berhenti melakukan panggilan yang ditolak. Batas tersebut berlaku untuk total biaya eksekusi sejauh ini. Total itu terus bertambah saat eksekusi menerima prompt lain, sehingga retry mengembalikan alasan yang sama.
  • the host has ended its input: misalnya, aplikasi SDK mengakhiri input streaming-nya, atau stdin ditutup pada eksekusi yang dimulai dengan --input-format stream-json. Eksekusi sedang berakhir dan tidak menerima input lagi, sehingga retry mengembalikan alasan yang sama. Buat mod Anda berhenti melakukan panggilan yang ditolak. Jika Anda sendiri yang memulai eksekusi, lain kali biarkan input tersebut tetap terbuka sampai mod Anda tidak memiliki panggilan lagi untuk dilakukan.
  • the session was stopped and nobody has sent input since: eksekusi diinterupsi, misalnya dengan interrupt() milik Agent SDK. Untuk melakukan panggilan lagi setelah pesan berikutnya dari pengguna atau aplikasi SDK, ikuti Coba ulang panggilan yang ditolak atau dibuang.

Eksekusi claude -p dengan mod Anda tidak pernah keluar

Jika mod Anda mengirimkan prompt di akhir setiap giliran, setiap prompt memulai giliran lain. Tanpa --max-turns atau --max-budget-usd, eksekusi claude -p yang memuat mod tersebut terus berjalan sampai Anda menghentikannya sendiri.

Agar eksekusi berakhir dengan sendirinya, tambahkan salah satu flag tersebut ke perintah di shell Anda. Jika orang lain yang memulai eksekusi, buat mod Anda berhenti mengirimkan prompt setelah pekerjaannya selesai. Perintah ini menambahkan --max-turns:

claude -p "find the TODOs" --max-turns 5

Setelah eksekusi mencapai batas tersebut, prompt berikutnya dari mod ditolak alih-alih memulai giliran, dan eksekusi berakhir.

Jika eksekusi masih melewati batas, jalankan claude --version di shell Anda, dan perbarui Claude Code jika versi Anda lebih lama dari v2.1.304. Prompt dari mod ditolak saat mencapai batas mulai dari v2.1.299. Jika mod Anda men-spawn subagent, giliran subagent tersebut dihitung terhadap --max-turns mulai dari v2.1.304.

Mengirim dan menerima pesan antar sesi

Sebuah mod dapat mengirim pesan teks biasa ke sesi Anda yang lain, ke salah satu subagent milik sesi ini, atau ke rekan tim dalam tim agent miliknya. Mod juga dapat mengamati pesan yang masuk dan keluar.

Untuk mengirim satu pesan, panggil $.session.send({ to, text }), yang melakukan pengiriman yang sama seperti yang dilakukan tool SendMessage. Atur to berdasarkan siapa yang menerima pesan:

  • Sesi Anda yang lain: { sessionId }
  • Subagent atau rekan tim: { agentId }, dengan id dari $.agent.list()
  • Pengirim pesan yang Anda terima: alamat string asal pesan tersebut

Panggilan ini selesai setelah pesan masuk antrean, dengan { isDelivered: true }. Ketika tidak ada yang tersampaikan, panggilan selesai dengan { isDelivered: false, reason }, dan reason menjelaskan alasannya.

Hook ini menjawab perintah /ping, yang didaftarkan sebagai perintah, dengan menanyakan status kepada sesi yang id-nya Anda ketik setelah perintah tersebut:

on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args is the session id typed after /ping
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // The call resolves either way, so check isDelivered to learn what happened
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // An empty result prints nothing in this session's transcript
  return {}
})

Ketika pesan masuk antrean, tidak ada yang muncul di sesi Anda, dan Claude di sesi lain membaca Status? One line. Ketika tidak ada yang tersampaikan, notifikasi toast menampilkan alasannya.

session.receive dan session.send memungkinkan mod mengamati pesan. Kembalikan next(e) dari keduanya untuk meneruskan setiap pesan tanpa perubahan:

Event Dipicu ketika Field yang berguna
session.receive Sebuah pesan tiba untuk sesi ini, sebelum Claude membacanya e.text, dan e.origin.kind, seperti peer atau peer-send-message untuk sesi atau agent lain, task-notification, atau scheduled-trigger. Kembalikan { consumed: reason } agar pesan tidak sampai ke Claude.
session.send Sebuah pesan akan keluar, dari tool SendMessage atau dari mod e.to, e.text, dan e.origin.kind, yang bernilai model atau plugin

Sesi yang diatur untuk menolak pesan masuk menolak pesan sebelum session.receive dipicu, sehingga hook tidak pernah melihatnya. Pesan yang ditahan untuk menunggu persetujuan Anda sampai ke hook terlebih dahulu, sehingga mod dapat membaca pesan yang belum Anda setujui. next(e) milik hook akan ditolak (reject) ketika pesan tidak tersampaikan.

Nama pengirim pada pesan yang diterima adalah apa pun yang ditulis oleh pengirim, jadi jangan mendasarkan keputusan padanya.

Jangkau file, proses, dan jaringan

Mod menjangkau sistem file, proses, dan jaringan melalui mods API, dengan izin yang sama dengan pengguna yang menjalankan Claude Code. Modul hook itu sendiri tidak memiliki API Node.js, tidak ada global timer seperti setTimeout, dan tidak memiliki akses jaringan atau file sendiri. API JavaScript standar dan web seperti URL, TextEncoder, AbortController, dan crypto.subtle tersedia. Setiap namespace di bawah mencakup satu jenis akses:

Namespace Apa yang dilakukannya
$.fs read(path), write(path, text), exists(path), stat(path), dan list(path) bekerja pada file dan direktori
$.process run(['git', 'status']) memulai perintah dan menyelesaikan saat keluar. spawn mengalirkan output perintah yang berjalan lama.
$.http fetch(url, init) atas http atau https. Itu menyelesaikan ke { status, ok, headers, text } setelah badan dibaca.
$.store Penyimpanan kunci-nilai JSON plugin Anda sendiri, disimpan antar sesi
$.env get dan set environment variable. Tulis nama sebagai string literal.
$.settings read() menyelesaikan ke semua pengaturan, digabungkan menjadi satu objek. Dengan { source } atau { key }, fungsi ini menyelesaikan ke pengaturan satu sumber atau nilai satu kunci sebagai gantinya.
$.session messages() mengembalikan transkrip sebagai daftar { role, text, toolUses }. Juga direktori kerja, model, dan lainnya. usage() mengembalikan penggunaan context window dan batas paket.
$.mcp call tool pada server MCP yang terhubung

File, proses, dan permintaan memiliki beberapa aturan mereka sendiri:

  • Paths: jalur relatif di-resolve terhadap direktori kerja sesi, atau direktori kerja subagent yang peristiwanya sedang ditangani oleh hook
  • $.fs.list: mengembalikan entri satu direktori sebagai { name, kind, size, isLink } dan tidak turun ke subdirektori
  • $.process.run: mengambil daftar argumen dan tidak menggunakan shell. Itu menyelesaikan ke { exitCode, stdout, stderr } apa pun kode keluar. Itu menolak jika program tidak dapat dimulai atau masih berjalan pada timeout, yang merupakan 30 detik secara default, jadi bungkus dalam try dan catch.
  • $.http.fetch: mengikuti hingga lima pengalihan. Pada pengalihan ke origin yang berbeda, fungsi ini hanya mempertahankan header permintaan accept, accept-language, content-type, dan user-agent yang Anda tetapkan dan membuang sisanya, sehingga permintaan yang bergantung pada header lain, seperti Authorization, dapat gagal setelah pengalihan tersebut. Batas memberikan timeout dan ukuran badannya.

Setiap satu dari panggilan ini sendiri adalah peristiwa, dinamai untuk namespace dan metodenya tanpa $., seperti fs.read untuk $.fs.read. Mod lebih awal dalam rantai dapat mengamati, menulis ulang, atau menolak panggilan Anda, yang merupakan cara organisasi membatasi apa yang dijangkau mod.

Sebuah mod dapat menolak panggilan $.process.spawn Anda setelah perintah menghasilkan output atau keluar, dan tidak ada yang telah dilakukan perintah tersebut yang dibatalkan. Panggilan tersebut kemudian ditolak dengan pesan yang diakhiri dengan salah satu string berikut dan alasan dari mod yang menolak:

  • $.process.spawn started, and a plugin withheld its result:: mod yang menolak belum membaca output perintah sampai akhir. Claude Code menghentikan perintah jika masih berjalan.
  • $.process.spawn ran, and a plugin withheld its result:: mod yang menolak telah membaca output perintah sampai akhir, sehingga perintah tersebut telah keluar

Baca semua pengaturan, satu sumber, atau satu kunci

$.settings.read() menyelesaikan ke satu objek yang memuat pengaturan dari semua sumber yang digunakan, digabungkan berdasarkan prioritas. Teruskan { source } untuk membaca satu sumber saja, { key } untuk membaca satu kunci, atau keduanya:

Panggilan Menyelesaikan ke
$.settings.read() Pengaturan gabungan sebagai satu objek. Kunci tingkat atasnya adalah kunci dari file settings.json, seperti permissions dan hooks.
$.settings.read({ source: 'policy' }) Pengaturan dari satu sumber tersebut sebagai satu objek, atau {} ketika sumber tersebut tidak memuat pengaturan apa pun
$.settings.read({ key: 'spinnerTipsEnabled' }) Nilai kunci tersebut dalam pengaturan gabungan, atau undefined ketika kunci tidak diatur
$.settings.read({ key: 'spinnerTipsEnabled', source: 'user' }) Nilai kunci tersebut dalam satu sumber tersebut, atau undefined ketika sumber tersebut tidak mengaturnya

Untuk source, teruskan nama satu sumber pengaturan yang akan dibaca:

  • user: ~/.claude/settings.json
  • project: .claude/settings.json
  • local: .claude/settings.local.json
  • flag: apa yang Anda teruskan dengan --settings, dan pengaturan inline SDK
  • policy: pengaturan terkelola

Untuk key, teruskan nama persis dari satu kunci tingkat atas, bukan jalur bertitik ke dalam objek bersarang. Argumen key memerlukan Claude Code v2.1.304 atau yang lebih baru.

Langkah berikutnya